xref: /openbmc/linux/drivers/tty/serial/8250/8250_fintek.c (revision fa01e2ca9f531b4a5693469a196eb1574b8d7d8a)
128e3fb6cSRicardo Ribalda Delgado /*
228e3fb6cSRicardo Ribalda Delgado  *  Probe for F81216A LPC to 4 UART
328e3fb6cSRicardo Ribalda Delgado  *
4*fa01e2caSRicardo Ribalda Delgado  *  Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S
528e3fb6cSRicardo Ribalda Delgado  *
628e3fb6cSRicardo Ribalda Delgado  *
728e3fb6cSRicardo Ribalda Delgado  * This program is free software; you can redistribute it and/or modify
828e3fb6cSRicardo Ribalda Delgado  * it under the terms of the GNU General Public License as published by
928e3fb6cSRicardo Ribalda Delgado  * the Free Software Foundation; either version 2 of the License.
1028e3fb6cSRicardo Ribalda Delgado  */
1128e3fb6cSRicardo Ribalda Delgado #include <linux/module.h>
1228e3fb6cSRicardo Ribalda Delgado #include <linux/pci.h>
1328e3fb6cSRicardo Ribalda Delgado #include <linux/pnp.h>
1428e3fb6cSRicardo Ribalda Delgado #include <linux/kernel.h>
1528e3fb6cSRicardo Ribalda Delgado #include <linux/serial_core.h>
1628e3fb6cSRicardo Ribalda Delgado #include  "8250.h"
1728e3fb6cSRicardo Ribalda Delgado 
18017bec38SRicardo Ribalda Delgado #define ADDR_PORT 0
19017bec38SRicardo Ribalda Delgado #define DATA_PORT 1
2028e3fb6cSRicardo Ribalda Delgado #define EXIT_KEY 0xAA
2128e3fb6cSRicardo Ribalda Delgado #define CHIP_ID1  0x20
2228e3fb6cSRicardo Ribalda Delgado #define CHIP_ID2  0x21
23dae77f75SRicardo Ribalda Delgado #define CHIP_ID_0 0x1602
24dae77f75SRicardo Ribalda Delgado #define CHIP_ID_1 0x0501
2528e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID1 0x23
2628e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID1_VAL 0x19
2728e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID2 0x24
2828e3fb6cSRicardo Ribalda Delgado #define VENDOR_ID2_VAL 0x34
2929d58642SRicardo Ribalda Delgado #define IO_ADDR1 0x61
3029d58642SRicardo Ribalda Delgado #define IO_ADDR2 0x60
3128e3fb6cSRicardo Ribalda Delgado #define LDN 0x7
3228e3fb6cSRicardo Ribalda Delgado 
3328e3fb6cSRicardo Ribalda Delgado #define RS485  0xF0
3428e3fb6cSRicardo Ribalda Delgado #define RTS_INVERT BIT(5)
3528e3fb6cSRicardo Ribalda Delgado #define RS485_URA BIT(4)
3628e3fb6cSRicardo Ribalda Delgado #define RXW4C_IRA BIT(3)
3728e3fb6cSRicardo Ribalda Delgado #define TXW4C_IRA BIT(2)
3828e3fb6cSRicardo Ribalda Delgado 
3992a5f11aSRicardo Ribalda Delgado struct fintek_8250 {
40017bec38SRicardo Ribalda Delgado 	u16 base_port;
4192a5f11aSRicardo Ribalda Delgado 	u8 index;
42ce8c267eSRicardo Ribalda Delgado 	u8 key;
4392a5f11aSRicardo Ribalda Delgado };
4492a5f11aSRicardo Ribalda Delgado 
45ce8c267eSRicardo Ribalda Delgado static int fintek_8250_enter_key(u16 base_port, u8 key)
46017bec38SRicardo Ribalda Delgado {
47*fa01e2caSRicardo Ribalda Delgado 	if (!request_muxed_region(base_port, 2, "8250_fintek"))
4828e3fb6cSRicardo Ribalda Delgado 		return -EBUSY;
4928e3fb6cSRicardo Ribalda Delgado 
50ce8c267eSRicardo Ribalda Delgado 	outb(key, base_port + ADDR_PORT);
51ce8c267eSRicardo Ribalda Delgado 	outb(key, base_port + ADDR_PORT);
5228e3fb6cSRicardo Ribalda Delgado 	return 0;
5328e3fb6cSRicardo Ribalda Delgado }
5428e3fb6cSRicardo Ribalda Delgado 
55017bec38SRicardo Ribalda Delgado static void fintek_8250_exit_key(u16 base_port)
56017bec38SRicardo Ribalda Delgado {
5728e3fb6cSRicardo Ribalda Delgado 
58017bec38SRicardo Ribalda Delgado 	outb(EXIT_KEY, base_port + ADDR_PORT);
59017bec38SRicardo Ribalda Delgado 	release_region(base_port + ADDR_PORT, 2);
6028e3fb6cSRicardo Ribalda Delgado }
6128e3fb6cSRicardo Ribalda Delgado 
62017bec38SRicardo Ribalda Delgado static int fintek_8250_check_id(u16 base_port)
6328e3fb6cSRicardo Ribalda Delgado {
64dae77f75SRicardo Ribalda Delgado 	u16 chip;
6528e3fb6cSRicardo Ribalda Delgado 
66017bec38SRicardo Ribalda Delgado 	outb(VENDOR_ID1, base_port + ADDR_PORT);
67017bec38SRicardo Ribalda Delgado 	if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL)
6828e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
6928e3fb6cSRicardo Ribalda Delgado 
70017bec38SRicardo Ribalda Delgado 	outb(VENDOR_ID2, base_port + ADDR_PORT);
71017bec38SRicardo Ribalda Delgado 	if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL)
7228e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
7328e3fb6cSRicardo Ribalda Delgado 
74dae77f75SRicardo Ribalda Delgado 	outb(CHIP_ID1, base_port + ADDR_PORT);
75dae77f75SRicardo Ribalda Delgado 	chip = inb(base_port + DATA_PORT);
76dae77f75SRicardo Ribalda Delgado 	outb(CHIP_ID2, base_port + ADDR_PORT);
77dae77f75SRicardo Ribalda Delgado 	chip |= inb(base_port + DATA_PORT) << 8;
78dae77f75SRicardo Ribalda Delgado 
79dae77f75SRicardo Ribalda Delgado 	if (chip != CHIP_ID_0 && chip != CHIP_ID_1)
80dae77f75SRicardo Ribalda Delgado 		return -ENODEV;
81dae77f75SRicardo Ribalda Delgado 
8228e3fb6cSRicardo Ribalda Delgado 	return 0;
8328e3fb6cSRicardo Ribalda Delgado }
8428e3fb6cSRicardo Ribalda Delgado 
8541e69093SRicardo Ribalda Delgado static int fintek_8250_rs485_config(struct uart_port *port,
8628e3fb6cSRicardo Ribalda Delgado 			      struct serial_rs485 *rs485)
8728e3fb6cSRicardo Ribalda Delgado {
8828e3fb6cSRicardo Ribalda Delgado 	uint8_t config = 0;
8992a5f11aSRicardo Ribalda Delgado 	struct fintek_8250 *pdata = port->private_data;
9028e3fb6cSRicardo Ribalda Delgado 
9192a5f11aSRicardo Ribalda Delgado 	if (!pdata)
9228e3fb6cSRicardo Ribalda Delgado 		return -EINVAL;
9328e3fb6cSRicardo Ribalda Delgado 
9428e3fb6cSRicardo Ribalda Delgado 	if (rs485->flags & SER_RS485_ENABLED)
9528e3fb6cSRicardo Ribalda Delgado 		memset(rs485->padding, 0, sizeof(rs485->padding));
9628e3fb6cSRicardo Ribalda Delgado 	else
9728e3fb6cSRicardo Ribalda Delgado 		memset(rs485, 0, sizeof(*rs485));
9828e3fb6cSRicardo Ribalda Delgado 
9928e3fb6cSRicardo Ribalda Delgado 	rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
10028e3fb6cSRicardo Ribalda Delgado 			SER_RS485_RTS_AFTER_SEND;
10128e3fb6cSRicardo Ribalda Delgado 
10228e3fb6cSRicardo Ribalda Delgado 	if (rs485->delay_rts_before_send) {
10328e3fb6cSRicardo Ribalda Delgado 		rs485->delay_rts_before_send = 1;
10428e3fb6cSRicardo Ribalda Delgado 		config |= TXW4C_IRA;
10528e3fb6cSRicardo Ribalda Delgado 	}
10628e3fb6cSRicardo Ribalda Delgado 
10728e3fb6cSRicardo Ribalda Delgado 	if (rs485->delay_rts_after_send) {
10828e3fb6cSRicardo Ribalda Delgado 		rs485->delay_rts_after_send = 1;
10928e3fb6cSRicardo Ribalda Delgado 		config |= RXW4C_IRA;
11028e3fb6cSRicardo Ribalda Delgado 	}
11128e3fb6cSRicardo Ribalda Delgado 
11228e3fb6cSRicardo Ribalda Delgado 	if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) ==
11328e3fb6cSRicardo Ribalda Delgado 			(!!(rs485->flags & SER_RS485_RTS_AFTER_SEND)))
11428e3fb6cSRicardo Ribalda Delgado 		rs485->flags &= SER_RS485_ENABLED;
11528e3fb6cSRicardo Ribalda Delgado 	else
11628e3fb6cSRicardo Ribalda Delgado 		config |= RS485_URA;
11728e3fb6cSRicardo Ribalda Delgado 
11828e3fb6cSRicardo Ribalda Delgado 	if (rs485->flags & SER_RS485_RTS_ON_SEND)
11928e3fb6cSRicardo Ribalda Delgado 		config |= RTS_INVERT;
12028e3fb6cSRicardo Ribalda Delgado 
121ce8c267eSRicardo Ribalda Delgado 	if (fintek_8250_enter_key(pdata->base_port, pdata->key))
12228e3fb6cSRicardo Ribalda Delgado 		return -EBUSY;
12328e3fb6cSRicardo Ribalda Delgado 
124017bec38SRicardo Ribalda Delgado 	outb(LDN, pdata->base_port + ADDR_PORT);
125017bec38SRicardo Ribalda Delgado 	outb(pdata->index, pdata->base_port + DATA_PORT);
126017bec38SRicardo Ribalda Delgado 	outb(RS485, pdata->base_port + ADDR_PORT);
127017bec38SRicardo Ribalda Delgado 	outb(config, pdata->base_port + DATA_PORT);
128017bec38SRicardo Ribalda Delgado 	fintek_8250_exit_key(pdata->base_port);
12928e3fb6cSRicardo Ribalda Delgado 
13041e69093SRicardo Ribalda Delgado 	port->rs485 = *rs485;
13141e69093SRicardo Ribalda Delgado 
13228e3fb6cSRicardo Ribalda Delgado 	return 0;
13328e3fb6cSRicardo Ribalda Delgado }
13428e3fb6cSRicardo Ribalda Delgado 
135*fa01e2caSRicardo Ribalda Delgado static int find_base_port(struct fintek_8250 *pdata, u16 io_address)
136017bec38SRicardo Ribalda Delgado {
137017bec38SRicardo Ribalda Delgado 	static const u16 addr[] = {0x4e, 0x2e};
138ce8c267eSRicardo Ribalda Delgado 	static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67};
13929d58642SRicardo Ribalda Delgado 	int i, j, k;
140017bec38SRicardo Ribalda Delgado 
141017bec38SRicardo Ribalda Delgado 	for (i = 0; i < ARRAY_SIZE(addr); i++) {
142ce8c267eSRicardo Ribalda Delgado 		for (j = 0; j < ARRAY_SIZE(keys); j++) {
143017bec38SRicardo Ribalda Delgado 
144ce8c267eSRicardo Ribalda Delgado 			if (fintek_8250_enter_key(addr[i], keys[j]))
145017bec38SRicardo Ribalda Delgado 				continue;
14629d58642SRicardo Ribalda Delgado 			if (fintek_8250_check_id(addr[i])) {
147017bec38SRicardo Ribalda Delgado 				fintek_8250_exit_key(addr[i]);
14829d58642SRicardo Ribalda Delgado 				continue;
14929d58642SRicardo Ribalda Delgado 			}
15029d58642SRicardo Ribalda Delgado 
15129d58642SRicardo Ribalda Delgado 			for (k = 0; k < 4; k++) {
15229d58642SRicardo Ribalda Delgado 				u16 aux;
15329d58642SRicardo Ribalda Delgado 
15429d58642SRicardo Ribalda Delgado 				outb(LDN, addr[i] + ADDR_PORT);
15529d58642SRicardo Ribalda Delgado 				outb(k, addr[i] + DATA_PORT);
15629d58642SRicardo Ribalda Delgado 
15729d58642SRicardo Ribalda Delgado 				outb(IO_ADDR1, addr[i] + ADDR_PORT);
15829d58642SRicardo Ribalda Delgado 				aux = inb(addr[i] + DATA_PORT);
15929d58642SRicardo Ribalda Delgado 				outb(IO_ADDR2, addr[i] + ADDR_PORT);
16029d58642SRicardo Ribalda Delgado 				aux |= inb(addr[i] + DATA_PORT) << 8;
16129d58642SRicardo Ribalda Delgado 				if (aux != io_address)
16229d58642SRicardo Ribalda Delgado 					continue;
16329d58642SRicardo Ribalda Delgado 
16429d58642SRicardo Ribalda Delgado 				fintek_8250_exit_key(addr[i]);
165*fa01e2caSRicardo Ribalda Delgado 				pdata->key = keys[j];
166*fa01e2caSRicardo Ribalda Delgado 				pdata->base_port = addr[i];
167*fa01e2caSRicardo Ribalda Delgado 				pdata->index = k;
168*fa01e2caSRicardo Ribalda Delgado 
169*fa01e2caSRicardo Ribalda Delgado 				return 0;
170017bec38SRicardo Ribalda Delgado 			}
171*fa01e2caSRicardo Ribalda Delgado 
17229d58642SRicardo Ribalda Delgado 			fintek_8250_exit_key(addr[i]);
173ce8c267eSRicardo Ribalda Delgado 		}
174ce8c267eSRicardo Ribalda Delgado 	}
175017bec38SRicardo Ribalda Delgado 
176017bec38SRicardo Ribalda Delgado 	return -ENODEV;
177017bec38SRicardo Ribalda Delgado }
178017bec38SRicardo Ribalda Delgado 
179*fa01e2caSRicardo Ribalda Delgado int fintek_8250_probe(struct uart_8250_port *uart)
18028e3fb6cSRicardo Ribalda Delgado {
18192a5f11aSRicardo Ribalda Delgado 	struct fintek_8250 *pdata;
182*fa01e2caSRicardo Ribalda Delgado 	struct fintek_8250 probe_data;
18328e3fb6cSRicardo Ribalda Delgado 
184*fa01e2caSRicardo Ribalda Delgado 	if (find_base_port(&probe_data, uart->port.iobase))
18528e3fb6cSRicardo Ribalda Delgado 		return -ENODEV;
18628e3fb6cSRicardo Ribalda Delgado 
187*fa01e2caSRicardo Ribalda Delgado 	pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL);
18892a5f11aSRicardo Ribalda Delgado 	if (!pdata)
18992a5f11aSRicardo Ribalda Delgado 		return -ENOMEM;
19092a5f11aSRicardo Ribalda Delgado 
191*fa01e2caSRicardo Ribalda Delgado 	memcpy(pdata, &probe_data, sizeof(probe_data));
192*fa01e2caSRicardo Ribalda Delgado 	uart->port.rs485_config = fintek_8250_rs485_config;
193*fa01e2caSRicardo Ribalda Delgado 	uart->port.private_data = pdata;
19428e3fb6cSRicardo Ribalda Delgado 
19528e3fb6cSRicardo Ribalda Delgado 	return 0;
19628e3fb6cSRicardo Ribalda Delgado }
197