On 04/28/2015 09:38 PM, Jun Nie wrote: > Support ZTE uart with some registers differing offset. > Probe as platform device for not AMBA IP ID is > available on ZTE uart. Reviewed-by: Peter Hurley <peter@xxxxxxxxxxxxxxxxxx> > Signed-off-by: Jun Nie <jun.nie@xxxxxxxxxx> > --- > drivers/tty/serial/Kconfig | 4 +- > drivers/tty/serial/amba-pl011.c | 223 +++++++++++++++++++++++++++++++++++++--- > include/linux/amba/serial.h | 14 +++ > 3 files changed, 226 insertions(+), 15 deletions(-) > > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > index f8120c1..13ccb44 100644 > --- a/drivers/tty/serial/Kconfig > +++ b/drivers/tty/serial/Kconfig > @@ -47,12 +47,12 @@ config SERIAL_AMBA_PL010_CONSOLE > > config SERIAL_AMBA_PL011 > tristate "ARM AMBA PL011 serial port support" > - depends on ARM_AMBA > + depends on ARM_AMBA || SOC_ZX296702 > select SERIAL_CORE > help > This selects the ARM(R) AMBA(R) PrimeCell PL011 UART. If you have > an Integrator/PP2, Integrator/CP or Versatile platform, say Y or M > - here. > + here. Say yes if you have SOC_ZX296702. > > If unsure, say N. > > diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c > index 14941be..6db5ee4 100644 > --- a/drivers/tty/serial/amba-pl011.c > +++ b/drivers/tty/serial/amba-pl011.c > @@ -75,6 +75,10 @@ > /* There is by now at least one vendor with differing details, so handle it */ > struct vendor_data { > unsigned int ifls; > + unsigned int fr_busy; > + unsigned int fr_dsr; > + unsigned int fr_cts; > + unsigned int u1_fr_ri; > u8 *reg_lut; > bool oversampling; > bool dma_threshold; > @@ -83,13 +87,14 @@ struct vendor_data { > unsigned int (*get_fifosize)(struct amba_device *dev); > }; > > +#ifdef CONFIG_ARM_AMBA > static unsigned int get_fifosize_arm(struct amba_device *dev) > { > return amba_rev(dev) < 3 ? 16 : 32; > } > > -static u8 arm_reg[] = { > - /* All registers offset are in order except [8]=0x2c */ > +static u8 arm_reg[REG_NR] = { > + /* All registers offset are in order except LCRH as comment */ > [IDX(UART01x_DR)] = UART01x_DR, > [IDX(UART01x_RSR)] = UART01x_RSR, > [IDX(ST_UART011_DMAWM)] = ST_UART011_DMAWM, > @@ -113,6 +118,10 @@ static u8 arm_reg[] = { > > static struct vendor_data vendor_arm = { > .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, > + .fr_busy = UART01x_FR_BUSY, > + .fr_dsr = UART01x_FR_DSR, > + .fr_cts = UART01x_FR_CTS, > + .u1_fr_ri = UART011_FR_RI, > .reg_lut = arm_reg, > .oversampling = false, > .dma_threshold = false, > @@ -125,7 +134,7 @@ static unsigned int get_fifosize_st(struct amba_device *dev) > return 64; > } > > -static u8 st_reg[] = { > +static u8 st_reg[REG_NR] = { > /* All registers offset are in order */ > [IDX(UART01x_DR)] = UART01x_DR, > [IDX(UART01x_RSR)] = UART01x_RSR, > @@ -150,12 +159,60 @@ static u8 st_reg[] = { > > static struct vendor_data vendor_st = { > .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, > + .fr_busy = UART01x_FR_BUSY, > + .fr_dsr = UART01x_FR_DSR, > + .fr_cts = UART01x_FR_CTS, > + .u1_fr_ri = UART011_FR_RI, > .reg_lut = st_reg, > .oversampling = true, > .dma_threshold = true, > .cts_event_workaround = true, > .get_fifosize = get_fifosize_st, > }; > +#endif > + > +#ifdef CONFIG_SOC_ZX296702 > +static unsigned int get_fifosize_zx(struct amba_device *dev) > +{ > + return 16; > +} > + > +static u8 zx_reg[REG_NR] = { > + /* Registers offset are remapped from origin offset as in comment */ > + [IDX(UART01x_DR)] = ZX_UART01x_DR, /* remapped */ > + [IDX(UART01x_RSR)] = UART01x_RSR, > + [IDX(ST_UART011_DMAWM)] = ST_UART011_DMAWM, > + [IDX(UART010_LCRM)] = UART010_LCRM, > + [IDX(UART010_LCRL)] = UART010_LCRL, > + [IDX(UART010_CR)] = UART010_CR, > + [IDX(UART01x_FR)] = ZX_UART01x_FR, /* remapped */ > + [IDX(UART011_LCRH_RX)] = UART011_LCRH_RX, > + [IDX(UART01x_ILPR)] = UART01x_ILPR, > + [IDX(UART011_IBRD)] = UART011_IBRD, > + [IDX(UART011_FBRD)] = UART011_FBRD, > + [IDX(UART011_LCRH_TX)] = ZX_UART011_LCRH_TX, /* remapped */ > + [IDX(UART011_CR)] = ZX_UART011_CR, /* remapped */ > + [IDX(UART011_IFLS)] = ZX_UART011_IFLS, /* remapped */ > + [IDX(UART011_IMSC)] = ZX_UART011_IMSC, /* remapped */ > + [IDX(UART011_RIS)] = UART011_RIS, > + [IDX(UART011_MIS)] = ZX_UART011_MIS, /* remapped */ > + [IDX(UART011_ICR)] = ZX_UART011_ICR, /* remapped */ > + [IDX(UART011_DMACR)] = ZX_UART011_DMACR, /* remapped */ > +}; > + > +static struct vendor_data vendor_zx = { > + .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, > + .fr_busy = ZX_UART01x_FR_BUSY, > + .fr_dsr = ZX_UART01x_FR_DSR, > + .fr_cts = ZX_UART01x_FR_CTS, > + .u1_fr_ri = ZX_UART011_FR_RI, > + .reg_lut = zx_reg, > + .oversampling = false, > + .dma_threshold = false, > + .cts_event_workaround = false, > + .get_fifosize = get_fifosize_zx, > +}; > +#endif > > /* Deals with DMA transactions */ > > @@ -199,6 +256,10 @@ struct uart_amba_port { > unsigned int im; /* interrupt mask */ > unsigned int old_status; > unsigned int fifosize; /* vendor-specific */ > + unsigned int fr_busy; /* vendor-specific */ > + unsigned int fr_dsr; /* vendor-specific */ > + unsigned int fr_cts; /* vendor-specific */ > + unsigned int u1_fr_ri; /* vendor-specific */ > unsigned int old_cr; /* state during shutdown */ > struct delayed_work tx_softirq_work; > bool autorts; > @@ -1123,7 +1184,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) > return; > > /* Disable RX and TX DMA */ > - while (pl011_readw(uap, UART01x_FR) & UART01x_FR_BUSY) > + while (pl011_readw(uap, UART01x_FR) & uap->fr_busy) > barrier(); > > spin_lock_irq(&uap->port.lock); > @@ -1406,11 +1467,11 @@ static void pl011_modem_status(struct uart_amba_port *uap) > if (delta & UART01x_FR_DCD) > uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); > > - if (delta & UART01x_FR_DSR) > + if (delta & uap->fr_dsr) > uap->port.icount.dsr++; > > - if (delta & UART01x_FR_CTS) > - uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); > + if (delta & uap->fr_cts) > + uart_handle_cts_change(&uap->port, status & uap->fr_cts); > > wake_up_interruptible(&uap->port.state->port.delta_msr_wait); > } > @@ -1501,7 +1562,7 @@ static unsigned int pl011_tx_empty(struct uart_port *port) > container_of(port, struct uart_amba_port, port); > > unsigned int status = pl011_readw(uap, UART01x_FR); > - return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; > + return status & (uap->fr_busy|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; > } > > static unsigned int pl011_get_mctrl(struct uart_port *port) > @@ -1516,9 +1577,9 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) > result |= tiocmbit > > TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR); > - TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR); > - TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS); > - TIOCMBIT(UART011_FR_RI, TIOCM_RNG); > + TIOCMBIT(uap->fr_dsr, TIOCM_DSR); > + TIOCMBIT(uap->fr_cts, TIOCM_CTS); > + TIOCMBIT(uap->u1_fr_ri, TIOCM_RNG); > #undef TIOCMBIT > return result; > } > @@ -2046,6 +2107,9 @@ static struct uart_amba_port *amba_ports[UART_NR]; > > static void pl011_console_putchar(struct uart_port *port, int ch) > { > + struct uart_amba_port *uap = > + container_of(port, struct uart_amba_port, port); > + > while (pl011_readw(uap, UART01x_FR) & UART01x_FR_TXFF) > barrier(); > pl011_writew(uap, ch, UART01x_DR); > @@ -2085,7 +2149,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) > */ > do { > status = pl011_readw(uap, UART01x_FR); > - } while (status & UART01x_FR_BUSY); > + } while (status & uap->fr_busy); > pl011_writew(uap, old_cr, UART011_CR); > > if (locked) > @@ -2196,7 +2260,7 @@ static void pl011_putc(struct uart_port *port, int c) > while (pl011_readw(uap, UART01x_FR) & UART01x_FR_TXFF) > ; > pl011_writeb(uap, c, UART01x_DR); > - while (pl011_readw(uap, UART01x_FR) & UART01x_FR_BUSY) > + while (pl011_readw(uap, UART01x_FR) & uap->fr_busy) > ; > } > > @@ -2233,6 +2297,7 @@ static struct uart_driver amba_reg = { > .cons = AMBA_CONSOLE, > }; > > +#ifdef CONFIG_ARM_AMBA > static int pl011_probe_dt_alias(int index, struct device *dev) > { > struct device_node *np; > @@ -2297,6 +2362,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) > > uap->reg_lut = vendor->reg_lut; > uap->vendor = vendor; > + uap->fr_busy = vendor->fr_busy; > + uap->fr_dsr = vendor->fr_dsr; > + uap->fr_cts = vendor->fr_cts; > + uap->u1_fr_ri = vendor->u1_fr_ri; > uap->old_cr = 0; > uap->fifosize = vendor->get_fifosize(dev); > uap->port.dev = &dev->dev; > @@ -2357,6 +2426,101 @@ static int pl011_remove(struct amba_device *dev) > uart_unregister_driver(&amba_reg); > return 0; > } > +#endif > + > +#ifdef CONFIG_SOC_ZX296702 > +static int zx_uart_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct uart_amba_port *uap; > + struct vendor_data *vendor = &vendor_zx; > + struct resource *res; > + void __iomem *base; > + int i, ret; > + > + uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), > + GFP_KERNEL); > + if (uap == NULL) { > + ret = -ENOMEM; > + goto out; > + } > + > + i = of_alias_get_id(np, "serial"); > + if (i < 0) { > + dev_err(&pdev->dev, "failed to get alias id: %d\n", i); > + ret = i; > + goto out; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + base = devm_ioremap_resource(&pdev->dev, res); > + if (!base) { > + ret = -ENOMEM; > + goto out; > + } > + > + uap->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(uap->clk)) { > + ret = PTR_ERR(uap->clk); > + goto out; > + } > + > + uap->reg_lut = vendor->reg_lut; > + uap->vendor = vendor; > + uap->fr_busy = vendor->fr_busy; > + uap->fr_dsr = vendor->fr_dsr; > + uap->fr_cts = vendor->fr_cts; > + uap->u1_fr_ri = vendor->u1_fr_ri; > + uap->old_cr = 0; > + uap->fifosize = 16; > + uap->port.dev = &pdev->dev; > + uap->port.mapbase = res->start; > + uap->port.membase = base; > + uap->port.iotype = UPIO_MEM; > + uap->port.irq = platform_get_irq(pdev, 0); > + uap->port.fifosize = uap->fifosize; > + uap->port.ops = &amba_pl011_pops; > + uap->port.flags = UPF_BOOT_AUTOCONF; > + uap->port.line = i; > + INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq); > + > + /* Ensure interrupts from this UART are masked and cleared */ > + pl011_writew(uap, 0, UART011_IMSC); > + pl011_writew(uap, 0xffff, UART011_ICR); > + > + amba_ports[i] = uap; > + > + platform_set_drvdata(pdev, uap); > + ret = uart_register_driver(&amba_reg); > + if (ret < 0) { > + dev_err(&pdev->dev, > + "Failed to register AMBA-PL011 driver\n"); > + return ret; > + } > + ret = uart_add_one_port(&amba_reg, &uap->port); > + if (ret) { > + amba_ports[i] = NULL; > + pl011_dma_remove(uap); > + } > +out: > + return ret; > +} > + > +static int zx_uart_remove(struct platform_device *pdev) > +{ > + struct uart_amba_port *uap = platform_get_drvdata(pdev); > + int i; > + > + uart_remove_one_port(&amba_reg, &uap->port); > + > + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) > + if (amba_ports[i] == uap) > + amba_ports[i] = NULL; > + > + pl011_dma_remove(uap); > + return 0; > +} > +#endif > > #ifdef CONFIG_PM_SLEEP > static int pl011_suspend(struct device *dev) > @@ -2382,6 +2546,7 @@ static int pl011_resume(struct device *dev) > > static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); > > +#ifdef CONFIG_ARM_AMBA > static struct amba_id pl011_ids[] = { > { > .id = 0x00041011, > @@ -2408,16 +2573,48 @@ static struct amba_driver pl011_driver = { > .remove = pl011_remove, > }; > > +#endif > + > +#ifdef CONFIG_SOC_ZX296702 > +static const struct of_device_id zx_uart_dt_ids[] = { > + { .compatible = "zte,zx296702-uart", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, zx_uart_dt_ids); > + > +static struct platform_driver zx_uart_driver = { > + .driver = { > + .name = "zx-uart", > + .owner = THIS_MODULE, > + .pm = &pl011_dev_pm_ops, > + .of_match_table = zx_uart_dt_ids, > + }, > + .probe = zx_uart_probe, > + .remove = zx_uart_remove, > +}; > +#endif > + > static int __init pl011_init(void) > { > printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); > > +#ifdef CONFIG_ARM_AMBA > return amba_driver_register(&pl011_driver); > +#endif > + > +#ifdef CONFIG_SOC_ZX296702 > + return platform_driver_register(&zx_uart_driver); > +#endif > } > > static void __exit pl011_exit(void) > { > +#ifdef CONFIG_ARM_AMBA > amba_driver_unregister(&pl011_driver); > +#endif > +#ifdef CONFIG_SOC_ZX296702 > + platform_driver_unregister(&zx_uart_driver); > +#endif > } > > /* > diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h > index 1fe1549..84f2330 100644 > --- a/include/linux/amba/serial.h > +++ b/include/linux/amba/serial.h > @@ -31,6 +31,7 @@ > * UART Register Offsets. > */ > #define UART01x_DR 0x00 /* Data read or written from the interface. */ > +#define ZX_UART01x_DR 0x04 /* Data read or written from the interface. */ > #define UART01x_RSR 0x04 /* Receive status register (Read). */ > #define UART01x_ECR 0x04 /* Error clear register (Write). */ > #define UART010_LCRH 0x08 /* Line control register, high byte. */ > @@ -39,6 +40,7 @@ > #define ST_UART011_TIMEOUT 0x0C /* Timeout period register. */ > #define UART010_LCRL 0x10 /* Line control register, low byte. */ > #define UART010_CR 0x14 /* Control register. */ > +#define ZX_UART01x_FR 0x14 /* Flag register (Read only). */ > #define UART01x_FR 0x18 /* Flag register (Read only). */ > #define UART010_IIR 0x1C /* Interrupt identification register (Read). */ > #define UART010_ICR 0x1C /* Interrupt clear register (Write). */ > @@ -50,14 +52,22 @@ > #define UART011_LCRH 0x2c /* Line control register. */ > #define UART011_LCRH_TX 0x2c /* Line control register. */ > #define ST_UART011_LCRH_TX 0x2c /* Tx Line control register. */ > +#define ZX_UART011_LCRH_TX 0x30 /* Tx Line control register. */ > #define UART011_CR 0x30 /* Control register. */ > #define UART011_IFLS 0x34 /* Interrupt fifo level select. */ > +#define ZX_UART011_CR 0x34 /* Control register. */ > +#define ZX_UART011_IFLS 0x38 /* Interrupt fifo level select. */ > #define UART011_IMSC 0x38 /* Interrupt mask. */ > #define UART011_RIS 0x3c /* Raw interrupt status. */ > +#define ZX_UART011_IMSC 0x40 /* Interrupt mask. */ > #define UART011_MIS 0x40 /* Masked interrupt status. */ > +#define ZX_UART011_RIS 0x44 /* Raw interrupt status. */ > #define UART011_ICR 0x44 /* Interrupt clear register. */ > #define UART011_DMACR 0x48 /* DMA control register. */ > +#define ZX_UART011_MIS 0x48 /* Masked interrupt status. */ > +#define ZX_UART011_ICR 0x4c /* Interrupt clear register. */ > #define ST_UART011_XFCR 0x50 /* XON/XOFF control register. */ > +#define ZX_UART011_DMACR 0x50 /* DMA control register. */ > #define ST_UART011_XON1 0x54 /* XON1 register. */ > #define ST_UART011_XON2 0x58 /* XON2 register. */ > #define ST_UART011_XOFF1 0x5C /* XON1 register. */ > @@ -79,15 +89,19 @@ > > #define UART011_FR_TXBUSY 0x100 > #define UART011_FR_RXBUSY 0x200 > +#define ZX_UART01x_FR_BUSY (UART011_FR_RXBUSY | UART011_FR_TXBUSY) > #define UART011_FR_RI 0x100 > #define UART011_FR_TXFE 0x080 > #define UART011_FR_RXFF 0x040 > #define UART01x_FR_TXFF 0x020 > #define UART01x_FR_RXFE 0x010 > #define UART01x_FR_BUSY 0x008 > +#define ZX_UART01x_FR_DSR 0x008 > #define UART01x_FR_DCD 0x004 > #define UART01x_FR_DSR 0x002 > +#define ZX_UART01x_FR_CTS 0x002 > #define UART01x_FR_CTS 0x001 > +#define ZX_UART011_FR_RI 0x001 > #define UART01x_FR_TMSK (UART01x_FR_TXFF + UART01x_FR_BUSY) > > #define UART011_CR_CTSEN 0x8000 /* CTS hardware flow control */ > -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html