Skip to content

Commit a13e19c

Browse files
andy-shevgregkh
authored andcommitted
serial: 8250_lpss: split LPSS driver to separate module
The SoCs, such as Intel Braswell, have DesignWare UART IP. Split out the support of such chips to a separate module which also will be used for Intel Quark later. The rationale to have the separate driver to be existing: - Do not contaminate 8250_pci.c anymore with LPSS related quirks - All of them are using same DMA engine and they are Designware IP which means that in the future we might share the code between 8250_dw.c and 8250_lpss.c - It reduces the kernel memory footprint on non-X86 machines where 8250_pci.c is in use Besides the split the driver also has been refactored, in particular a) the DMA and port setup are separate functions, b) the two new structures lpss8250 and lpss8250_board are introduced to keep necessary data instead of pciserial_board, c) DMA parameters are passed to the DMA setup via mentioned custom structure. Most of the changes are done due to the future support of UART DMA on Intel Quark. The Intel Quark UART DMA support is based on bits taking from BSP code published by Intel earlier. The driver does not use any specific power management. PCI core takes care of the default behaviour during suspend and resume. Signed-off-by: Andy Shevchenko <[email protected]> Tested-by: Bryan O'Donoghue <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 68af490 commit a13e19c

File tree

4 files changed

+303
-235
lines changed

4 files changed

+303
-235
lines changed

drivers/tty/serial/8250/8250_lpss.c

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
* 8250_lpss.c - Driver for UART on Intel Braswell and various other Intel SoCs
3+
*
4+
* Copyright (C) 2016 Intel Corporation
5+
* Author: Andy Shevchenko <[email protected]>
6+
*
7+
* This program is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2 as
9+
* published by the Free Software Foundation.
10+
*/
11+
12+
#include <linux/bitops.h>
13+
#include <linux/module.h>
14+
#include <linux/pci.h>
15+
#include <linux/rational.h>
16+
17+
#include <linux/dmaengine.h>
18+
#include <linux/platform_data/dma-dw.h>
19+
20+
#include "8250.h"
21+
22+
#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a
23+
#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c
24+
25+
#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a
26+
#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c
27+
28+
#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3
29+
#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4
30+
31+
/* Intel LPSS specific registers */
32+
33+
#define BYT_PRV_CLK 0x800
34+
#define BYT_PRV_CLK_EN BIT(0)
35+
#define BYT_PRV_CLK_M_VAL_SHIFT 1
36+
#define BYT_PRV_CLK_N_VAL_SHIFT 16
37+
#define BYT_PRV_CLK_UPDATE BIT(31)
38+
39+
#define BYT_TX_OVF_INT 0x820
40+
#define BYT_TX_OVF_INT_MASK BIT(1)
41+
42+
struct lpss8250;
43+
44+
struct lpss8250_board {
45+
unsigned long freq;
46+
unsigned int base_baud;
47+
int (*setup)(struct lpss8250 *, struct uart_port *p);
48+
};
49+
50+
struct lpss8250 {
51+
int line;
52+
struct lpss8250_board *board;
53+
54+
/* DMA parameters */
55+
struct uart_8250_dma dma;
56+
struct dw_dma_slave dma_param;
57+
u8 dma_maxburst;
58+
};
59+
60+
static void byt_set_termios(struct uart_port *p, struct ktermios *termios,
61+
struct ktermios *old)
62+
{
63+
unsigned int baud = tty_termios_baud_rate(termios);
64+
struct lpss8250 *lpss = p->private_data;
65+
unsigned long fref = lpss->board->freq, fuart = baud * 16;
66+
unsigned long w = BIT(15) - 1;
67+
unsigned long m, n;
68+
u32 reg;
69+
70+
/* Gracefully handle the B0 case: fall back to B9600 */
71+
fuart = fuart ? fuart : 9600 * 16;
72+
73+
/* Get Fuart closer to Fref */
74+
fuart *= rounddown_pow_of_two(fref / fuart);
75+
76+
/*
77+
* For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
78+
* dividers must be adjusted.
79+
*
80+
* uartclk = (m / n) * 100 MHz, where m <= n
81+
*/
82+
rational_best_approximation(fuart, fref, w, w, &m, &n);
83+
p->uartclk = fuart;
84+
85+
/* Reset the clock */
86+
reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
87+
writel(reg, p->membase + BYT_PRV_CLK);
88+
reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
89+
writel(reg, p->membase + BYT_PRV_CLK);
90+
91+
p->status &= ~UPSTAT_AUTOCTS;
92+
if (termios->c_cflag & CRTSCTS)
93+
p->status |= UPSTAT_AUTOCTS;
94+
95+
serial8250_do_set_termios(p, termios, old);
96+
}
97+
98+
static unsigned int byt_get_mctrl(struct uart_port *port)
99+
{
100+
unsigned int ret = serial8250_do_get_mctrl(port);
101+
102+
/* Force DCD and DSR signals to permanently be reported as active */
103+
ret |= TIOCM_CAR | TIOCM_DSR;
104+
105+
return ret;
106+
}
107+
108+
static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
109+
{
110+
struct dw_dma_slave *param = &lpss->dma_param;
111+
struct uart_8250_port *up = up_to_u8250p(port);
112+
struct pci_dev *pdev = to_pci_dev(port->dev);
113+
unsigned int dma_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
114+
struct pci_dev *dma_dev = pci_get_slot(pdev->bus, dma_devfn);
115+
116+
switch (pdev->device) {
117+
case PCI_DEVICE_ID_INTEL_BYT_UART1:
118+
case PCI_DEVICE_ID_INTEL_BSW_UART1:
119+
case PCI_DEVICE_ID_INTEL_BDW_UART1:
120+
param->src_id = 3;
121+
param->dst_id = 2;
122+
break;
123+
case PCI_DEVICE_ID_INTEL_BYT_UART2:
124+
case PCI_DEVICE_ID_INTEL_BSW_UART2:
125+
case PCI_DEVICE_ID_INTEL_BDW_UART2:
126+
param->src_id = 5;
127+
param->dst_id = 4;
128+
break;
129+
default:
130+
return -EINVAL;
131+
}
132+
133+
param->dma_dev = &dma_dev->dev;
134+
param->m_master = 0;
135+
param->p_master = 1;
136+
137+
/* TODO: Detect FIFO size automaticaly for DesignWare 8250 */
138+
port->fifosize = 64;
139+
up->tx_loadsz = 64;
140+
141+
lpss->dma_maxburst = 16;
142+
143+
port->set_termios = byt_set_termios;
144+
port->get_mctrl = byt_get_mctrl;
145+
146+
/* Disable TX counter interrupts */
147+
writel(BYT_TX_OVF_INT_MASK, port->membase + BYT_TX_OVF_INT);
148+
149+
return 0;
150+
}
151+
152+
static bool lpss8250_dma_filter(struct dma_chan *chan, void *param)
153+
{
154+
struct dw_dma_slave *dws = param;
155+
156+
if (dws->dma_dev != chan->device->dev)
157+
return false;
158+
159+
chan->private = dws;
160+
return true;
161+
}
162+
163+
static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port)
164+
{
165+
struct uart_8250_dma *dma = &lpss->dma;
166+
struct dw_dma_slave *rx_param, *tx_param;
167+
struct device *dev = port->port.dev;
168+
169+
rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
170+
if (!rx_param)
171+
return -ENOMEM;
172+
173+
tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
174+
if (!tx_param)
175+
return -ENOMEM;
176+
177+
*rx_param = lpss->dma_param;
178+
dma->rxconf.src_maxburst = lpss->dma_maxburst;
179+
180+
*tx_param = lpss->dma_param;
181+
dma->txconf.dst_maxburst = lpss->dma_maxburst;
182+
183+
dma->fn = lpss8250_dma_filter;
184+
dma->rx_param = rx_param;
185+
dma->tx_param = tx_param;
186+
187+
port->dma = dma;
188+
return 0;
189+
}
190+
191+
static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
192+
{
193+
struct uart_8250_port uart;
194+
struct lpss8250 *lpss;
195+
int ret;
196+
197+
ret = pcim_enable_device(pdev);
198+
if (ret)
199+
return ret;
200+
201+
pci_set_master(pdev);
202+
203+
lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL);
204+
if (!lpss)
205+
return -ENOMEM;
206+
207+
lpss->board = (struct lpss8250_board *)id->driver_data;
208+
209+
memset(&uart, 0, sizeof(struct uart_8250_port));
210+
211+
uart.port.dev = &pdev->dev;
212+
uart.port.irq = pdev->irq;
213+
uart.port.private_data = lpss;
214+
uart.port.type = PORT_16550A;
215+
uart.port.iotype = UPIO_MEM;
216+
uart.port.regshift = 2;
217+
uart.port.uartclk = lpss->board->base_baud * 16;
218+
uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
219+
uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE;
220+
uart.port.mapbase = pci_resource_start(pdev, 0);
221+
uart.port.membase = pcim_iomap(pdev, 0, 0);
222+
if (!uart.port.membase)
223+
return -ENOMEM;
224+
225+
ret = lpss->board->setup(lpss, &uart.port);
226+
if (ret)
227+
return ret;
228+
229+
ret = lpss8250_dma_setup(lpss, &uart);
230+
if (ret)
231+
return ret;
232+
233+
ret = serial8250_register_8250_port(&uart);
234+
if (ret < 0)
235+
return ret;
236+
237+
lpss->line = ret;
238+
239+
pci_set_drvdata(pdev, lpss);
240+
return 0;
241+
}
242+
243+
static void lpss8250_remove(struct pci_dev *pdev)
244+
{
245+
struct lpss8250 *lpss = pci_get_drvdata(pdev);
246+
247+
serial8250_unregister_port(lpss->line);
248+
}
249+
250+
static const struct lpss8250_board byt_board = {
251+
.freq = 100000000,
252+
.base_baud = 2764800,
253+
.setup = byt_serial_setup,
254+
};
255+
256+
#define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
257+
258+
static const struct pci_device_id pci_ids[] = {
259+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board),
260+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board),
261+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board),
262+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART2, byt_board),
263+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART1, byt_board),
264+
LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART2, byt_board),
265+
{ },
266+
};
267+
MODULE_DEVICE_TABLE(pci, pci_ids);
268+
269+
static struct pci_driver lpss8250_pci_driver = {
270+
.name = "8250_lpss",
271+
.id_table = pci_ids,
272+
.probe = lpss8250_probe,
273+
.remove = lpss8250_remove,
274+
};
275+
276+
module_pci_driver(lpss8250_pci_driver);
277+
278+
MODULE_AUTHOR("Intel Corporation");
279+
MODULE_LICENSE("GPL v2");
280+
MODULE_DESCRIPTION("Intel LPSS UART driver");

0 commit comments

Comments
 (0)