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