1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 222886ee9SMagnus Damm /* 322886ee9SMagnus Damm * Renesas Emma Mobile 8250 driver 422886ee9SMagnus Damm * 522886ee9SMagnus Damm * Copyright (C) 2012 Magnus Damm 622886ee9SMagnus Damm */ 722886ee9SMagnus Damm 822886ee9SMagnus Damm #include <linux/device.h> 922886ee9SMagnus Damm #include <linux/io.h> 1022886ee9SMagnus Damm #include <linux/module.h> 11ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 1222886ee9SMagnus Damm #include <linux/serial_8250.h> 1322886ee9SMagnus Damm #include <linux/serial_reg.h> 1422886ee9SMagnus Damm #include <linux/platform_device.h> 1522886ee9SMagnus Damm #include <linux/clk.h> 1622886ee9SMagnus Damm #include <linux/slab.h> 1722886ee9SMagnus Damm 1822886ee9SMagnus Damm #include "8250.h" 1922886ee9SMagnus Damm 2022886ee9SMagnus Damm #define UART_DLL_EM 9 2122886ee9SMagnus Damm #define UART_DLM_EM 10 2222886ee9SMagnus Damm 2322886ee9SMagnus Damm struct serial8250_em_priv { 2422886ee9SMagnus Damm struct clk *sclk; 2522886ee9SMagnus Damm int line; 2622886ee9SMagnus Damm }; 2722886ee9SMagnus Damm 2822886ee9SMagnus Damm static void serial8250_em_serial_out(struct uart_port *p, int offset, int value) 2922886ee9SMagnus Damm { 3022886ee9SMagnus Damm switch (offset) { 3122886ee9SMagnus Damm case UART_TX: /* TX @ 0x00 */ 3222886ee9SMagnus Damm writeb(value, p->membase); 3322886ee9SMagnus Damm break; 3422886ee9SMagnus Damm case UART_FCR: /* FCR @ 0x0c (+1) */ 3522886ee9SMagnus Damm case UART_LCR: /* LCR @ 0x10 (+1) */ 3622886ee9SMagnus Damm case UART_MCR: /* MCR @ 0x14 (+1) */ 3722886ee9SMagnus Damm case UART_SCR: /* SCR @ 0x20 (+1) */ 3822886ee9SMagnus Damm writel(value, p->membase + ((offset + 1) << 2)); 3922886ee9SMagnus Damm break; 4022886ee9SMagnus Damm case UART_IER: /* IER @ 0x04 */ 4122886ee9SMagnus Damm value &= 0x0f; /* only 4 valid bits - not Xscale */ 42*df561f66SGustavo A. R. Silva fallthrough; 4322886ee9SMagnus Damm case UART_DLL_EM: /* DLL @ 0x24 (+9) */ 4422886ee9SMagnus Damm case UART_DLM_EM: /* DLM @ 0x28 (+9) */ 4522886ee9SMagnus Damm writel(value, p->membase + (offset << 2)); 4622886ee9SMagnus Damm } 4722886ee9SMagnus Damm } 4822886ee9SMagnus Damm 4922886ee9SMagnus Damm static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset) 5022886ee9SMagnus Damm { 5122886ee9SMagnus Damm switch (offset) { 5222886ee9SMagnus Damm case UART_RX: /* RX @ 0x00 */ 5322886ee9SMagnus Damm return readb(p->membase); 5422886ee9SMagnus Damm case UART_MCR: /* MCR @ 0x14 (+1) */ 5522886ee9SMagnus Damm case UART_LSR: /* LSR @ 0x18 (+1) */ 5622886ee9SMagnus Damm case UART_MSR: /* MSR @ 0x1c (+1) */ 5722886ee9SMagnus Damm case UART_SCR: /* SCR @ 0x20 (+1) */ 5822886ee9SMagnus Damm return readl(p->membase + ((offset + 1) << 2)); 5922886ee9SMagnus Damm case UART_IER: /* IER @ 0x04 */ 6022886ee9SMagnus Damm case UART_IIR: /* IIR @ 0x08 */ 6122886ee9SMagnus Damm case UART_DLL_EM: /* DLL @ 0x24 (+9) */ 6222886ee9SMagnus Damm case UART_DLM_EM: /* DLM @ 0x28 (+9) */ 6322886ee9SMagnus Damm return readl(p->membase + (offset << 2)); 6422886ee9SMagnus Damm } 6522886ee9SMagnus Damm return 0; 6622886ee9SMagnus Damm } 6722886ee9SMagnus Damm 6822886ee9SMagnus Damm static int serial8250_em_serial_dl_read(struct uart_8250_port *up) 6922886ee9SMagnus Damm { 7022886ee9SMagnus Damm return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8; 7122886ee9SMagnus Damm } 7222886ee9SMagnus Damm 7322886ee9SMagnus Damm static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value) 7422886ee9SMagnus Damm { 7522886ee9SMagnus Damm serial_out(up, UART_DLL_EM, value & 0xff); 7622886ee9SMagnus Damm serial_out(up, UART_DLM_EM, value >> 8 & 0xff); 7722886ee9SMagnus Damm } 7822886ee9SMagnus Damm 799671f099SBill Pemberton static int serial8250_em_probe(struct platform_device *pdev) 8022886ee9SMagnus Damm { 8122886ee9SMagnus Damm struct serial8250_em_priv *priv; 8222886ee9SMagnus Damm struct uart_8250_port up; 832a1dbd25SAndy Shevchenko struct resource *regs; 842a1dbd25SAndy Shevchenko int irq, ret; 8522886ee9SMagnus Damm 862a1dbd25SAndy Shevchenko irq = platform_get_irq(pdev, 0); 872a1dbd25SAndy Shevchenko if (irq < 0) 882a1dbd25SAndy Shevchenko return irq; 892a1dbd25SAndy Shevchenko 902a1dbd25SAndy Shevchenko regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 912a1dbd25SAndy Shevchenko if (!regs) { 922a1dbd25SAndy Shevchenko dev_err(&pdev->dev, "missing registers\n"); 93299a6257SLaurent Pinchart return -EINVAL; 9422886ee9SMagnus Damm } 9522886ee9SMagnus Damm 96299a6257SLaurent Pinchart priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 9786b20dfeSPeter Hurley if (!priv) 98299a6257SLaurent Pinchart return -ENOMEM; 9922886ee9SMagnus Damm 100299a6257SLaurent Pinchart priv->sclk = devm_clk_get(&pdev->dev, "sclk"); 10194e792abSMagnus Damm if (IS_ERR(priv->sclk)) { 10222886ee9SMagnus Damm dev_err(&pdev->dev, "unable to get clock\n"); 103299a6257SLaurent Pinchart return PTR_ERR(priv->sclk); 10422886ee9SMagnus Damm } 10522886ee9SMagnus Damm 10622886ee9SMagnus Damm memset(&up, 0, sizeof(up)); 10722886ee9SMagnus Damm up.port.mapbase = regs->start; 1082a1dbd25SAndy Shevchenko up.port.irq = irq; 10922886ee9SMagnus Damm up.port.type = PORT_UNKNOWN; 11022886ee9SMagnus Damm up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP; 11122886ee9SMagnus Damm up.port.dev = &pdev->dev; 11222886ee9SMagnus Damm up.port.private_data = priv; 11322886ee9SMagnus Damm 11412082ba2SShinya Kuribayashi clk_prepare_enable(priv->sclk); 11522886ee9SMagnus Damm up.port.uartclk = clk_get_rate(priv->sclk); 11622886ee9SMagnus Damm 11722886ee9SMagnus Damm up.port.iotype = UPIO_MEM32; 11822886ee9SMagnus Damm up.port.serial_in = serial8250_em_serial_in; 11922886ee9SMagnus Damm up.port.serial_out = serial8250_em_serial_out; 12022886ee9SMagnus Damm up.dl_read = serial8250_em_serial_dl_read; 12122886ee9SMagnus Damm up.dl_write = serial8250_em_serial_dl_write; 12222886ee9SMagnus Damm 12322886ee9SMagnus Damm ret = serial8250_register_8250_port(&up); 12422886ee9SMagnus Damm if (ret < 0) { 12522886ee9SMagnus Damm dev_err(&pdev->dev, "unable to register 8250 port\n"); 12612082ba2SShinya Kuribayashi clk_disable_unprepare(priv->sclk); 127299a6257SLaurent Pinchart return ret; 12822886ee9SMagnus Damm } 12922886ee9SMagnus Damm 13022886ee9SMagnus Damm priv->line = ret; 13122886ee9SMagnus Damm platform_set_drvdata(pdev, priv); 13222886ee9SMagnus Damm return 0; 13322886ee9SMagnus Damm } 13422886ee9SMagnus Damm 135ae8d8a14SBill Pemberton static int serial8250_em_remove(struct platform_device *pdev) 13622886ee9SMagnus Damm { 13722886ee9SMagnus Damm struct serial8250_em_priv *priv = platform_get_drvdata(pdev); 13822886ee9SMagnus Damm 13922886ee9SMagnus Damm serial8250_unregister_port(priv->line); 14012082ba2SShinya Kuribayashi clk_disable_unprepare(priv->sclk); 14122886ee9SMagnus Damm return 0; 14222886ee9SMagnus Damm } 14322886ee9SMagnus Damm 144512f82a0SBill Pemberton static const struct of_device_id serial8250_em_dt_ids[] = { 1453e62c413SMagnus Damm { .compatible = "renesas,em-uart", }, 1463e62c413SMagnus Damm {}, 1473e62c413SMagnus Damm }; 1483e62c413SMagnus Damm MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids); 1493e62c413SMagnus Damm 15022886ee9SMagnus Damm static struct platform_driver serial8250_em_platform_driver = { 15122886ee9SMagnus Damm .driver = { 15222886ee9SMagnus Damm .name = "serial8250-em", 1533e62c413SMagnus Damm .of_match_table = serial8250_em_dt_ids, 15422886ee9SMagnus Damm }, 15522886ee9SMagnus Damm .probe = serial8250_em_probe, 1562d47b716SBill Pemberton .remove = serial8250_em_remove, 15722886ee9SMagnus Damm }; 15822886ee9SMagnus Damm 15922886ee9SMagnus Damm module_platform_driver(serial8250_em_platform_driver); 16022886ee9SMagnus Damm 16122886ee9SMagnus Damm MODULE_AUTHOR("Magnus Damm"); 16222886ee9SMagnus Damm MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver"); 16322886ee9SMagnus Damm MODULE_LICENSE("GPL v2"); 164