18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Renesas Emma Mobile 8250 driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Magnus Damm 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 128c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 138c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "8250.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define UART_DLL_EM 9 218c2ecf20Sopenharmony_ci#define UART_DLM_EM 10 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct serial8250_em_priv { 248c2ecf20Sopenharmony_ci struct clk *sclk; 258c2ecf20Sopenharmony_ci int line; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void serial8250_em_serial_out(struct uart_port *p, int offset, int value) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci switch (offset) { 318c2ecf20Sopenharmony_ci case UART_TX: /* TX @ 0x00 */ 328c2ecf20Sopenharmony_ci writeb(value, p->membase); 338c2ecf20Sopenharmony_ci break; 348c2ecf20Sopenharmony_ci case UART_FCR: /* FCR @ 0x0c (+1) */ 358c2ecf20Sopenharmony_ci case UART_LCR: /* LCR @ 0x10 (+1) */ 368c2ecf20Sopenharmony_ci case UART_MCR: /* MCR @ 0x14 (+1) */ 378c2ecf20Sopenharmony_ci case UART_SCR: /* SCR @ 0x20 (+1) */ 388c2ecf20Sopenharmony_ci writel(value, p->membase + ((offset + 1) << 2)); 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci case UART_IER: /* IER @ 0x04 */ 418c2ecf20Sopenharmony_ci value &= 0x0f; /* only 4 valid bits - not Xscale */ 428c2ecf20Sopenharmony_ci fallthrough; 438c2ecf20Sopenharmony_ci case UART_DLL_EM: /* DLL @ 0x24 (+9) */ 448c2ecf20Sopenharmony_ci case UART_DLM_EM: /* DLM @ 0x28 (+9) */ 458c2ecf20Sopenharmony_ci writel(value, p->membase + (offset << 2)); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic unsigned int serial8250_em_serial_in(struct uart_port *p, int offset) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci switch (offset) { 528c2ecf20Sopenharmony_ci case UART_RX: /* RX @ 0x00 */ 538c2ecf20Sopenharmony_ci return readb(p->membase); 548c2ecf20Sopenharmony_ci case UART_MCR: /* MCR @ 0x14 (+1) */ 558c2ecf20Sopenharmony_ci case UART_LSR: /* LSR @ 0x18 (+1) */ 568c2ecf20Sopenharmony_ci case UART_MSR: /* MSR @ 0x1c (+1) */ 578c2ecf20Sopenharmony_ci case UART_SCR: /* SCR @ 0x20 (+1) */ 588c2ecf20Sopenharmony_ci return readl(p->membase + ((offset + 1) << 2)); 598c2ecf20Sopenharmony_ci case UART_IER: /* IER @ 0x04 */ 608c2ecf20Sopenharmony_ci case UART_IIR: /* IIR @ 0x08 */ 618c2ecf20Sopenharmony_ci case UART_DLL_EM: /* DLL @ 0x24 (+9) */ 628c2ecf20Sopenharmony_ci case UART_DLM_EM: /* DLM @ 0x28 (+9) */ 638c2ecf20Sopenharmony_ci return readl(p->membase + (offset << 2)); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int serial8250_em_serial_dl_read(struct uart_8250_port *up) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci serial_out(up, UART_DLL_EM, value & 0xff); 768c2ecf20Sopenharmony_ci serial_out(up, UART_DLM_EM, value >> 8 & 0xff); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int serial8250_em_probe(struct platform_device *pdev) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct serial8250_em_priv *priv; 828c2ecf20Sopenharmony_ci struct uart_8250_port up; 838c2ecf20Sopenharmony_ci struct resource *regs; 848c2ecf20Sopenharmony_ci int irq, ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 878c2ecf20Sopenharmony_ci if (irq < 0) 888c2ecf20Sopenharmony_ci return irq; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 918c2ecf20Sopenharmony_ci if (!regs) { 928c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing registers\n"); 938c2ecf20Sopenharmony_ci return -EINVAL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 978c2ecf20Sopenharmony_ci if (!priv) 988c2ecf20Sopenharmony_ci return -ENOMEM; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci priv->sclk = devm_clk_get(&pdev->dev, "sclk"); 1018c2ecf20Sopenharmony_ci if (IS_ERR(priv->sclk)) { 1028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to get clock\n"); 1038c2ecf20Sopenharmony_ci return PTR_ERR(priv->sclk); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci memset(&up, 0, sizeof(up)); 1078c2ecf20Sopenharmony_ci up.port.mapbase = regs->start; 1088c2ecf20Sopenharmony_ci up.port.irq = irq; 1098c2ecf20Sopenharmony_ci up.port.type = PORT_16750; 1108c2ecf20Sopenharmony_ci up.port.flags = UPF_FIXED_PORT | UPF_IOREMAP | UPF_FIXED_TYPE; 1118c2ecf20Sopenharmony_ci up.port.dev = &pdev->dev; 1128c2ecf20Sopenharmony_ci up.port.private_data = priv; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci clk_prepare_enable(priv->sclk); 1158c2ecf20Sopenharmony_ci up.port.uartclk = clk_get_rate(priv->sclk); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci up.port.iotype = UPIO_MEM32; 1188c2ecf20Sopenharmony_ci up.port.serial_in = serial8250_em_serial_in; 1198c2ecf20Sopenharmony_ci up.port.serial_out = serial8250_em_serial_out; 1208c2ecf20Sopenharmony_ci up.dl_read = serial8250_em_serial_dl_read; 1218c2ecf20Sopenharmony_ci up.dl_write = serial8250_em_serial_dl_write; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ret = serial8250_register_8250_port(&up); 1248c2ecf20Sopenharmony_ci if (ret < 0) { 1258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to register 8250 port\n"); 1268c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->sclk); 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci priv->line = ret; 1318c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int serial8250_em_remove(struct platform_device *pdev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct serial8250_em_priv *priv = platform_get_drvdata(pdev); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci serial8250_unregister_port(priv->line); 1408c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->sclk); 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const struct of_device_id serial8250_em_dt_ids[] = { 1458c2ecf20Sopenharmony_ci { .compatible = "renesas,em-uart", }, 1468c2ecf20Sopenharmony_ci {}, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, serial8250_em_dt_ids); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct platform_driver serial8250_em_platform_driver = { 1518c2ecf20Sopenharmony_ci .driver = { 1528c2ecf20Sopenharmony_ci .name = "serial8250-em", 1538c2ecf20Sopenharmony_ci .of_match_table = serial8250_em_dt_ids, 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci .probe = serial8250_em_probe, 1568c2ecf20Sopenharmony_ci .remove = serial8250_em_remove, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cimodule_platform_driver(serial8250_em_platform_driver); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm"); 1628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver"); 1638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 164