diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
commit | b4b7ff4b08e691656c9d77c758fc355833128ac0 (patch) | |
tree | 82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/tty/serial/8250/8250_ingenic.c | |
parent | 35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff) |
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/tty/serial/8250/8250_ingenic.c')
-rw-r--r-- | drivers/tty/serial/8250/8250_ingenic.c | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 7c1e4be48..49394b4c5 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -21,19 +21,33 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_fdt.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> +#include "8250.h" + +/** ingenic_uart_config: SOC specific config data. */ +struct ingenic_uart_config { + int tx_loadsz; + int fifosize; +}; + struct ingenic_uart_data { struct clk *clk_module; struct clk *clk_baud; int line; }; +static const struct of_device_id of_match[]; + #define UART_FCR_UME BIT(4) +#define UART_MCR_MDCE BIT(7) +#define UART_MCR_FCM BIT(6) + static struct earlycon_device *early_device; static uint8_t __init early_in(struct uart_port *port, int offset) @@ -129,6 +143,8 @@ OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) { + int ier; + switch (offset) { case UART_FCR: /* UART module enable */ @@ -136,9 +152,22 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) break; case UART_IER: + /* Enable receive timeout interrupt with the + * receive line status interrupt */ value |= (value & 0x4) << 2; break; + case UART_MCR: + /* If we have enabled modem status IRQs we should enable modem + * mode. */ + ier = p->serial_in(p, UART_IER); + + if (ier & UART_IER_MSI) + value |= UART_MCR_MDCE | UART_MCR_FCM; + else + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + default: break; } @@ -146,14 +175,45 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) writeb(value, p->membase + (offset << p->regshift)); } +static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset) +{ + unsigned int value; + + value = readb(p->membase + (offset << p->regshift)); + + /* Hide non-16550 compliant bits from higher levels */ + switch (offset) { + case UART_FCR: + value &= ~UART_FCR_UME; + break; + + case UART_MCR: + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + + default: + break; + } + return value; +} + static int ingenic_uart_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct ingenic_uart_data *data; + const struct ingenic_uart_config *cdata; + const struct of_device_id *match; int err, line; + match = of_match_device(of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + if (!regs || !irq) { dev_err(&pdev->dev, "no registers/irq defined\n"); return -EINVAL; @@ -164,14 +224,18 @@ static int ingenic_uart_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&uart.port.lock); - uart.port.type = PORT_16550; + uart.port.type = PORT_16550A; uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; uart.port.iotype = UPIO_MEM; uart.port.mapbase = regs->start; uart.port.regshift = 2; uart.port.serial_out = ingenic_uart_serial_out; + uart.port.serial_in = ingenic_uart_serial_in; uart.port.irq = irq->start; uart.port.dev = &pdev->dev; + uart.port.fifosize = cdata->fifosize; + uart.tx_loadsz = cdata->tx_loadsz; + uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; /* Check for a fixed line number */ line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -241,10 +305,26 @@ static int ingenic_uart_remove(struct platform_device *pdev) return 0; } +static const struct ingenic_uart_config jz4740_uart_config = { + .tx_loadsz = 8, + .fifosize = 16, +}; + +static const struct ingenic_uart_config jz4760_uart_config = { + .tx_loadsz = 16, + .fifosize = 32, +}; + +static const struct ingenic_uart_config jz4780_uart_config = { + .tx_loadsz = 32, + .fifosize = 64, +}; + static const struct of_device_id of_match[] = { - { .compatible = "ingenic,jz4740-uart" }, - { .compatible = "ingenic,jz4775-uart" }, - { .compatible = "ingenic,jz4780-uart" }, + { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config }, + { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_match); |