Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | // SPDX-License-Identifier: GPL-2.0 #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/io.h> #include <linux/mcb.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> #define MEN_UART_ID_Z025 0x19 #define MEN_UART_ID_Z057 0x39 #define MEN_UART_ID_Z125 0x7d #define MEN_UART_MEM_SIZE 0x10 struct serial_8250_men_mcb_data { struct uart_8250_port uart; int line; }; /* * The Z125 16550-compatible UART has no fixed base clock assigned * So, depending on the board we're on, we need to adjust the * parameter in order to really set the correct baudrate, and * do so if possible without user interaction */ static u32 men_lookup_uartclk(struct mcb_device *mdev) { /* use default value if board is not available below */ u32 clkval = 1041666; dev_info(&mdev->dev, "%s on board %s\n", dev_name(&mdev->dev), mdev->bus->name); if (strncmp(mdev->bus->name, "F075", 4) == 0) clkval = 1041666; else if (strncmp(mdev->bus->name, "F216", 4) == 0) clkval = 1843200; else if (strncmp(mdev->bus->name, "G215", 4) == 0) clkval = 1843200; else if (strncmp(mdev->bus->name, "F210", 4) == 0) clkval = 115200; else dev_info(&mdev->dev, "board not detected, using default uartclk\n"); clkval = clkval << 4; return clkval; } static int get_num_ports(struct mcb_device *mdev, void __iomem *membase) { switch (mdev->id) { case MEN_UART_ID_Z125: return 1U; case MEN_UART_ID_Z025: return readb(membase) >> 4; case MEN_UART_ID_Z057: return 4U; default: dev_err(&mdev->dev, "no supported device!\n"); return -ENODEV; } } static int serial_8250_men_mcb_probe(struct mcb_device *mdev, const struct mcb_device_id *id) { struct serial_8250_men_mcb_data *data; struct resource *mem; int num_ports; int i; void __iomem *membase; mem = mcb_get_resource(mdev, IORESOURCE_MEM); if (mem == NULL) return -ENXIO; membase = devm_ioremap_resource(&mdev->dev, mem); if (IS_ERR(membase)) return PTR_ERR_OR_ZERO(membase); num_ports = get_num_ports(mdev, membase); dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n", mdev->id, num_ports); if (num_ports <= 0 || num_ports > 4) { dev_err(&mdev->dev, "unexpected number of ports: %u\n", num_ports); return -ENODEV; } data = devm_kcalloc(&mdev->dev, num_ports, sizeof(struct serial_8250_men_mcb_data), GFP_KERNEL); if (!data) return -ENOMEM; mcb_set_drvdata(mdev, data); for (i = 0; i < num_ports; i++) { data[i].uart.port.dev = mdev->dma_dev; spin_lock_init(&data[i].uart.port.lock); data[i].uart.port.type = PORT_16550; data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; data[i].uart.port.iotype = UPIO_MEM; data[i].uart.port.uartclk = men_lookup_uartclk(mdev); data[i].uart.port.regshift = 0; data[i].uart.port.irq = mcb_get_irq(mdev); data[i].uart.port.membase = membase; data[i].uart.port.fifosize = 60; data[i].uart.port.mapbase = (unsigned long) mem->start + i * MEN_UART_MEM_SIZE; data[i].uart.port.iobase = data[i].uart.port.mapbase; /* ok, register the port */ data[i].line = serial8250_register_8250_port(&data[i].uart); if (data[i].line < 0) { dev_err(&mdev->dev, "unable to register UART port\n"); return data[i].line; } dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line); } return 0; } static void serial_8250_men_mcb_remove(struct mcb_device *mdev) { int num_ports, i; struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev); if (!data) return; num_ports = get_num_ports(mdev, data[0].uart.port.membase); if (num_ports <= 0 || num_ports > 4) { dev_err(&mdev->dev, "error retrieving number of ports!\n"); return; } for (i = 0; i < num_ports; i++) serial8250_unregister_port(data[i].line); } static const struct mcb_device_id serial_8250_men_mcb_ids[] = { { .device = MEN_UART_ID_Z025 }, { .device = MEN_UART_ID_Z057 }, { .device = MEN_UART_ID_Z125 }, { } }; MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); static struct mcb_driver mcb_driver = { .driver = { .name = "8250_men_mcb", .owner = THIS_MODULE, }, .probe = serial_8250_men_mcb_probe, .remove = serial_8250_men_mcb_remove, .id_table = serial_8250_men_mcb_ids, }; module_mcb_driver(mcb_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MEN 8250 UART driver"); MODULE_AUTHOR("Michael Moese <michael.moese@men.de"); MODULE_ALIAS("mcb:16z125"); MODULE_ALIAS("mcb:16z025"); MODULE_ALIAS("mcb:16z057"); MODULE_IMPORT_NS(MCB); |