18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/device.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/io.h> 68c2ecf20Sopenharmony_ci#include <linux/mcb.h> 78c2ecf20Sopenharmony_ci#include <linux/serial.h> 88c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 98c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 108c2ecf20Sopenharmony_ci#include <uapi/linux/serial_core.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define MEN_UART_ID_Z025 0x19 138c2ecf20Sopenharmony_ci#define MEN_UART_ID_Z057 0x39 148c2ecf20Sopenharmony_ci#define MEN_UART_ID_Z125 0x7d 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MEN_UART_MEM_SIZE 0x10 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct serial_8250_men_mcb_data { 198c2ecf20Sopenharmony_ci struct uart_8250_port uart; 208c2ecf20Sopenharmony_ci int line; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * The Z125 16550-compatible UART has no fixed base clock assigned 258c2ecf20Sopenharmony_ci * So, depending on the board we're on, we need to adjust the 268c2ecf20Sopenharmony_ci * parameter in order to really set the correct baudrate, and 278c2ecf20Sopenharmony_ci * do so if possible without user interaction 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic u32 men_lookup_uartclk(struct mcb_device *mdev) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci /* use default value if board is not available below */ 328c2ecf20Sopenharmony_ci u32 clkval = 1041666; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci dev_info(&mdev->dev, "%s on board %s\n", 358c2ecf20Sopenharmony_ci dev_name(&mdev->dev), 368c2ecf20Sopenharmony_ci mdev->bus->name); 378c2ecf20Sopenharmony_ci if (strncmp(mdev->bus->name, "F075", 4) == 0) 388c2ecf20Sopenharmony_ci clkval = 1041666; 398c2ecf20Sopenharmony_ci else if (strncmp(mdev->bus->name, "F216", 4) == 0) 408c2ecf20Sopenharmony_ci clkval = 1843200; 418c2ecf20Sopenharmony_ci else if (strncmp(mdev->bus->name, "G215", 4) == 0) 428c2ecf20Sopenharmony_ci clkval = 1843200; 438c2ecf20Sopenharmony_ci else if (strncmp(mdev->bus->name, "F210", 4) == 0) 448c2ecf20Sopenharmony_ci clkval = 115200; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci dev_info(&mdev->dev, 478c2ecf20Sopenharmony_ci "board not detected, using default uartclk\n"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci clkval = clkval << 4; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return clkval; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int get_num_ports(struct mcb_device *mdev, 558c2ecf20Sopenharmony_ci void __iomem *membase) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci switch (mdev->id) { 588c2ecf20Sopenharmony_ci case MEN_UART_ID_Z125: 598c2ecf20Sopenharmony_ci return 1U; 608c2ecf20Sopenharmony_ci case MEN_UART_ID_Z025: 618c2ecf20Sopenharmony_ci return readb(membase) >> 4; 628c2ecf20Sopenharmony_ci case MEN_UART_ID_Z057: 638c2ecf20Sopenharmony_ci return 4U; 648c2ecf20Sopenharmony_ci default: 658c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "no supported device!\n"); 668c2ecf20Sopenharmony_ci return -ENODEV; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int serial_8250_men_mcb_probe(struct mcb_device *mdev, 718c2ecf20Sopenharmony_ci const struct mcb_device_id *id) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct serial_8250_men_mcb_data *data; 748c2ecf20Sopenharmony_ci struct resource *mem; 758c2ecf20Sopenharmony_ci int num_ports; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci void __iomem *membase; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci mem = mcb_get_resource(mdev, IORESOURCE_MEM); 808c2ecf20Sopenharmony_ci if (mem == NULL) 818c2ecf20Sopenharmony_ci return -ENXIO; 828c2ecf20Sopenharmony_ci membase = devm_ioremap_resource(&mdev->dev, mem); 838c2ecf20Sopenharmony_ci if (IS_ERR(membase)) 848c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(membase); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci num_ports = get_num_ports(mdev, membase); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n", 898c2ecf20Sopenharmony_ci mdev->id, num_ports); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (num_ports <= 0 || num_ports > 4) { 928c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "unexpected number of ports: %u\n", 938c2ecf20Sopenharmony_ci num_ports); 948c2ecf20Sopenharmony_ci return -ENODEV; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci data = devm_kcalloc(&mdev->dev, num_ports, 988c2ecf20Sopenharmony_ci sizeof(struct serial_8250_men_mcb_data), 998c2ecf20Sopenharmony_ci GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!data) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mcb_set_drvdata(mdev, data); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) { 1068c2ecf20Sopenharmony_ci data[i].uart.port.dev = mdev->dma_dev; 1078c2ecf20Sopenharmony_ci spin_lock_init(&data[i].uart.port.lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci data[i].uart.port.type = PORT_16550; 1108c2ecf20Sopenharmony_ci data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ 1118c2ecf20Sopenharmony_ci | UPF_FIXED_TYPE; 1128c2ecf20Sopenharmony_ci data[i].uart.port.iotype = UPIO_MEM; 1138c2ecf20Sopenharmony_ci data[i].uart.port.uartclk = men_lookup_uartclk(mdev); 1148c2ecf20Sopenharmony_ci data[i].uart.port.regshift = 0; 1158c2ecf20Sopenharmony_ci data[i].uart.port.irq = mcb_get_irq(mdev); 1168c2ecf20Sopenharmony_ci data[i].uart.port.membase = membase; 1178c2ecf20Sopenharmony_ci data[i].uart.port.fifosize = 60; 1188c2ecf20Sopenharmony_ci data[i].uart.port.mapbase = (unsigned long) mem->start 1198c2ecf20Sopenharmony_ci + i * MEN_UART_MEM_SIZE; 1208c2ecf20Sopenharmony_ci data[i].uart.port.iobase = data[i].uart.port.mapbase; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* ok, register the port */ 1238c2ecf20Sopenharmony_ci data[i].line = serial8250_register_8250_port(&data[i].uart); 1248c2ecf20Sopenharmony_ci if (data[i].line < 0) { 1258c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "unable to register UART port\n"); 1268c2ecf20Sopenharmony_ci return data[i].line; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void serial_8250_men_mcb_remove(struct mcb_device *mdev) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int num_ports, i; 1378c2ecf20Sopenharmony_ci struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!data) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci num_ports = get_num_ports(mdev, data[0].uart.port.membase); 1438c2ecf20Sopenharmony_ci if (num_ports <= 0 || num_ports > 4) { 1448c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "error retrieving number of ports!\n"); 1458c2ecf20Sopenharmony_ci return; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) 1498c2ecf20Sopenharmony_ci serial8250_unregister_port(data[i].line); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct mcb_device_id serial_8250_men_mcb_ids[] = { 1538c2ecf20Sopenharmony_ci { .device = MEN_UART_ID_Z025 }, 1548c2ecf20Sopenharmony_ci { .device = MEN_UART_ID_Z057 }, 1558c2ecf20Sopenharmony_ci { .device = MEN_UART_ID_Z125 }, 1568c2ecf20Sopenharmony_ci { } 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct mcb_driver mcb_driver = { 1618c2ecf20Sopenharmony_ci .driver = { 1628c2ecf20Sopenharmony_ci .name = "8250_men_mcb", 1638c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci .probe = serial_8250_men_mcb_probe, 1668c2ecf20Sopenharmony_ci .remove = serial_8250_men_mcb_remove, 1678c2ecf20Sopenharmony_ci .id_table = serial_8250_men_mcb_ids, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_cimodule_mcb_driver(mcb_driver); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MEN 8250 UART driver"); 1738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Moese <michael.moese@men.de"); 1748c2ecf20Sopenharmony_ciMODULE_ALIAS("mcb:16z125"); 1758c2ecf20Sopenharmony_ciMODULE_ALIAS("mcb:16z025"); 1768c2ecf20Sopenharmony_ciMODULE_ALIAS("mcb:16z057"); 1778c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(MCB); 178