diff options
Diffstat (limited to 'drivers/tty/serial/sccnxp.c')
| -rw-r--r-- | drivers/tty/serial/sccnxp.c | 512 |
1 files changed, 276 insertions, 236 deletions
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 418b495e323..e84b6a3bdd1 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -15,6 +15,8 @@ #define SUPPORT_SYSRQ #endif +#include <linux/clk.h> +#include <linux/err.h> #include <linux/module.h> #include <linux/device.h> #include <linux/console.h> @@ -23,8 +25,10 @@ #include <linux/io.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/platform_data/sccnxp.h> +#include <linux/platform_data/serial-sccnxp.h> +#include <linux/regulator/consumer.h> #define SCCNXP_NAME "uart-sccnxp" #define SCCNXP_MAJOR 204 @@ -91,65 +95,131 @@ #define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0) #define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0) -/* Supported chip types */ -enum { - SCCNXP_TYPE_SC2681 = 2681, - SCCNXP_TYPE_SC2691 = 2691, - SCCNXP_TYPE_SC2692 = 2692, - SCCNXP_TYPE_SC2891 = 2891, - SCCNXP_TYPE_SC2892 = 2892, - SCCNXP_TYPE_SC28202 = 28202, - SCCNXP_TYPE_SC68681 = 68681, - SCCNXP_TYPE_SC68692 = 68692, +#define SCCNXP_HAVE_IO 0x00000001 +#define SCCNXP_HAVE_MR0 0x00000002 + +struct sccnxp_chip { + const char *name; + unsigned int nr; + unsigned long freq_min; + unsigned long freq_std; + unsigned long freq_max; + unsigned int flags; + unsigned int fifosize; }; struct sccnxp_port { struct uart_driver uart; struct uart_port port[SCCNXP_MAX_UARTS]; + bool opened[SCCNXP_MAX_UARTS]; - const char *name; int irq; - u8 imr; - u8 addr_mask; - int freq_std; - int flags; -#define SCCNXP_HAVE_IO 0x00000001 -#define SCCNXP_HAVE_MR0 0x00000002 + struct sccnxp_chip *chip; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE struct console console; #endif - struct mutex sccnxp_mutex; + spinlock_t lock; + + bool poll; + struct timer_list timer; struct sccnxp_pdata pdata; + + struct regulator *regulator; }; -static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) -{ - return readb(base + (reg << shift)); -} +static const struct sccnxp_chip sc2681 = { + .name = "SC2681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; -static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v) -{ - writeb(v, base + (reg << shift)); -} +static const struct sccnxp_chip sc2691 = { + .name = "SC2691", + .nr = 1, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = 0, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2692 = { + .name = "SC2692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2891 = { + .name = "SC2891", + .nr = 1, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc2892 = { + .name = "SC2892", + .nr = 2, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc28202 = { + .name = "SC28202", + .nr = 2, + .freq_min = 1000000, + .freq_std = 14745600, + .freq_max = 50000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 256, +}; + +static const struct sccnxp_chip sc68681 = { + .name = "SC68681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc68692 = { + .name = "SC68692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; static inline u8 sccnxp_read(struct uart_port *port, u8 reg) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - return sccnxp_raw_read(port->membase, reg & s->addr_mask, - port->regshift); + return readb(port->membase + (reg << port->regshift)); } static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v); + writeb(v, port->membase + (reg << port->regshift)); } static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) @@ -174,14 +244,12 @@ static int sccnxp_update_best_err(int a, int b, int *besterr) return 1; } -struct baud_table { +static const struct { u8 csr; u8 acr; u8 mr0; int baud; -}; - -const struct baud_table baud_std[] = { +} baud_std[] = { { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, @@ -217,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) { struct sccnxp_port *s = dev_get_drvdata(port->dev); int div_std, tmp_baud, bestbaud = baud, besterr = -1; + struct sccnxp_chip *chip = s->chip; u8 i, acr = 0, csr = 0, mr0 = 0; /* Find best baud from table */ for (i = 0; baud_std[i].baud && besterr; i++) { - if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0)) + if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0)) continue; - div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud); + div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud); tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std); if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) { acr = baud_std[i].acr; @@ -233,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) } } - if (s->flags & SCCNXP_HAVE_MR0) { + if (chip->flags & SCCNXP_HAVE_MR0) { /* Enable FIFO, set half level for TX */ mr0 |= MR0_FIFO | MR0_TXLVL; /* Update MR0 */ @@ -285,10 +354,6 @@ static void sccnxp_handle_rx(struct uart_port *port) { u8 sr; unsigned int ch, flag; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - - if (!tty) - return; for (;;) { sr = sccnxp_port_read(port, SCCNXP_SR_REG); @@ -304,14 +369,19 @@ static void sccnxp_handle_rx(struct uart_port *port) if (unlikely(sr)) { if (sr & SR_BRK) { port->icount.brk++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_BREAK_RESET); if (uart_handle_break(port)) continue; } else if (sr & SR_PE) port->icount.parity++; else if (sr & SR_FE) port->icount.frame++; - else if (sr & SR_OVR) + else if (sr & SR_OVR) { port->icount.overrun++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_STATUS_RESET); + } sr &= port->read_status_mask; if (sr & SR_BRK) @@ -333,9 +403,7 @@ static void sccnxp_handle_rx(struct uart_port *port) uart_insert_char(port, sr, SR_OVR, ch, flag); } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); } static void sccnxp_handle_tx(struct uart_port *port) @@ -357,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port) sccnxp_disable_irq(port, IMR_TXRDY); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); } return; @@ -377,31 +445,46 @@ static void sccnxp_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static irqreturn_t sccnxp_ist(int irq, void *dev_id) +static void sccnxp_handle_events(struct sccnxp_port *s) { int i; u8 isr; - struct sccnxp_port *s = (struct sccnxp_port *)dev_id; - - mutex_lock(&s->sccnxp_mutex); - for (;;) { + do { isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); isr &= s->imr; if (!isr) break; - dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); - for (i = 0; i < s->uart.nr; i++) { - if (isr & ISR_RXRDY(i)) + if (s->opened[i] && (isr & ISR_RXRDY(i))) sccnxp_handle_rx(&s->port[i]); - if (isr & ISR_TXRDY(i)) + if (s->opened[i] && (isr & ISR_TXRDY(i))) sccnxp_handle_tx(&s->port[i]); } - } + } while (1); +} + +static void sccnxp_timer(unsigned long data) +{ + struct sccnxp_port *s = (struct sccnxp_port *)data; + unsigned long flags; - mutex_unlock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); + + mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us)); +} + +static irqreturn_t sccnxp_ist(int irq, void *dev_id) +{ + struct sccnxp_port *s = (struct sccnxp_port *)dev_id; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); return IRQ_HANDLED; } @@ -409,16 +492,17 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id) static void sccnxp_start_tx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Set direction to output */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 1); sccnxp_enable_irq(port, IMR_TXRDY); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_stop_tx(struct uart_port *port) @@ -429,20 +513,22 @@ static void sccnxp_stop_tx(struct uart_port *port) static void sccnxp_stop_rx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_tx_empty(struct uart_port *port) { u8 val; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); val = sccnxp_port_read(port, SCCNXP_SR_REG); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; } @@ -455,28 +541,30 @@ static void sccnxp_enable_ms(struct uart_port *port) static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_get_mctrl(struct uart_port *port) { u8 bitmask, ipr; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return mctrl; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); @@ -505,7 +593,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; } - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return mctrl; } @@ -513,21 +601,23 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) static void sccnxp_break_ctl(struct uart_port *port, int break_state) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; u8 mr1, mr2; int baud; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -575,41 +665,45 @@ static void sccnxp_set_termios(struct uart_port *port, port->read_status_mask = SR_OVR; if (termios->c_iflag & INPCK) port->read_status_mask |= SR_PE | SR_FE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= SR_BRK; /* Set status ignore mask */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNBRK) port->ignore_status_mask |= SR_BRK; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; if (!(termios->c_cflag & CREAD)) port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK; /* Setup baudrate */ baud = uart_get_baud_rate(port, termios, old, 50, - (s->flags & SCCNXP_HAVE_MR0) ? + (s->chip->flags & SCCNXP_HAVE_MR0) ? 230400 : 38400); baud = sccnxp_set_baud(port, baud); /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); + /* Report actual baudrate back to core */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); /* Enable RX & TX */ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_startup(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); - if (s->flags & SCCNXP_HAVE_IO) { + if (s->chip->flags & SCCNXP_HAVE_IO) { /* Outputs are controlled manually */ sccnxp_write(port, SCCNXP_OPCR_REG, 0); } @@ -626,7 +720,9 @@ static int sccnxp_startup(struct uart_port *port) /* Enable RX interrupt */ sccnxp_enable_irq(port, IMR_RXRDY); - mutex_unlock(&s->sccnxp_mutex); + s->opened[port->line] = 1; + + spin_unlock_irqrestore(&s->lock, flags); return 0; } @@ -634,8 +730,11 @@ static int sccnxp_startup(struct uart_port *port) static void sccnxp_shutdown(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); - mutex_lock(&s->sccnxp_mutex); + s->opened[port->line] = 0; /* Disable interrupts */ sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); @@ -644,17 +743,17 @@ static void sccnxp_shutdown(struct uart_port *port) sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE); /* Leave direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static const char *sccnxp_type(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_SC26XX) ? s->name : NULL; + return (port->type == PORT_SC26XX) ? s->chip->name : NULL; } static void sccnxp_release_port(struct uart_port *port) @@ -721,10 +820,11 @@ static void sccnxp_console_write(struct console *co, const char *c, unsigned n) { struct sccnxp_port *s = (struct sccnxp_port *)co->data; struct uart_port *port = &s->port[co->index]; + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); uart_console_write(port, c, n, sccnxp_console_putchar); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_console_setup(struct console *co, char *options) @@ -740,21 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options) } #endif +static const struct platform_device_id sccnxp_id_table[] = { + { .name = "sc2681", .driver_data = (kernel_ulong_t)&sc2681, }, + { .name = "sc2691", .driver_data = (kernel_ulong_t)&sc2691, }, + { .name = "sc2692", .driver_data = (kernel_ulong_t)&sc2692, }, + { .name = "sc2891", .driver_data = (kernel_ulong_t)&sc2891, }, + { .name = "sc2892", .driver_data = (kernel_ulong_t)&sc2892, }, + { .name = "sc28202", .driver_data = (kernel_ulong_t)&sc28202, }, + { .name = "sc68681", .driver_data = (kernel_ulong_t)&sc68681, }, + { .name = "sc68692", .driver_data = (kernel_ulong_t)&sc68692, }, + { } +}; +MODULE_DEVICE_TABLE(platform, sccnxp_id_table); + static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int chiptype = pdev->id_entry->driver_data; struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); - int i, ret, fifosize, freq_min, freq_max; + int i, ret, uartclk; struct sccnxp_port *s; void __iomem *membase; + struct clk *clk; - if (!res) { - dev_err(&pdev->dev, "Missing memory resource data\n"); - return -EADDRNOTAVAIL; - } - - dev_set_name(&pdev->dev, SCCNXP_NAME); + membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(membase)) + return PTR_ERR(membase); s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); if (!s) { @@ -763,129 +873,62 @@ static int sccnxp_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, s); - mutex_init(&s->sccnxp_mutex); - - /* Individual chip settings */ - switch (chiptype) { - case SCCNXP_TYPE_SC2681: - s->name = "SC2681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2691: - s->name = "SC2691"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x07; - s->flags = 0; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2692: - s->name = "SC2692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2891: - s->name = "SC2891"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC2892: - s->name = "SC2892"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC28202: - s->name = "SC28202"; - s->uart.nr = 2; - s->freq_std = 14745600; - s->addr_mask = 0x7f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 256; - freq_min = 1000000; - freq_max = 50000000; - break; - case SCCNXP_TYPE_SC68681: - s->name = "SC68681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC68692: - s->name = "SC68692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - default: - dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype); - ret = -ENOTSUPP; - goto err_out; - } + spin_lock_init(&s->lock); - if (!pdata) { - dev_warn(&pdev->dev, - "No platform data supplied, using defaults\n"); - s->pdata.frequency = s->freq_std; - } else - memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; - s->irq = platform_get_irq(pdev, 0); - if (s->irq <= 0) { - dev_err(&pdev->dev, "Missing irq resource data\n"); - ret = -ENXIO; - goto err_out; - } + s->regulator = devm_regulator_get(&pdev->dev, "vcc"); + if (!IS_ERR(s->regulator)) { + ret = regulator_enable(s->regulator); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable regulator: %i\n", ret); + return ret; + } + } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_out; + } + dev_notice(&pdev->dev, "Using default clock frequency\n"); + uartclk = s->chip->freq_std; + } else + uartclk = clk_get_rate(clk); /* Check input frequency */ - if ((s->pdata.frequency < freq_min) || - (s->pdata.frequency > freq_max)) { + if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) { dev_err(&pdev->dev, "Frequency out of bounds\n"); ret = -EINVAL; goto err_out; } - membase = devm_request_and_ioremap(&pdev->dev, res); - if (!membase) { - dev_err(&pdev->dev, "Failed to ioremap\n"); - ret = -EIO; - goto err_out; + if (pdata) + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + + if (s->pdata.poll_time_us) { + dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", + s->pdata.poll_time_us); + s->poll = 1; + } + + if (!s->poll) { + s->irq = platform_get_irq(pdev, 0); + if (s->irq < 0) { + dev_err(&pdev->dev, "Missing irq resource data\n"); + ret = -ENXIO; + goto err_out; + } } s->uart.owner = THIS_MODULE; s->uart.dev_name = "ttySC"; s->uart.major = SCCNXP_MAJOR; s->uart.minor = SCCNXP_MINOR; + s->uart.nr = s->chip->nr; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE s->uart.cons = &s->console; s->uart.cons->device = uart_console_device; @@ -907,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev) s->port[i].dev = &pdev->dev; s->port[i].irq = s->irq; s->port[i].type = PORT_SC26XX; - s->port[i].fifosize = fifosize; + s->port[i].fifosize = s->chip->fifosize; s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; s->port[i].iotype = UPIO_MEM; s->port[i].mapbase = res->start; s->port[i].membase = membase; s->port[i].regshift = s->pdata.reg_shift; - s->port[i].uartclk = s->pdata.frequency; + s->port[i].uartclk = uartclk; s->port[i].ops = &sccnxp_ops; uart_add_one_port(&s->uart, &s->port[i]); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(&s->port[i], DIR_OP, 0); } @@ -925,20 +968,28 @@ static int sccnxp_probe(struct platform_device *pdev) s->imr = 0; sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); - /* Board specific configure */ - if (s->pdata.init) - s->pdata.init(); - - ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&pdev->dev), s); - if (!ret) + if (!s->poll) { + ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, + sccnxp_ist, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&pdev->dev), s); + if (!ret) + return 0; + + dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + } else { + init_timer(&s->timer); + setup_timer(&s->timer, sccnxp_timer, (unsigned long)s); + mod_timer(&s->timer, jiffies + + usecs_to_jiffies(s->pdata.poll_time_us)); return 0; + } - dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); - + uart_unregister_driver(&s->uart); err_out: - platform_set_drvdata(pdev, NULL); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return ret; } @@ -948,33 +999,22 @@ static int sccnxp_remove(struct platform_device *pdev) int i; struct sccnxp_port *s = platform_get_drvdata(pdev); - devm_free_irq(&pdev->dev, s->irq, s); + if (!s->poll) + devm_free_irq(&pdev->dev, s->irq, s); + else + del_timer_sync(&s->timer); for (i = 0; i < s->uart.nr; i++) uart_remove_one_port(&s->uart, &s->port[i]); uart_unregister_driver(&s->uart); - platform_set_drvdata(pdev, NULL); - if (s->pdata.exit) - s->pdata.exit(); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return 0; } -static const struct platform_device_id sccnxp_id_table[] = { - { "sc2681", SCCNXP_TYPE_SC2681 }, - { "sc2691", SCCNXP_TYPE_SC2691 }, - { "sc2692", SCCNXP_TYPE_SC2692 }, - { "sc2891", SCCNXP_TYPE_SC2891 }, - { "sc2892", SCCNXP_TYPE_SC2892 }, - { "sc28202", SCCNXP_TYPE_SC28202 }, - { "sc68681", SCCNXP_TYPE_SC68681 }, - { "sc68692", SCCNXP_TYPE_SC68692 }, - { }, -}; -MODULE_DEVICE_TABLE(platform, sccnxp_id_table); - static struct platform_driver sccnxp_uart_driver = { .driver = { .name = SCCNXP_NAME, |
