1*e34a79d0SMatthew Gerlach // SPDX-License-Identifier: GPL-2.0
2*e34a79d0SMatthew Gerlach /*
3*e34a79d0SMatthew Gerlach * Driver for FPGA UART
4*e34a79d0SMatthew Gerlach *
5*e34a79d0SMatthew Gerlach * Copyright (C) 2022 Intel Corporation.
6*e34a79d0SMatthew Gerlach *
7*e34a79d0SMatthew Gerlach * Authors:
8*e34a79d0SMatthew Gerlach * Ananda Ravuri <ananda.ravuri@intel.com>
9*e34a79d0SMatthew Gerlach * Matthew Gerlach <matthew.gerlach@linux.intel.com>
10*e34a79d0SMatthew Gerlach */
11*e34a79d0SMatthew Gerlach
12*e34a79d0SMatthew Gerlach #include <linux/bitfield.h>
13*e34a79d0SMatthew Gerlach #include <linux/device.h>
14*e34a79d0SMatthew Gerlach #include <linux/dfl.h>
15*e34a79d0SMatthew Gerlach #include <linux/errno.h>
16*e34a79d0SMatthew Gerlach #include <linux/ioport.h>
17*e34a79d0SMatthew Gerlach #include <linux/module.h>
18*e34a79d0SMatthew Gerlach #include <linux/mod_devicetable.h>
19*e34a79d0SMatthew Gerlach #include <linux/types.h>
20*e34a79d0SMatthew Gerlach
21*e34a79d0SMatthew Gerlach #include <linux/serial.h>
22*e34a79d0SMatthew Gerlach #include <linux/serial_8250.h>
23*e34a79d0SMatthew Gerlach
24*e34a79d0SMatthew Gerlach #define DFHv1_PARAM_ID_CLK_FRQ 0x2
25*e34a79d0SMatthew Gerlach #define DFHv1_PARAM_ID_FIFO_LEN 0x3
26*e34a79d0SMatthew Gerlach
27*e34a79d0SMatthew Gerlach #define DFHv1_PARAM_ID_REG_LAYOUT 0x4
28*e34a79d0SMatthew Gerlach #define DFHv1_PARAM_REG_LAYOUT_WIDTH GENMASK_ULL(63, 32)
29*e34a79d0SMatthew Gerlach #define DFHv1_PARAM_REG_LAYOUT_SHIFT GENMASK_ULL(31, 0)
30*e34a79d0SMatthew Gerlach
31*e34a79d0SMatthew Gerlach struct dfl_uart {
32*e34a79d0SMatthew Gerlach int line;
33*e34a79d0SMatthew Gerlach };
34*e34a79d0SMatthew Gerlach
dfh_get_u64_param_val(struct dfl_device * dfl_dev,int param_id,u64 * pval)35*e34a79d0SMatthew Gerlach static int dfh_get_u64_param_val(struct dfl_device *dfl_dev, int param_id, u64 *pval)
36*e34a79d0SMatthew Gerlach {
37*e34a79d0SMatthew Gerlach size_t psize;
38*e34a79d0SMatthew Gerlach u64 *p;
39*e34a79d0SMatthew Gerlach
40*e34a79d0SMatthew Gerlach p = dfh_find_param(dfl_dev, param_id, &psize);
41*e34a79d0SMatthew Gerlach if (IS_ERR(p))
42*e34a79d0SMatthew Gerlach return PTR_ERR(p);
43*e34a79d0SMatthew Gerlach
44*e34a79d0SMatthew Gerlach if (psize != sizeof(*pval))
45*e34a79d0SMatthew Gerlach return -EINVAL;
46*e34a79d0SMatthew Gerlach
47*e34a79d0SMatthew Gerlach *pval = *p;
48*e34a79d0SMatthew Gerlach
49*e34a79d0SMatthew Gerlach return 0;
50*e34a79d0SMatthew Gerlach }
51*e34a79d0SMatthew Gerlach
dfl_uart_get_params(struct dfl_device * dfl_dev,struct uart_8250_port * uart)52*e34a79d0SMatthew Gerlach static int dfl_uart_get_params(struct dfl_device *dfl_dev, struct uart_8250_port *uart)
53*e34a79d0SMatthew Gerlach {
54*e34a79d0SMatthew Gerlach struct device *dev = &dfl_dev->dev;
55*e34a79d0SMatthew Gerlach u64 fifo_len, clk_freq, reg_layout;
56*e34a79d0SMatthew Gerlach u32 reg_width;
57*e34a79d0SMatthew Gerlach int ret;
58*e34a79d0SMatthew Gerlach
59*e34a79d0SMatthew Gerlach ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_CLK_FRQ, &clk_freq);
60*e34a79d0SMatthew Gerlach if (ret)
61*e34a79d0SMatthew Gerlach return dev_err_probe(dev, ret, "missing CLK_FRQ param\n");
62*e34a79d0SMatthew Gerlach
63*e34a79d0SMatthew Gerlach uart->port.uartclk = clk_freq;
64*e34a79d0SMatthew Gerlach
65*e34a79d0SMatthew Gerlach ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_FIFO_LEN, &fifo_len);
66*e34a79d0SMatthew Gerlach if (ret)
67*e34a79d0SMatthew Gerlach return dev_err_probe(dev, ret, "missing FIFO_LEN param\n");
68*e34a79d0SMatthew Gerlach
69*e34a79d0SMatthew Gerlach switch (fifo_len) {
70*e34a79d0SMatthew Gerlach case 32:
71*e34a79d0SMatthew Gerlach uart->port.type = PORT_ALTR_16550_F32;
72*e34a79d0SMatthew Gerlach break;
73*e34a79d0SMatthew Gerlach
74*e34a79d0SMatthew Gerlach case 64:
75*e34a79d0SMatthew Gerlach uart->port.type = PORT_ALTR_16550_F64;
76*e34a79d0SMatthew Gerlach break;
77*e34a79d0SMatthew Gerlach
78*e34a79d0SMatthew Gerlach case 128:
79*e34a79d0SMatthew Gerlach uart->port.type = PORT_ALTR_16550_F128;
80*e34a79d0SMatthew Gerlach break;
81*e34a79d0SMatthew Gerlach
82*e34a79d0SMatthew Gerlach default:
83*e34a79d0SMatthew Gerlach return dev_err_probe(dev, -EINVAL, "unsupported FIFO_LEN %llu\n", fifo_len);
84*e34a79d0SMatthew Gerlach }
85*e34a79d0SMatthew Gerlach
86*e34a79d0SMatthew Gerlach ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_REG_LAYOUT, ®_layout);
87*e34a79d0SMatthew Gerlach if (ret)
88*e34a79d0SMatthew Gerlach return dev_err_probe(dev, ret, "missing REG_LAYOUT param\n");
89*e34a79d0SMatthew Gerlach
90*e34a79d0SMatthew Gerlach uart->port.regshift = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_SHIFT, reg_layout);
91*e34a79d0SMatthew Gerlach reg_width = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_WIDTH, reg_layout);
92*e34a79d0SMatthew Gerlach switch (reg_width) {
93*e34a79d0SMatthew Gerlach case 4:
94*e34a79d0SMatthew Gerlach uart->port.iotype = UPIO_MEM32;
95*e34a79d0SMatthew Gerlach break;
96*e34a79d0SMatthew Gerlach
97*e34a79d0SMatthew Gerlach case 2:
98*e34a79d0SMatthew Gerlach uart->port.iotype = UPIO_MEM16;
99*e34a79d0SMatthew Gerlach break;
100*e34a79d0SMatthew Gerlach
101*e34a79d0SMatthew Gerlach default:
102*e34a79d0SMatthew Gerlach return dev_err_probe(dev, -EINVAL, "unsupported reg-width %u\n", reg_width);
103*e34a79d0SMatthew Gerlach
104*e34a79d0SMatthew Gerlach }
105*e34a79d0SMatthew Gerlach
106*e34a79d0SMatthew Gerlach return 0;
107*e34a79d0SMatthew Gerlach }
108*e34a79d0SMatthew Gerlach
dfl_uart_probe(struct dfl_device * dfl_dev)109*e34a79d0SMatthew Gerlach static int dfl_uart_probe(struct dfl_device *dfl_dev)
110*e34a79d0SMatthew Gerlach {
111*e34a79d0SMatthew Gerlach struct device *dev = &dfl_dev->dev;
112*e34a79d0SMatthew Gerlach struct uart_8250_port uart = { };
113*e34a79d0SMatthew Gerlach struct dfl_uart *dfluart;
114*e34a79d0SMatthew Gerlach int ret;
115*e34a79d0SMatthew Gerlach
116*e34a79d0SMatthew Gerlach uart.port.flags = UPF_IOREMAP;
117*e34a79d0SMatthew Gerlach uart.port.mapbase = dfl_dev->mmio_res.start;
118*e34a79d0SMatthew Gerlach uart.port.mapsize = resource_size(&dfl_dev->mmio_res);
119*e34a79d0SMatthew Gerlach
120*e34a79d0SMatthew Gerlach ret = dfl_uart_get_params(dfl_dev, &uart);
121*e34a79d0SMatthew Gerlach if (ret < 0)
122*e34a79d0SMatthew Gerlach return dev_err_probe(dev, ret, "failed uart feature walk\n");
123*e34a79d0SMatthew Gerlach
124*e34a79d0SMatthew Gerlach if (dfl_dev->num_irqs == 1)
125*e34a79d0SMatthew Gerlach uart.port.irq = dfl_dev->irqs[0];
126*e34a79d0SMatthew Gerlach
127*e34a79d0SMatthew Gerlach dfluart = devm_kzalloc(dev, sizeof(*dfluart), GFP_KERNEL);
128*e34a79d0SMatthew Gerlach if (!dfluart)
129*e34a79d0SMatthew Gerlach return -ENOMEM;
130*e34a79d0SMatthew Gerlach
131*e34a79d0SMatthew Gerlach dfluart->line = serial8250_register_8250_port(&uart);
132*e34a79d0SMatthew Gerlach if (dfluart->line < 0)
133*e34a79d0SMatthew Gerlach return dev_err_probe(dev, dfluart->line, "unable to register 8250 port.\n");
134*e34a79d0SMatthew Gerlach
135*e34a79d0SMatthew Gerlach dev_set_drvdata(dev, dfluart);
136*e34a79d0SMatthew Gerlach
137*e34a79d0SMatthew Gerlach return 0;
138*e34a79d0SMatthew Gerlach }
139*e34a79d0SMatthew Gerlach
dfl_uart_remove(struct dfl_device * dfl_dev)140*e34a79d0SMatthew Gerlach static void dfl_uart_remove(struct dfl_device *dfl_dev)
141*e34a79d0SMatthew Gerlach {
142*e34a79d0SMatthew Gerlach struct dfl_uart *dfluart = dev_get_drvdata(&dfl_dev->dev);
143*e34a79d0SMatthew Gerlach
144*e34a79d0SMatthew Gerlach serial8250_unregister_port(dfluart->line);
145*e34a79d0SMatthew Gerlach }
146*e34a79d0SMatthew Gerlach
147*e34a79d0SMatthew Gerlach #define FME_FEATURE_ID_UART 0x24
148*e34a79d0SMatthew Gerlach
149*e34a79d0SMatthew Gerlach static const struct dfl_device_id dfl_uart_ids[] = {
150*e34a79d0SMatthew Gerlach { FME_ID, FME_FEATURE_ID_UART },
151*e34a79d0SMatthew Gerlach { }
152*e34a79d0SMatthew Gerlach };
153*e34a79d0SMatthew Gerlach MODULE_DEVICE_TABLE(dfl, dfl_uart_ids);
154*e34a79d0SMatthew Gerlach
155*e34a79d0SMatthew Gerlach static struct dfl_driver dfl_uart_driver = {
156*e34a79d0SMatthew Gerlach .drv = {
157*e34a79d0SMatthew Gerlach .name = "dfl-uart",
158*e34a79d0SMatthew Gerlach },
159*e34a79d0SMatthew Gerlach .id_table = dfl_uart_ids,
160*e34a79d0SMatthew Gerlach .probe = dfl_uart_probe,
161*e34a79d0SMatthew Gerlach .remove = dfl_uart_remove,
162*e34a79d0SMatthew Gerlach };
163*e34a79d0SMatthew Gerlach module_dfl_driver(dfl_uart_driver);
164*e34a79d0SMatthew Gerlach
165*e34a79d0SMatthew Gerlach MODULE_DESCRIPTION("DFL Intel UART driver");
166*e34a79d0SMatthew Gerlach MODULE_AUTHOR("Intel Corporation");
167*e34a79d0SMatthew Gerlach MODULE_LICENSE("GPL");
168