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 static int fintek_8250_init(void) 238 { 239 return pnp_register_driver(&fintek_8250_driver); 240 } 241 module_init(fintek_8250_init); 242 243 static void fintek_8250_exit(void) 244 { 245 pnp_unregister_driver(&fintek_8250_driver); 246 } 247 module_exit(fintek_8250_exit); 248 249 MODULE_DESCRIPTION("Fintek F812164 module"); 250 MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>"); 251 MODULE_LICENSE("GPL"); 252