]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/tty/serial/8250/8250_fintek.c
serial/8250_fintek: Use private data structure
[mirror_ubuntu-jammy-kernel.git] / drivers / tty / serial / 8250 / 8250_fintek.c
CommitLineData
28e3fb6c
RR
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
92a5f11a
RR
42struct fintek_8250 {
43 u8 index;
44 long line;
45};
46
28e3fb6c
RR
47static int fintek_8250_enter_key(void){
48
49 if (!request_muxed_region(ADDR_PORT, 2, DRIVER_NAME))
50 return -EBUSY;
51
52 outb(ENTRY_KEY, ADDR_PORT);
53 outb(ENTRY_KEY, ADDR_PORT);
54 return 0;
55}
56
57static void fintek_8250_exit_key(void){
58
59 outb(EXIT_KEY, ADDR_PORT);
60 release_region(ADDR_PORT, 2);
61}
62
63static int fintek_8250_get_index(resource_size_t base_addr)
64{
65 resource_size_t base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
66 int i;
67
68 for (i = 0; i < ARRAY_SIZE(base); i++)
69 if (base_addr == base[i])
70 return i;
71
72 return -ENODEV;
73}
74
75static int fintek_8250_check_id(void)
76{
77
78 outb(CHIP_ID1, ADDR_PORT);
79 if (inb(DATA_PORT) != CHIP_ID1_VAL)
80 return -ENODEV;
81
82 outb(CHIP_ID2, ADDR_PORT);
83 if (inb(DATA_PORT) != CHIP_ID2_VAL)
84 return -ENODEV;
85
86 outb(VENDOR_ID1, ADDR_PORT);
87 if (inb(DATA_PORT) != VENDOR_ID1_VAL)
88 return -ENODEV;
89
90 outb(VENDOR_ID2, ADDR_PORT);
91 if (inb(DATA_PORT) != VENDOR_ID2_VAL)
92 return -ENODEV;
93
94 return 0;
95}
96
41e69093 97static int fintek_8250_rs485_config(struct uart_port *port,
28e3fb6c
RR
98 struct serial_rs485 *rs485)
99{
100 uint8_t config = 0;
92a5f11a 101 struct fintek_8250 *pdata = port->private_data;
28e3fb6c 102
92a5f11a 103 if (!pdata)
28e3fb6c
RR
104 return -EINVAL;
105
106 if (rs485->flags & SER_RS485_ENABLED)
107 memset(rs485->padding, 0, sizeof(rs485->padding));
108 else
109 memset(rs485, 0, sizeof(*rs485));
110
111 rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
112 SER_RS485_RTS_AFTER_SEND;
113
114 if (rs485->delay_rts_before_send) {
115 rs485->delay_rts_before_send = 1;
116 config |= TXW4C_IRA;
117 }
118
119 if (rs485->delay_rts_after_send) {
120 rs485->delay_rts_after_send = 1;
121 config |= RXW4C_IRA;
122 }
123
124 if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) ==
125 (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND)))
126 rs485->flags &= SER_RS485_ENABLED;
127 else
128 config |= RS485_URA;
129
130 if (rs485->flags & SER_RS485_RTS_ON_SEND)
131 config |= RTS_INVERT;
132
133 if (fintek_8250_enter_key())
134 return -EBUSY;
135
136 outb(LDN, ADDR_PORT);
92a5f11a 137 outb(pdata->index, DATA_PORT);
28e3fb6c
RR
138 outb(RS485, ADDR_PORT);
139 outb(config, DATA_PORT);
140 fintek_8250_exit_key();
141
41e69093
RR
142 port->rs485 = *rs485;
143
28e3fb6c
RR
144 return 0;
145}
146
147static int
148fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
149{
28e3fb6c 150 struct uart_8250_port uart;
92a5f11a 151 struct fintek_8250 *pdata;
28e3fb6c 152 int ret;
92a5f11a 153 int index;
28e3fb6c
RR
154
155 if (!pnp_port_valid(dev, 0))
156 return -ENODEV;
157
92a5f11a
RR
158 index = fintek_8250_get_index(pnp_port_start(dev, 0));
159 if (index < 0)
28e3fb6c
RR
160 return -ENODEV;
161
162 /* Enable configuration registers*/
163 if (fintek_8250_enter_key())
164 return -EBUSY;
165
166 /*Check ID*/
167 ret = fintek_8250_check_id();
168 fintek_8250_exit_key();
169 if (ret)
170 return ret;
171
172 memset(&uart, 0, sizeof(uart));
92a5f11a
RR
173
174 pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
175 if (!pdata)
176 return -ENOMEM;
177 uart.port.private_data = pdata;
178
28e3fb6c
RR
179 if (!pnp_irq_valid(dev, 0))
180 return -ENODEV;
181 uart.port.irq = pnp_irq(dev, 0);
182 uart.port.iobase = pnp_port_start(dev, 0);
183 uart.port.iotype = UPIO_PORT;
41e69093 184 uart.port.rs485_config = fintek_8250_rs485_config;
28e3fb6c
RR
185
186 uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
187 if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
188 uart.port.flags |= UPF_SHARE_IRQ;
189 uart.port.uartclk = 1843200;
190 uart.port.dev = &dev->dev;
191
92a5f11a
RR
192 pdata->index = index;
193 pdata->line = serial8250_register_8250_port(&uart);
194 if (pdata->line < 0)
28e3fb6c
RR
195 return -ENODEV;
196
92a5f11a 197 pnp_set_drvdata(dev, pdata);
28e3fb6c
RR
198 return 0;
199}
200
201static void fintek_8250_remove(struct pnp_dev *dev)
202{
92a5f11a 203 struct fintek_8250 *pdata = pnp_get_drvdata(dev);
28e3fb6c 204
92a5f11a
RR
205 if (pdata)
206 serial8250_unregister_port(pdata->line);
28e3fb6c
RR
207}
208
209#ifdef CONFIG_PM
210static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state)
211{
92a5f11a 212 struct fintek_8250 *pdata = pnp_get_drvdata(dev);
28e3fb6c 213
92a5f11a 214 if (!pdata)
28e3fb6c 215 return -ENODEV;
92a5f11a 216 serial8250_suspend_port(pdata->line);
28e3fb6c
RR
217 return 0;
218}
219
220static int fintek_8250_resume(struct pnp_dev *dev)
221{
92a5f11a 222 struct fintek_8250 *pdata = pnp_get_drvdata(dev);
28e3fb6c 223
92a5f11a 224 if (!pdata)
28e3fb6c 225 return -ENODEV;
92a5f11a 226 serial8250_resume_port(pdata->line);
28e3fb6c
RR
227 return 0;
228}
229#else
230#define fintek_8250_suspend NULL
231#define fintek_8250_resume NULL
232#endif /* CONFIG_PM */
233
234static const struct pnp_device_id fintek_dev_table[] = {
235 /* Qtechnology Panel PC / IO1000 */
236 { "PNP0501"},
237 {}
238};
239
240MODULE_DEVICE_TABLE(pnp, fintek_dev_table);
241
242static struct pnp_driver fintek_8250_driver = {
243 .name = DRIVER_NAME,
244 .probe = fintek_8250_probe,
245 .remove = fintek_8250_remove,
246 .suspend = fintek_8250_suspend,
247 .resume = fintek_8250_resume,
248 .id_table = fintek_dev_table,
249};
250
aee94467 251module_pnp_driver(fintek_8250_driver);
28e3fb6c
RR
252MODULE_DESCRIPTION("Fintek F812164 module");
253MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
254MODULE_LICENSE("GPL");