1 /* 2 * Probe for F81216A LPC to 4 UART 3 * 4 * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License. 10 */ 11 #include <linux/module.h> 12 #include <linux/pci.h> 13 #include <linux/pnp.h> 14 #include <linux/kernel.h> 15 #include <linux/serial_core.h> 16 #include "8250.h" 17 18 #define ADDR_PORT 0 19 #define DATA_PORT 1 20 #define EXIT_KEY 0xAA 21 #define CHIP_ID1 0x20 22 #define CHIP_ID2 0x21 23 #define CHIP_ID_0 0x1602 24 #define CHIP_ID_1 0x0501 25 #define VENDOR_ID1 0x23 26 #define VENDOR_ID1_VAL 0x19 27 #define VENDOR_ID2 0x24 28 #define VENDOR_ID2_VAL 0x34 29 #define IO_ADDR1 0x61 30 #define IO_ADDR2 0x60 31 #define LDN 0x7 32 33 #define RS485 0xF0 34 #define RTS_INVERT BIT(5) 35 #define RS485_URA BIT(4) 36 #define RXW4C_IRA BIT(3) 37 #define TXW4C_IRA BIT(2) 38 39 struct fintek_8250 { 40 u16 base_port; 41 u8 index; 42 u8 key; 43 }; 44 45 static int fintek_8250_enter_key(u16 base_port, u8 key) 46 { 47 if (!request_muxed_region(base_port, 2, "8250_fintek")) 48 return -EBUSY; 49 50 outb(key, base_port + ADDR_PORT); 51 outb(key, base_port + ADDR_PORT); 52 return 0; 53 } 54 55 static void fintek_8250_exit_key(u16 base_port) 56 { 57 58 outb(EXIT_KEY, base_port + ADDR_PORT); 59 release_region(base_port + ADDR_PORT, 2); 60 } 61 62 static int fintek_8250_check_id(u16 base_port) 63 { 64 u16 chip; 65 66 outb(VENDOR_ID1, base_port + ADDR_PORT); 67 if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL) 68 return -ENODEV; 69 70 outb(VENDOR_ID2, base_port + ADDR_PORT); 71 if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL) 72 return -ENODEV; 73 74 outb(CHIP_ID1, base_port + ADDR_PORT); 75 chip = inb(base_port + DATA_PORT); 76 outb(CHIP_ID2, base_port + ADDR_PORT); 77 chip |= inb(base_port + DATA_PORT) << 8; 78 79 if (chip != CHIP_ID_0 && chip != CHIP_ID_1) 80 return -ENODEV; 81 82 return 0; 83 } 84 85 static int fintek_8250_rs485_config(struct uart_port *port, 86 struct serial_rs485 *rs485) 87 { 88 uint8_t config = 0; 89 struct fintek_8250 *pdata = port->private_data; 90 91 if (!pdata) 92 return -EINVAL; 93 94 if (rs485->flags & SER_RS485_ENABLED) 95 memset(rs485->padding, 0, sizeof(rs485->padding)); 96 else 97 memset(rs485, 0, sizeof(*rs485)); 98 99 rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | 100 SER_RS485_RTS_AFTER_SEND; 101 102 if (rs485->delay_rts_before_send) { 103 rs485->delay_rts_before_send = 1; 104 config |= TXW4C_IRA; 105 } 106 107 if (rs485->delay_rts_after_send) { 108 rs485->delay_rts_after_send = 1; 109 config |= RXW4C_IRA; 110 } 111 112 if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == 113 (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) 114 rs485->flags &= SER_RS485_ENABLED; 115 else 116 config |= RS485_URA; 117 118 if (rs485->flags & SER_RS485_RTS_ON_SEND) 119 config |= RTS_INVERT; 120 121 if (fintek_8250_enter_key(pdata->base_port, pdata->key)) 122 return -EBUSY; 123 124 outb(LDN, pdata->base_port + ADDR_PORT); 125 outb(pdata->index, pdata->base_port + DATA_PORT); 126 outb(RS485, pdata->base_port + ADDR_PORT); 127 outb(config, pdata->base_port + DATA_PORT); 128 fintek_8250_exit_key(pdata->base_port); 129 130 port->rs485 = *rs485; 131 132 return 0; 133 } 134 135 static int find_base_port(struct fintek_8250 *pdata, u16 io_address) 136 { 137 static const u16 addr[] = {0x4e, 0x2e}; 138 static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; 139 int i, j, k; 140 141 for (i = 0; i < ARRAY_SIZE(addr); i++) { 142 for (j = 0; j < ARRAY_SIZE(keys); j++) { 143 144 if (fintek_8250_enter_key(addr[i], keys[j])) 145 continue; 146 if (fintek_8250_check_id(addr[i])) { 147 fintek_8250_exit_key(addr[i]); 148 continue; 149 } 150 151 for (k = 0; k < 4; k++) { 152 u16 aux; 153 154 outb(LDN, addr[i] + ADDR_PORT); 155 outb(k, addr[i] + DATA_PORT); 156 157 outb(IO_ADDR1, addr[i] + ADDR_PORT); 158 aux = inb(addr[i] + DATA_PORT); 159 outb(IO_ADDR2, addr[i] + ADDR_PORT); 160 aux |= inb(addr[i] + DATA_PORT) << 8; 161 if (aux != io_address) 162 continue; 163 164 fintek_8250_exit_key(addr[i]); 165 pdata->key = keys[j]; 166 pdata->base_port = addr[i]; 167 pdata->index = k; 168 169 return 0; 170 } 171 172 fintek_8250_exit_key(addr[i]); 173 } 174 } 175 176 return -ENODEV; 177 } 178 179 int fintek_8250_probe(struct uart_8250_port *uart) 180 { 181 struct fintek_8250 *pdata; 182 struct fintek_8250 probe_data; 183 184 if (find_base_port(&probe_data, uart->port.iobase)) 185 return -ENODEV; 186 187 pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); 188 if (!pdata) 189 return -ENOMEM; 190 191 memcpy(pdata, &probe_data, sizeof(probe_data)); 192 uart->port.rs485_config = fintek_8250_rs485_config; 193 uart->port.private_data = pdata; 194 195 return 0; 196 } 197