xref: /openbmc/linux/drivers/tty/serial/8250/8250_fintek.c (revision 28e3fb6c4dce76d59a76755c4360d1cd5e0e226c)
1*28e3fb6cSRicardo Ribalda Delgado /*
2*28e3fb6cSRicardo Ribalda Delgado  *  Probe for F81216A LPC to 4 UART
3*28e3fb6cSRicardo Ribalda Delgado  *
4*28e3fb6cSRicardo Ribalda Delgado  *  Based on drivers/tty/serial/8250_pnp.c, by Russell King, et al
5*28e3fb6cSRicardo Ribalda Delgado  *
6*28e3fb6cSRicardo Ribalda Delgado  *  Copyright (C) 2014 Ricardo Ribalda, Qtechnology A/S
7*28e3fb6cSRicardo Ribalda Delgado  *
8*28e3fb6cSRicardo Ribalda Delgado  *
9*28e3fb6cSRicardo Ribalda Delgado  * This program is free software; you can redistribute it and/or modify
10*28e3fb6cSRicardo Ribalda Delgado  * it under the terms of the GNU General Public License as published by
11*28e3fb6cSRicardo Ribalda Delgado  * the Free Software Foundation; either version 2 of the License.
12*28e3fb6cSRicardo Ribalda Delgado  */
13*28e3fb6cSRicardo Ribalda Delgado #include <linux/module.h>
14*28e3fb6cSRicardo Ribalda Delgado #include <linux/pci.h>
15*28e3fb6cSRicardo Ribalda Delgado #include <linux/pnp.h>
16*28e3fb6cSRicardo Ribalda Delgado #include <linux/kernel.h>
17*28e3fb6cSRicardo Ribalda Delgado #include <linux/serial_core.h>
18*28e3fb6cSRicardo Ribalda Delgado #include  "8250.h"
19*28e3fb6cSRicardo Ribalda Delgado 
20*28e3fb6cSRicardo Ribalda Delgado #define ADDR_PORT 0x4E
21*28e3fb6cSRicardo Ribalda Delgado #define DATA_PORT 0x4F
22*28e3fb6cSRicardo Ribalda Delgado #define ENTRY_KEY 0x77
23*28e3fb6cSRicardo Ribalda Delgado #define EXIT_KEY 0xAA
24*28e3fb6cSRicardo Ribalda Delgado #define CHIP_ID1  0x20
25*28e3fb6cSRicardo Ribalda Delgado #define CHIP_ID1_VAL 0x02
26*28e3fb6cSRicardo Ribalda Delgado #define CHIP_ID2  0x21
27*28e3fb6cSRicardo Ribalda Delgado #define CHIP_ID2_VAL 0x16
28*28e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID1 0x23
29*28e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID1_VAL 0x19
30*28e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID2 0x24
31*28e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID2_VAL 0x34
32*28e3fb6cSRicardo Ribalda Delgado #define LDN 0x7
33*28e3fb6cSRicardo Ribalda Delgado 
34*28e3fb6cSRicardo Ribalda Delgado #define RS485  0xF0
35*28e3fb6cSRicardo Ribalda Delgado #define RTS_INVERT BIT(5)
36*28e3fb6cSRicardo Ribalda Delgado #define RS485_URA BIT(4)
37*28e3fb6cSRicardo Ribalda Delgado #define RXW4C_IRA BIT(3)
38*28e3fb6cSRicardo Ribalda Delgado #define TXW4C_IRA BIT(2)
39*28e3fb6cSRicardo Ribalda Delgado 
40*28e3fb6cSRicardo Ribalda Delgado #define DRIVER_NAME "8250_fintek"
41*28e3fb6cSRicardo Ribalda Delgado 
42*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_enter_key(void){
43*28e3fb6cSRicardo Ribalda Delgado 
44*28e3fb6cSRicardo Ribalda Delgado 	if (!request_muxed_region(ADDR_PORT, 2, DRIVER_NAME))
45*28e3fb6cSRicardo Ribalda Delgado 		return -EBUSY;
46*28e3fb6cSRicardo Ribalda Delgado 
47*28e3fb6cSRicardo Ribalda Delgado 	outb(ENTRY_KEY, ADDR_PORT);
48*28e3fb6cSRicardo Ribalda Delgado 	outb(ENTRY_KEY, ADDR_PORT);
49*28e3fb6cSRicardo Ribalda Delgado 	return 0;
50*28e3fb6cSRicardo Ribalda Delgado }
51*28e3fb6cSRicardo Ribalda Delgado 
52*28e3fb6cSRicardo Ribalda Delgado static void fintek_8250_exit_key(void){
53*28e3fb6cSRicardo Ribalda Delgado 
54*28e3fb6cSRicardo Ribalda Delgado 	outb(EXIT_KEY, ADDR_PORT);
55*28e3fb6cSRicardo Ribalda Delgado 	release_region(ADDR_PORT, 2);
56*28e3fb6cSRicardo Ribalda Delgado }
57*28e3fb6cSRicardo Ribalda Delgado 
58*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_get_index(resource_size_t base_addr)
59*28e3fb6cSRicardo Ribalda Delgado {
60*28e3fb6cSRicardo Ribalda Delgado 	resource_size_t base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
61*28e3fb6cSRicardo Ribalda Delgado 	int i;
62*28e3fb6cSRicardo Ribalda Delgado 
63*28e3fb6cSRicardo Ribalda Delgado 	for (i = 0; i < ARRAY_SIZE(base); i++)
64*28e3fb6cSRicardo Ribalda Delgado 		if (base_addr == base[i])
65*28e3fb6cSRicardo Ribalda Delgado 			return i;
66*28e3fb6cSRicardo Ribalda Delgado 
67*28e3fb6cSRicardo Ribalda Delgado 	return -ENODEV;
68*28e3fb6cSRicardo Ribalda Delgado }
69*28e3fb6cSRicardo Ribalda Delgado 
70*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_check_id(void)
71*28e3fb6cSRicardo Ribalda Delgado {
72*28e3fb6cSRicardo Ribalda Delgado 
73*28e3fb6cSRicardo Ribalda Delgado 	outb(CHIP_ID1, ADDR_PORT);
74*28e3fb6cSRicardo Ribalda Delgado 	if (inb(DATA_PORT) != CHIP_ID1_VAL)
75*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
76*28e3fb6cSRicardo Ribalda Delgado 
77*28e3fb6cSRicardo Ribalda Delgado 	outb(CHIP_ID2, ADDR_PORT);
78*28e3fb6cSRicardo Ribalda Delgado 	if (inb(DATA_PORT) != CHIP_ID2_VAL)
79*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
80*28e3fb6cSRicardo Ribalda Delgado 
81*28e3fb6cSRicardo Ribalda Delgado 	outb(VENDOR_ID1, ADDR_PORT);
82*28e3fb6cSRicardo Ribalda Delgado 	if (inb(DATA_PORT) != VENDOR_ID1_VAL)
83*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
84*28e3fb6cSRicardo Ribalda Delgado 
85*28e3fb6cSRicardo Ribalda Delgado 	outb(VENDOR_ID2, ADDR_PORT);
86*28e3fb6cSRicardo Ribalda Delgado 	if (inb(DATA_PORT) != VENDOR_ID2_VAL)
87*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
88*28e3fb6cSRicardo Ribalda Delgado 
89*28e3fb6cSRicardo Ribalda Delgado 	return 0;
90*28e3fb6cSRicardo Ribalda Delgado }
91*28e3fb6cSRicardo Ribalda Delgado 
92*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_rs4850_config(struct uart_8250_port *uart,
93*28e3fb6cSRicardo Ribalda Delgado 			      struct serial_rs485 *rs485)
94*28e3fb6cSRicardo Ribalda Delgado {
95*28e3fb6cSRicardo Ribalda Delgado 	uint8_t config = 0;
96*28e3fb6cSRicardo Ribalda Delgado 	int index = fintek_8250_get_index(uart->port.iobase);
97*28e3fb6cSRicardo Ribalda Delgado 
98*28e3fb6cSRicardo Ribalda Delgado 	if (index < 0)
99*28e3fb6cSRicardo Ribalda Delgado 		return -EINVAL;
100*28e3fb6cSRicardo Ribalda Delgado 
101*28e3fb6cSRicardo Ribalda Delgado 	if (rs485->flags & SER_RS485_ENABLED)
102*28e3fb6cSRicardo Ribalda Delgado 		memset(rs485->padding, 0, sizeof(rs485->padding));
103*28e3fb6cSRicardo Ribalda Delgado 	else
104*28e3fb6cSRicardo Ribalda Delgado 		memset(rs485, 0, sizeof(*rs485));
105*28e3fb6cSRicardo Ribalda Delgado 
106*28e3fb6cSRicardo Ribalda Delgado 	rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
107*28e3fb6cSRicardo Ribalda Delgado 			SER_RS485_RTS_AFTER_SEND;
108*28e3fb6cSRicardo Ribalda Delgado 
109*28e3fb6cSRicardo Ribalda Delgado 	if (rs485->delay_rts_before_send) {
110*28e3fb6cSRicardo Ribalda Delgado 		rs485->delay_rts_before_send = 1;
111*28e3fb6cSRicardo Ribalda Delgado 		config |= TXW4C_IRA;
112*28e3fb6cSRicardo Ribalda Delgado 	}
113*28e3fb6cSRicardo Ribalda Delgado 
114*28e3fb6cSRicardo Ribalda Delgado 	if (rs485->delay_rts_after_send) {
115*28e3fb6cSRicardo Ribalda Delgado 		rs485->delay_rts_after_send = 1;
116*28e3fb6cSRicardo Ribalda Delgado 		config |= RXW4C_IRA;
117*28e3fb6cSRicardo Ribalda Delgado 	}
118*28e3fb6cSRicardo Ribalda Delgado 
119*28e3fb6cSRicardo Ribalda Delgado 	if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) ==
120*28e3fb6cSRicardo Ribalda Delgado 			(!!(rs485->flags & SER_RS485_RTS_AFTER_SEND)))
121*28e3fb6cSRicardo Ribalda Delgado 		rs485->flags &= SER_RS485_ENABLED;
122*28e3fb6cSRicardo Ribalda Delgado 	else
123*28e3fb6cSRicardo Ribalda Delgado 		config |= RS485_URA;
124*28e3fb6cSRicardo Ribalda Delgado 
125*28e3fb6cSRicardo Ribalda Delgado 	if (rs485->flags & SER_RS485_RTS_ON_SEND)
126*28e3fb6cSRicardo Ribalda Delgado 		config |= RTS_INVERT;
127*28e3fb6cSRicardo Ribalda Delgado 
128*28e3fb6cSRicardo Ribalda Delgado 	if (fintek_8250_enter_key())
129*28e3fb6cSRicardo Ribalda Delgado 		return -EBUSY;
130*28e3fb6cSRicardo Ribalda Delgado 
131*28e3fb6cSRicardo Ribalda Delgado 	outb(LDN, ADDR_PORT);
132*28e3fb6cSRicardo Ribalda Delgado 	outb(index, DATA_PORT);
133*28e3fb6cSRicardo Ribalda Delgado 	outb(RS485, ADDR_PORT);
134*28e3fb6cSRicardo Ribalda Delgado 	outb(config, DATA_PORT);
135*28e3fb6cSRicardo Ribalda Delgado 	fintek_8250_exit_key();
136*28e3fb6cSRicardo Ribalda Delgado 
137*28e3fb6cSRicardo Ribalda Delgado 	return 0;
138*28e3fb6cSRicardo Ribalda Delgado }
139*28e3fb6cSRicardo Ribalda Delgado 
140*28e3fb6cSRicardo Ribalda Delgado static int
141*28e3fb6cSRicardo Ribalda Delgado fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
142*28e3fb6cSRicardo Ribalda Delgado {
143*28e3fb6cSRicardo Ribalda Delgado 	int line;
144*28e3fb6cSRicardo Ribalda Delgado 	struct uart_8250_port uart;
145*28e3fb6cSRicardo Ribalda Delgado 	int ret;
146*28e3fb6cSRicardo Ribalda Delgado 
147*28e3fb6cSRicardo Ribalda Delgado 	if (!pnp_port_valid(dev, 0))
148*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
149*28e3fb6cSRicardo Ribalda Delgado 
150*28e3fb6cSRicardo Ribalda Delgado 	if (fintek_8250_get_index(pnp_port_start(dev, 0)) < 0)
151*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
152*28e3fb6cSRicardo Ribalda Delgado 
153*28e3fb6cSRicardo Ribalda Delgado 	/* Enable configuration registers*/
154*28e3fb6cSRicardo Ribalda Delgado 	if (fintek_8250_enter_key())
155*28e3fb6cSRicardo Ribalda Delgado 		return -EBUSY;
156*28e3fb6cSRicardo Ribalda Delgado 
157*28e3fb6cSRicardo Ribalda Delgado 	/*Check ID*/
158*28e3fb6cSRicardo Ribalda Delgado 	ret = fintek_8250_check_id();
159*28e3fb6cSRicardo Ribalda Delgado 	fintek_8250_exit_key();
160*28e3fb6cSRicardo Ribalda Delgado 	if (ret)
161*28e3fb6cSRicardo Ribalda Delgado 		return ret;
162*28e3fb6cSRicardo Ribalda Delgado 
163*28e3fb6cSRicardo Ribalda Delgado 	memset(&uart, 0, sizeof(uart));
164*28e3fb6cSRicardo Ribalda Delgado 	if (!pnp_irq_valid(dev, 0))
165*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
166*28e3fb6cSRicardo Ribalda Delgado 	uart.port.irq = pnp_irq(dev, 0);
167*28e3fb6cSRicardo Ribalda Delgado 	uart.port.iobase = pnp_port_start(dev, 0);
168*28e3fb6cSRicardo Ribalda Delgado 	uart.port.iotype = UPIO_PORT;
169*28e3fb6cSRicardo Ribalda Delgado 	uart.rs485_config = fintek_8250_rs4850_config;
170*28e3fb6cSRicardo Ribalda Delgado 
171*28e3fb6cSRicardo Ribalda Delgado 	uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
172*28e3fb6cSRicardo Ribalda Delgado 	if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
173*28e3fb6cSRicardo Ribalda Delgado 		uart.port.flags |= UPF_SHARE_IRQ;
174*28e3fb6cSRicardo Ribalda Delgado 	uart.port.uartclk = 1843200;
175*28e3fb6cSRicardo Ribalda Delgado 	uart.port.dev = &dev->dev;
176*28e3fb6cSRicardo Ribalda Delgado 
177*28e3fb6cSRicardo Ribalda Delgado 	line = serial8250_register_8250_port(&uart);
178*28e3fb6cSRicardo Ribalda Delgado 	if (line < 0)
179*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
180*28e3fb6cSRicardo Ribalda Delgado 
181*28e3fb6cSRicardo Ribalda Delgado 	pnp_set_drvdata(dev, (void *)((long)line + 1));
182*28e3fb6cSRicardo Ribalda Delgado 	return 0;
183*28e3fb6cSRicardo Ribalda Delgado }
184*28e3fb6cSRicardo Ribalda Delgado 
185*28e3fb6cSRicardo Ribalda Delgado static void fintek_8250_remove(struct pnp_dev *dev)
186*28e3fb6cSRicardo Ribalda Delgado {
187*28e3fb6cSRicardo Ribalda Delgado 	long line = (long)pnp_get_drvdata(dev);
188*28e3fb6cSRicardo Ribalda Delgado 
189*28e3fb6cSRicardo Ribalda Delgado 	if (line)
190*28e3fb6cSRicardo Ribalda Delgado 		serial8250_unregister_port(line - 1);
191*28e3fb6cSRicardo Ribalda Delgado }
192*28e3fb6cSRicardo Ribalda Delgado 
193*28e3fb6cSRicardo Ribalda Delgado #ifdef CONFIG_PM
194*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state)
195*28e3fb6cSRicardo Ribalda Delgado {
196*28e3fb6cSRicardo Ribalda Delgado 	long line = (long)pnp_get_drvdata(dev);
197*28e3fb6cSRicardo Ribalda Delgado 
198*28e3fb6cSRicardo Ribalda Delgado 	if (!line)
199*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
200*28e3fb6cSRicardo Ribalda Delgado 	serial8250_suspend_port(line - 1);
201*28e3fb6cSRicardo Ribalda Delgado 	return 0;
202*28e3fb6cSRicardo Ribalda Delgado }
203*28e3fb6cSRicardo Ribalda Delgado 
204*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_resume(struct pnp_dev *dev)
205*28e3fb6cSRicardo Ribalda Delgado {
206*28e3fb6cSRicardo Ribalda Delgado 	long line = (long)pnp_get_drvdata(dev);
207*28e3fb6cSRicardo Ribalda Delgado 
208*28e3fb6cSRicardo Ribalda Delgado 	if (!line)
209*28e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
210*28e3fb6cSRicardo Ribalda Delgado 	serial8250_resume_port(line - 1);
211*28e3fb6cSRicardo Ribalda Delgado 	return 0;
212*28e3fb6cSRicardo Ribalda Delgado }
213*28e3fb6cSRicardo Ribalda Delgado #else
214*28e3fb6cSRicardo Ribalda Delgado #define fintek_8250_suspend NULL
215*28e3fb6cSRicardo Ribalda Delgado #define fintek_8250_resume NULL
216*28e3fb6cSRicardo Ribalda Delgado #endif /* CONFIG_PM */
217*28e3fb6cSRicardo Ribalda Delgado 
218*28e3fb6cSRicardo Ribalda Delgado static const struct pnp_device_id fintek_dev_table[] = {
219*28e3fb6cSRicardo Ribalda Delgado 	/* Qtechnology Panel PC / IO1000 */
220*28e3fb6cSRicardo Ribalda Delgado 	{ "PNP0501"},
221*28e3fb6cSRicardo Ribalda Delgado 	{}
222*28e3fb6cSRicardo Ribalda Delgado };
223*28e3fb6cSRicardo Ribalda Delgado 
224*28e3fb6cSRicardo Ribalda Delgado MODULE_DEVICE_TABLE(pnp, fintek_dev_table);
225*28e3fb6cSRicardo Ribalda Delgado 
226*28e3fb6cSRicardo Ribalda Delgado static struct pnp_driver fintek_8250_driver = {
227*28e3fb6cSRicardo Ribalda Delgado 	.name		= DRIVER_NAME,
228*28e3fb6cSRicardo Ribalda Delgado 	.probe		= fintek_8250_probe,
229*28e3fb6cSRicardo Ribalda Delgado 	.remove		= fintek_8250_remove,
230*28e3fb6cSRicardo Ribalda Delgado 	.suspend	= fintek_8250_suspend,
231*28e3fb6cSRicardo Ribalda Delgado 	.resume		= fintek_8250_resume,
232*28e3fb6cSRicardo Ribalda Delgado 	.id_table	= fintek_dev_table,
233*28e3fb6cSRicardo Ribalda Delgado };
234*28e3fb6cSRicardo Ribalda Delgado 
235*28e3fb6cSRicardo Ribalda Delgado static int fintek_8250_init(void)
236*28e3fb6cSRicardo Ribalda Delgado {
237*28e3fb6cSRicardo Ribalda Delgado 	return pnp_register_driver(&fintek_8250_driver);
238*28e3fb6cSRicardo Ribalda Delgado }
239*28e3fb6cSRicardo Ribalda Delgado module_init(fintek_8250_init);
240*28e3fb6cSRicardo Ribalda Delgado 
241*28e3fb6cSRicardo Ribalda Delgado static void fintek_8250_exit(void)
242*28e3fb6cSRicardo Ribalda Delgado {
243*28e3fb6cSRicardo Ribalda Delgado 	pnp_unregister_driver(&fintek_8250_driver);
244*28e3fb6cSRicardo Ribalda Delgado }
245*28e3fb6cSRicardo Ribalda Delgado module_exit(fintek_8250_exit);
246*28e3fb6cSRicardo Ribalda Delgado 
247*28e3fb6cSRicardo Ribalda Delgado MODULE_DESCRIPTION("Fintek F812164 module");
248*28e3fb6cSRicardo Ribalda Delgado MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
249*28e3fb6cSRicardo Ribalda Delgado MODULE_LICENSE("GPL");
250