aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/pic32_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/pic32_uart.c')
-rw-r--r--drivers/tty/serial/pic32_uart.c219
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;