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