diff options
Diffstat (limited to 'drivers/tty/serial/pic32_uart.c')
-rw-r--r-- | drivers/tty/serial/pic32_uart.c | 219 |
1 files changed, 132 insertions, 87 deletions
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index 484b7e8d5381..2beada66c824 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -25,13 +25,105 @@ #include <linux/delay.h> #include <asm/mach-pic32/pic32.h> -#include "pic32_uart.h" /* UART name and device definitions */ #define PIC32_DEV_NAME "pic32-uart" #define PIC32_MAX_UARTS 6 #define PIC32_SDEV_NAME "ttyPIC" +#define PIC32_UART_DFLT_BRATE 9600 +#define PIC32_UART_TX_FIFO_DEPTH 8 +#define PIC32_UART_RX_FIFO_DEPTH 8 + +#define PIC32_UART_MODE 0x00 +#define PIC32_UART_STA 0x10 +#define PIC32_UART_TX 0x20 +#define PIC32_UART_RX 0x30 +#define PIC32_UART_BRG 0x40 + +/* struct pic32_sport - pic32 serial port descriptor + * @port: uart port descriptor + * @idx: port index + * @irq_fault: virtual fault interrupt number + * @irq_fault_name: irq fault name + * @irq_rx: virtual rx interrupt number + * @irq_rx_name: irq rx name + * @irq_tx: virtual tx interrupt number + * @irq_tx_name: irq tx name + * @cts_gpiod: clear to send GPIO + * @dev: device descriptor + **/ +struct pic32_sport { + struct uart_port port; + int idx; + + int irq_fault; + const char *irq_fault_name; + int irq_rx; + const char *irq_rx_name; + int irq_tx; + const char *irq_tx_name; + bool enable_tx_irq; + + struct gpio_desc *cts_gpiod; + + struct clk *clk; + + struct device *dev; +}; + +static inline struct pic32_sport *to_pic32_sport(struct uart_port *port) +{ + return container_of(port, struct pic32_sport, port); +} + +static inline void pic32_uart_writel(struct pic32_sport *sport, + u32 reg, u32 val) +{ + __raw_writel(val, sport->port.membase + reg); +} + +static inline u32 pic32_uart_readl(struct pic32_sport *sport, u32 reg) +{ + return __raw_readl(sport->port.membase + reg); +} + +/* pic32 uart mode register bits */ +#define PIC32_UART_MODE_ON BIT(15) +#define PIC32_UART_MODE_FRZ BIT(14) +#define PIC32_UART_MODE_SIDL BIT(13) +#define PIC32_UART_MODE_IREN BIT(12) +#define PIC32_UART_MODE_RTSMD BIT(11) +#define PIC32_UART_MODE_RESV1 BIT(10) +#define PIC32_UART_MODE_UEN1 BIT(9) +#define PIC32_UART_MODE_UEN0 BIT(8) +#define PIC32_UART_MODE_WAKE BIT(7) +#define PIC32_UART_MODE_LPBK BIT(6) +#define PIC32_UART_MODE_ABAUD BIT(5) +#define PIC32_UART_MODE_RXINV BIT(4) +#define PIC32_UART_MODE_BRGH BIT(3) +#define PIC32_UART_MODE_PDSEL1 BIT(2) +#define PIC32_UART_MODE_PDSEL0 BIT(1) +#define PIC32_UART_MODE_STSEL BIT(0) + +/* pic32 uart status register bits */ +#define PIC32_UART_STA_UTXISEL1 BIT(15) +#define PIC32_UART_STA_UTXISEL0 BIT(14) +#define PIC32_UART_STA_UTXINV BIT(13) +#define PIC32_UART_STA_URXEN BIT(12) +#define PIC32_UART_STA_UTXBRK BIT(11) +#define PIC32_UART_STA_UTXEN BIT(10) +#define PIC32_UART_STA_UTXBF BIT(9) +#define PIC32_UART_STA_TRMT BIT(8) +#define PIC32_UART_STA_URXISEL1 BIT(7) +#define PIC32_UART_STA_URXISEL0 BIT(6) +#define PIC32_UART_STA_ADDEN BIT(5) +#define PIC32_UART_STA_RIDLE BIT(4) +#define PIC32_UART_STA_PERR BIT(3) +#define PIC32_UART_STA_FERR BIT(2) +#define PIC32_UART_STA_OERR BIT(1) +#define PIC32_UART_STA_URXDA BIT(0) + /* pic32_sport pointer for console use */ static struct pic32_sport *pic32_sports[PIC32_MAX_UARTS]; @@ -42,23 +134,6 @@ static inline void pic32_wait_deplete_txbuf(struct pic32_sport *sport) udelay(1); } -static inline int pic32_enable_clock(struct pic32_sport *sport) -{ - int ret = clk_prepare_enable(sport->clk); - - if (ret) - return ret; - - sport->ref_clk++; - return 0; -} - -static inline void pic32_disable_clock(struct pic32_sport *sport) -{ - sport->ref_clk--; - clk_disable_unprepare(sport->clk); -} - /* serial core request to check if uart tx buffer is empty */ static unsigned int pic32_uart_tx_empty(struct uart_port *port) { @@ -82,25 +157,16 @@ static void pic32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) PIC32_UART_MODE_LPBK); } -/* get the state of CTS input pin for this port */ -static unsigned int get_cts_state(struct pic32_sport *sport) -{ - /* read and invert UxCTS */ - if (gpio_is_valid(sport->cts_gpio)) - return !gpio_get_value(sport->cts_gpio); - - return 1; -} - /* serial core request to return the state of misc UART input pins */ static unsigned int pic32_uart_get_mctrl(struct uart_port *port) { struct pic32_sport *sport = to_pic32_sport(port); unsigned int mctrl = 0; - if (!sport->hw_flow_ctrl) + /* get the state of CTS input pin for this port */ + if (!sport->cts_gpiod) mctrl |= TIOCM_CTS; - else if (get_cts_state(sport)) + else if (gpiod_get_value(sport->cts_gpiod)) mctrl |= TIOCM_CTS; /* DSR and CD are not supported in PIC32, so return 1 @@ -117,16 +183,16 @@ static unsigned int pic32_uart_get_mctrl(struct uart_port *port) */ static inline void pic32_uart_irqtxen(struct pic32_sport *sport, u8 en) { - if (en && !tx_irq_enabled(sport)) { + if (en && !sport->enable_tx_irq) { enable_irq(sport->irq_tx); - tx_irq_enabled(sport) = 1; - } else if (!en && tx_irq_enabled(sport)) { + sport->enable_tx_irq = true; + } else if (!en && sport->enable_tx_irq) { /* use disable_irq_nosync() and not disable_irq() to avoid self * imposed deadlock by not waiting for irq handler to end, * since this callback is called from interrupt context. */ disable_irq_nosync(sport->irq_tx); - tx_irq_enabled(sport) = 0; + sport->enable_tx_irq = false; } } @@ -395,7 +461,7 @@ static int pic32_uart_startup(struct uart_port *port) local_irq_save(flags); - ret = pic32_enable_clock(sport); + ret = clk_prepare_enable(sport->clk); if (ret) { local_irq_restore(flags); goto out_done; @@ -419,7 +485,7 @@ static int pic32_uart_startup(struct uart_port *port) * For each irq request_irq() is called with interrupt disabled. * And the irq is enabled as soon as we are ready to handle them. */ - tx_irq_enabled(sport) = 0; + sport->enable_tx_irq = false; sport->irq_fault_name = kasprintf(GFP_KERNEL, "%s%d-fault", pic32_uart_type(port), @@ -427,11 +493,11 @@ static int pic32_uart_startup(struct uart_port *port) if (!sport->irq_fault_name) { dev_err(port->dev, "%s: kasprintf err!", __func__); ret = -ENOMEM; - goto out_done; + goto out_disable_clk; } irq_set_status_flags(sport->irq_fault, IRQ_NOAUTOEN); ret = request_irq(sport->irq_fault, pic32_uart_fault_interrupt, - sport->irqflags_fault, sport->irq_fault_name, port); + IRQF_NO_THREAD, sport->irq_fault_name, port); if (ret) { dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", __func__, sport->irq_fault, ret, @@ -449,7 +515,7 @@ static int pic32_uart_startup(struct uart_port *port) } irq_set_status_flags(sport->irq_rx, IRQ_NOAUTOEN); ret = request_irq(sport->irq_rx, pic32_uart_rx_interrupt, - sport->irqflags_rx, sport->irq_rx_name, port); + IRQF_NO_THREAD, sport->irq_rx_name, port); if (ret) { dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", __func__, sport->irq_rx, ret, @@ -467,7 +533,7 @@ static int pic32_uart_startup(struct uart_port *port) } irq_set_status_flags(sport->irq_tx, IRQ_NOAUTOEN); ret = request_irq(sport->irq_tx, pic32_uart_tx_interrupt, - sport->irqflags_tx, sport->irq_tx_name, port); + IRQF_NO_THREAD, sport->irq_tx_name, port); if (ret) { dev_err(port->dev, "%s: request irq(%d) err! ret:%d name:%s\n", __func__, sport->irq_tx, ret, @@ -488,19 +554,23 @@ static int pic32_uart_startup(struct uart_port *port) /* enable all interrupts and eanable uart */ pic32_uart_en_and_unmask(port); + local_irq_restore(flags); + enable_irq(sport->irq_rx); return 0; out_t: - kfree(sport->irq_tx_name); free_irq(sport->irq_tx, port); + kfree(sport->irq_tx_name); out_r: - kfree(sport->irq_rx_name); free_irq(sport->irq_rx, port); + kfree(sport->irq_rx_name); out_f: - kfree(sport->irq_fault_name); free_irq(sport->irq_fault, port); + kfree(sport->irq_fault_name); +out_disable_clk: + clk_disable_unprepare(sport->clk); out_done: return ret; } @@ -515,18 +585,21 @@ static void pic32_uart_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); pic32_uart_dsbl_and_mask(port); spin_unlock_irqrestore(&port->lock, flags); - pic32_disable_clock(sport); + clk_disable_unprepare(sport->clk); /* free all 3 interrupts for this UART */ free_irq(sport->irq_fault, port); + kfree(sport->irq_fault_name); free_irq(sport->irq_tx, port); + kfree(sport->irq_tx_name); free_irq(sport->irq_rx, port); + kfree(sport->irq_rx_name); } /* serial core request to change current uart setting */ static void pic32_uart_set_termios(struct uart_port *port, struct ktermios *new, - struct ktermios *old) + const struct ktermios *old) { struct pic32_sport *sport = to_pic32_sport(port); unsigned int baud; @@ -565,7 +638,7 @@ static void pic32_uart_set_termios(struct uart_port *port, PIC32_UART_MODE_PDSEL0); } /* if hw flow ctrl, then the pins must be specified in device tree */ - if ((new->c_cflag & CRTSCTS) && sport->hw_flow_ctrl) { + if ((new->c_cflag & CRTSCTS) && sport->cts_gpiod) { /* enable hardware flow control */ pic32_uart_writel(sport, PIC32_SET(PIC32_UART_MODE), PIC32_UART_MODE_UEN1); @@ -691,7 +764,7 @@ static const struct uart_ops pic32_uart_ops = { #ifdef CONFIG_SERIAL_PIC32_CONSOLE /* output given char */ -static void pic32_console_putchar(struct uart_port *port, int ch) +static void pic32_console_putchar(struct uart_port *port, unsigned char ch) { struct pic32_sport *sport = to_pic32_sport(port); @@ -712,10 +785,9 @@ static void pic32_console_write(struct console *co, const char *s, unsigned int count) { struct pic32_sport *sport = pic32_sports[co->index]; - struct uart_port *port = pic32_get_port(sport); /* call uart helper to deal with \r\n */ - uart_console_write(port, s, count, pic32_console_putchar); + uart_console_write(&sport->port, s, count, pic32_console_putchar); } /* console core request to setup given console, find matching uart @@ -724,7 +796,6 @@ static void pic32_console_write(struct console *co, const char *s, static int pic32_console_setup(struct console *co, char *options) { struct pic32_sport *sport; - struct uart_port *port = NULL; int baud = 115200; int bits = 8; int parity = 'n'; @@ -737,16 +808,15 @@ static int pic32_console_setup(struct console *co, char *options) sport = pic32_sports[co->index]; if (!sport) return -ENODEV; - port = pic32_get_port(sport); - ret = pic32_enable_clock(sport); + ret = clk_prepare_enable(sport->clk); if (ret) return ret; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(port, co, baud, parity, bits, flow); + return uart_set_options(&sport->port, co, baud, parity, bits, flow); } static struct uart_driver pic32_uart_driver; @@ -768,11 +838,6 @@ static int __init pic32_console_init(void) } console_initcall(pic32_console_init); -static inline bool is_pic32_console_port(struct uart_port *port) -{ - return port->cons && port->cons->index == port->line; -} - /* * Late console initialization. */ @@ -800,7 +865,8 @@ static struct uart_driver pic32_uart_driver = { static int pic32_uart_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct pic32_sport *sport; int uart_idx = 0; struct resource *res_mem; @@ -821,41 +887,21 @@ static int pic32_uart_probe(struct platform_device *pdev) sport->idx = uart_idx; sport->irq_fault = irq_of_parse_and_map(np, 0); - sport->irqflags_fault = IRQF_NO_THREAD; sport->irq_rx = irq_of_parse_and_map(np, 1); - sport->irqflags_rx = IRQF_NO_THREAD; sport->irq_tx = irq_of_parse_and_map(np, 2); - sport->irqflags_tx = IRQF_NO_THREAD; sport->clk = devm_clk_get(&pdev->dev, NULL); - sport->cts_gpio = -EINVAL; sport->dev = &pdev->dev; /* Hardware flow control: gpios * !Note: Basically, CTS is needed for reading the status. */ - sport->hw_flow_ctrl = false; - sport->cts_gpio = of_get_named_gpio(np, "cts-gpios", 0); - if (gpio_is_valid(sport->cts_gpio)) { - sport->hw_flow_ctrl = true; - - ret = devm_gpio_request(sport->dev, - sport->cts_gpio, "CTS"); - if (ret) { - dev_err(&pdev->dev, - "error requesting CTS GPIO\n"); - goto err; - } - - ret = gpio_direction_input(sport->cts_gpio); - if (ret) { - dev_err(&pdev->dev, "error setting CTS GPIO\n"); - goto err; - } - } + sport->cts_gpiod = devm_gpiod_get_optional(dev, "cts", GPIOD_IN); + if (IS_ERR(sport->cts_gpiod)) + return dev_err_probe(dev, PTR_ERR(sport->cts_gpiod), "error requesting CTS GPIO\n"); + gpiod_set_consumer_name(sport->cts_gpiod, "CTS"); pic32_sports[uart_idx] = sport; port = &sport->port; - memset(port, 0, sizeof(*port)); port->iotype = UPIO_MEM; port->mapbase = res_mem->start; port->ops = &pic32_uart_ops; @@ -873,12 +919,11 @@ static int pic32_uart_probe(struct platform_device *pdev) } #ifdef CONFIG_SERIAL_PIC32_CONSOLE - if (is_pic32_console_port(port) && - (pic32_console.flags & CON_ENABLED)) { + if (uart_console_enabled(port)) { /* The peripheral clock has been enabled by console_setup, * so disable it till the port is used. */ - pic32_disable_clock(sport); + clk_disable_unprepare(sport->clk); } #endif @@ -899,7 +944,7 @@ static int pic32_uart_remove(struct platform_device *pdev) struct pic32_sport *sport = to_pic32_sport(port); uart_remove_one_port(&pic32_uart_driver, port); - pic32_disable_clock(sport); + clk_disable_unprepare(sport->clk); platform_set_drvdata(pdev, NULL); pic32_sports[sport->idx] = NULL; |