162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/device.h> 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/module.h> 562306a36Sopenharmony_ci#include <linux/io.h> 662306a36Sopenharmony_ci#include <linux/mcb.h> 762306a36Sopenharmony_ci#include <linux/serial.h> 862306a36Sopenharmony_ci#include <linux/serial_core.h> 962306a36Sopenharmony_ci#include <linux/serial_8250.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define MEN_UART_ID_Z025 0x19 1262306a36Sopenharmony_ci#define MEN_UART_ID_Z057 0x39 1362306a36Sopenharmony_ci#define MEN_UART_ID_Z125 0x7d 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * IP Cores Z025 and Z057 can have up to 4 UART 1762306a36Sopenharmony_ci * The UARTs available are stored in a global 1862306a36Sopenharmony_ci * register saved in physical address + 0x40 1962306a36Sopenharmony_ci * Is saved as follows: 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * 7 0 2262306a36Sopenharmony_ci * +------+-------+-------+-------+-------+-------+-------+-------+ 2362306a36Sopenharmony_ci * |UART4 | UART3 | UART2 | UART1 | U4irq | U3irq | U2irq | U1irq | 2462306a36Sopenharmony_ci * +------+-------+-------+-------+-------+-------+-------+-------+ 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci#define MEN_UART1_MASK 0x01 2762306a36Sopenharmony_ci#define MEN_UART2_MASK 0x02 2862306a36Sopenharmony_ci#define MEN_UART3_MASK 0x04 2962306a36Sopenharmony_ci#define MEN_UART4_MASK 0x08 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MEN_Z125_UARTS_AVAILABLE 0x01 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MEN_Z025_MAX_UARTS 4 3462306a36Sopenharmony_ci#define MEN_UART_MEM_SIZE 0x10 3562306a36Sopenharmony_ci#define MEM_UART_REGISTER_SIZE 0x01 3662306a36Sopenharmony_ci#define MEN_Z025_REGISTER_OFFSET 0x40 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MEN_UART1_OFFSET 0 3962306a36Sopenharmony_ci#define MEN_UART2_OFFSET (MEN_UART1_OFFSET + MEN_UART_MEM_SIZE) 4062306a36Sopenharmony_ci#define MEN_UART3_OFFSET (MEN_UART2_OFFSET + MEN_UART_MEM_SIZE) 4162306a36Sopenharmony_ci#define MEN_UART4_OFFSET (MEN_UART3_OFFSET + MEN_UART_MEM_SIZE) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MEN_READ_REGISTER(addr) readb(addr) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define MAX_PORTS 4 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct serial_8250_men_mcb_data { 4862306a36Sopenharmony_ci int num_ports; 4962306a36Sopenharmony_ci int line[MAX_PORTS]; 5062306a36Sopenharmony_ci unsigned int offset[MAX_PORTS]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * The Z125 16550-compatible UART has no fixed base clock assigned 5562306a36Sopenharmony_ci * So, depending on the board we're on, we need to adjust the 5662306a36Sopenharmony_ci * parameter in order to really set the correct baudrate, and 5762306a36Sopenharmony_ci * do so if possible without user interaction 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic u32 men_lookup_uartclk(struct mcb_device *mdev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* use default value if board is not available below */ 6262306a36Sopenharmony_ci u32 clkval = 1041666; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci dev_info(&mdev->dev, "%s on board %s\n", 6562306a36Sopenharmony_ci dev_name(&mdev->dev), 6662306a36Sopenharmony_ci mdev->bus->name); 6762306a36Sopenharmony_ci if (strncmp(mdev->bus->name, "F075", 4) == 0) 6862306a36Sopenharmony_ci clkval = 1041666; 6962306a36Sopenharmony_ci else if (strncmp(mdev->bus->name, "F216", 4) == 0) 7062306a36Sopenharmony_ci clkval = 1843200; 7162306a36Sopenharmony_ci else if (strncmp(mdev->bus->name, "F210", 4) == 0) 7262306a36Sopenharmony_ci clkval = 115200; 7362306a36Sopenharmony_ci else if (strstr(mdev->bus->name, "215")) 7462306a36Sopenharmony_ci clkval = 1843200; 7562306a36Sopenharmony_ci else 7662306a36Sopenharmony_ci dev_info(&mdev->dev, 7762306a36Sopenharmony_ci "board not detected, using default uartclk\n"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci clkval = clkval << 4; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return clkval; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int read_uarts_available_from_register(struct resource *mem_res, 8562306a36Sopenharmony_ci u8 *uarts_available) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci void __iomem *mem; 8862306a36Sopenharmony_ci int reg_value; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!request_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, 9162306a36Sopenharmony_ci MEM_UART_REGISTER_SIZE, KBUILD_MODNAME)) { 9262306a36Sopenharmony_ci return -EBUSY; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mem = ioremap(mem_res->start + MEN_Z025_REGISTER_OFFSET, 9662306a36Sopenharmony_ci MEM_UART_REGISTER_SIZE); 9762306a36Sopenharmony_ci if (!mem) { 9862306a36Sopenharmony_ci release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, 9962306a36Sopenharmony_ci MEM_UART_REGISTER_SIZE); 10062306a36Sopenharmony_ci return -ENOMEM; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci reg_value = MEN_READ_REGISTER(mem); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci iounmap(mem); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, 10862306a36Sopenharmony_ci MEM_UART_REGISTER_SIZE); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci *uarts_available = reg_value >> 4; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int read_serial_data(struct mcb_device *mdev, 11662306a36Sopenharmony_ci struct resource *mem_res, 11762306a36Sopenharmony_ci struct serial_8250_men_mcb_data *serial_data) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u8 uarts_available; 12062306a36Sopenharmony_ci int count = 0; 12162306a36Sopenharmony_ci int mask; 12262306a36Sopenharmony_ci int res; 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci res = read_uarts_available_from_register(mem_res, &uarts_available); 12662306a36Sopenharmony_ci if (res < 0) 12762306a36Sopenharmony_ci return res; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < MAX_PORTS; i++) { 13062306a36Sopenharmony_ci mask = 0x1 << i; 13162306a36Sopenharmony_ci switch (uarts_available & mask) { 13262306a36Sopenharmony_ci case MEN_UART1_MASK: 13362306a36Sopenharmony_ci serial_data->offset[count] = MEN_UART1_OFFSET; 13462306a36Sopenharmony_ci count++; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case MEN_UART2_MASK: 13762306a36Sopenharmony_ci serial_data->offset[count] = MEN_UART2_OFFSET; 13862306a36Sopenharmony_ci count++; 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case MEN_UART3_MASK: 14162306a36Sopenharmony_ci serial_data->offset[count] = MEN_UART3_OFFSET; 14262306a36Sopenharmony_ci count++; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case MEN_UART4_MASK: 14562306a36Sopenharmony_ci serial_data->offset[count] = MEN_UART4_OFFSET; 14662306a36Sopenharmony_ci count++; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci default: 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (count <= 0 || count > MAX_PORTS) { 15462306a36Sopenharmony_ci dev_err(&mdev->dev, "unexpected number of ports: %u\n", 15562306a36Sopenharmony_ci count); 15662306a36Sopenharmony_ci return -ENODEV; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci serial_data->num_ports = count; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int init_serial_data(struct mcb_device *mdev, 16562306a36Sopenharmony_ci struct resource *mem_res, 16662306a36Sopenharmony_ci struct serial_8250_men_mcb_data *serial_data) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci switch (mdev->id) { 16962306a36Sopenharmony_ci case MEN_UART_ID_Z125: 17062306a36Sopenharmony_ci serial_data->num_ports = 1; 17162306a36Sopenharmony_ci serial_data->offset[0] = 0; 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci case MEN_UART_ID_Z025: 17462306a36Sopenharmony_ci case MEN_UART_ID_Z057: 17562306a36Sopenharmony_ci return read_serial_data(mdev, mem_res, serial_data); 17662306a36Sopenharmony_ci default: 17762306a36Sopenharmony_ci dev_err(&mdev->dev, "no supported device!\n"); 17862306a36Sopenharmony_ci return -ENODEV; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int serial_8250_men_mcb_probe(struct mcb_device *mdev, 18362306a36Sopenharmony_ci const struct mcb_device_id *id) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct uart_8250_port uart; 18662306a36Sopenharmony_ci struct serial_8250_men_mcb_data *data; 18762306a36Sopenharmony_ci struct resource *mem; 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci int res; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci mem = mcb_get_resource(mdev, IORESOURCE_MEM); 19262306a36Sopenharmony_ci if (mem == NULL) 19362306a36Sopenharmony_ci return -ENXIO; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci data = devm_kzalloc(&mdev->dev, 19662306a36Sopenharmony_ci sizeof(struct serial_8250_men_mcb_data), 19762306a36Sopenharmony_ci GFP_KERNEL); 19862306a36Sopenharmony_ci if (!data) 19962306a36Sopenharmony_ci return -ENOMEM; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci res = init_serial_data(mdev, mem, data); 20262306a36Sopenharmony_ci if (res < 0) 20362306a36Sopenharmony_ci return res; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n", 20662306a36Sopenharmony_ci mdev->id, data->num_ports); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci mcb_set_drvdata(mdev, data); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < data->num_ports; i++) { 21162306a36Sopenharmony_ci memset(&uart, 0, sizeof(struct uart_8250_port)); 21262306a36Sopenharmony_ci spin_lock_init(&uart.port.lock); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci uart.port.flags = UPF_SKIP_TEST | 21562306a36Sopenharmony_ci UPF_SHARE_IRQ | 21662306a36Sopenharmony_ci UPF_BOOT_AUTOCONF | 21762306a36Sopenharmony_ci UPF_IOREMAP; 21862306a36Sopenharmony_ci uart.port.iotype = UPIO_MEM; 21962306a36Sopenharmony_ci uart.port.uartclk = men_lookup_uartclk(mdev); 22062306a36Sopenharmony_ci uart.port.irq = mcb_get_irq(mdev); 22162306a36Sopenharmony_ci uart.port.mapbase = (unsigned long) mem->start 22262306a36Sopenharmony_ci + data->offset[i]; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* ok, register the port */ 22562306a36Sopenharmony_ci res = serial8250_register_8250_port(&uart); 22662306a36Sopenharmony_ci if (res < 0) { 22762306a36Sopenharmony_ci dev_err(&mdev->dev, "unable to register UART port\n"); 22862306a36Sopenharmony_ci return res; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci data->line[i] = res; 23262306a36Sopenharmony_ci dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data->line[i]); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void serial_8250_men_mcb_remove(struct mcb_device *mdev) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci int i; 24162306a36Sopenharmony_ci struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!data) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci for (i = 0; i < data->num_ports; i++) 24762306a36Sopenharmony_ci serial8250_unregister_port(data->line[i]); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic const struct mcb_device_id serial_8250_men_mcb_ids[] = { 25162306a36Sopenharmony_ci { .device = MEN_UART_ID_Z025 }, 25262306a36Sopenharmony_ci { .device = MEN_UART_ID_Z057 }, 25362306a36Sopenharmony_ci { .device = MEN_UART_ID_Z125 }, 25462306a36Sopenharmony_ci { } 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic struct mcb_driver mcb_driver = { 25962306a36Sopenharmony_ci .driver = { 26062306a36Sopenharmony_ci .name = "8250_men_mcb", 26162306a36Sopenharmony_ci }, 26262306a36Sopenharmony_ci .probe = serial_8250_men_mcb_probe, 26362306a36Sopenharmony_ci .remove = serial_8250_men_mcb_remove, 26462306a36Sopenharmony_ci .id_table = serial_8250_men_mcb_ids, 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_cimodule_mcb_driver(mcb_driver); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 26962306a36Sopenharmony_ciMODULE_DESCRIPTION("MEN 8250 UART driver"); 27062306a36Sopenharmony_ciMODULE_AUTHOR("Michael Moese <michael.moese@men.de"); 27162306a36Sopenharmony_ciMODULE_ALIAS("mcb:16z125"); 27262306a36Sopenharmony_ciMODULE_ALIAS("mcb:16z025"); 27362306a36Sopenharmony_ciMODULE_ALIAS("mcb:16z057"); 27462306a36Sopenharmony_ciMODULE_IMPORT_NS(MCB); 275