linux/drivers/tty/serial/8250/8250_fintek.c
<<
>>
Prefs
   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 0
  21#define DATA_PORT 1
  22#define EXIT_KEY 0xAA
  23#define CHIP_ID1  0x20
  24#define CHIP_ID2  0x21
  25#define CHIP_ID_0 0x1602
  26#define CHIP_ID_1 0x0501
  27#define VENDOR_ID1 0x23
  28#define VENDOR_ID1_VAL 0x19
  29#define VENDOR_ID2 0x24
  30#define VENDOR_ID2_VAL 0x34
  31#define IO_ADDR1 0x61
  32#define IO_ADDR2 0x60
  33#define LDN 0x7
  34
  35#define RS485  0xF0
  36#define RTS_INVERT BIT(5)
  37#define RS485_URA BIT(4)
  38#define RXW4C_IRA BIT(3)
  39#define TXW4C_IRA BIT(2)
  40
  41#define DRIVER_NAME "8250_fintek"
  42
  43struct fintek_8250 {
  44        u16 base_port;
  45        u8 index;
  46        u8 key;
  47        long line;
  48};
  49
  50static int fintek_8250_enter_key(u16 base_port, u8 key)
  51{
  52
  53        if (!request_muxed_region(base_port, 2, DRIVER_NAME))
  54                return -EBUSY;
  55
  56        outb(key, base_port + ADDR_PORT);
  57        outb(key, base_port + ADDR_PORT);
  58        return 0;
  59}
  60
  61static void fintek_8250_exit_key(u16 base_port)
  62{
  63
  64        outb(EXIT_KEY, base_port + ADDR_PORT);
  65        release_region(base_port + ADDR_PORT, 2);
  66}
  67
  68static int fintek_8250_check_id(u16 base_port)
  69{
  70        u16 chip;
  71
  72        outb(VENDOR_ID1, base_port + ADDR_PORT);
  73        if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL)
  74                return -ENODEV;
  75
  76        outb(VENDOR_ID2, base_port + ADDR_PORT);
  77        if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL)
  78                return -ENODEV;
  79
  80        outb(CHIP_ID1, base_port + ADDR_PORT);
  81        chip = inb(base_port + DATA_PORT);
  82        outb(CHIP_ID2, base_port + ADDR_PORT);
  83        chip |= inb(base_port + DATA_PORT) << 8;
  84
  85        if (chip != CHIP_ID_0 && chip != CHIP_ID_1)
  86                return -ENODEV;
  87
  88        return 0;
  89}
  90
  91static int fintek_8250_rs485_config(struct uart_port *port,
  92                              struct serial_rs485 *rs485)
  93{
  94        uint8_t config = 0;
  95        struct fintek_8250 *pdata = port->private_data;
  96
  97        if (!pdata)
  98                return -EINVAL;
  99
 100        if (rs485->flags & SER_RS485_ENABLED)
 101                memset(rs485->padding, 0, sizeof(rs485->padding));
 102        else
 103                memset(rs485, 0, sizeof(*rs485));
 104
 105        rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
 106                        SER_RS485_RTS_AFTER_SEND;
 107
 108        if (rs485->delay_rts_before_send) {
 109                rs485->delay_rts_before_send = 1;
 110                config |= TXW4C_IRA;
 111        }
 112
 113        if (rs485->delay_rts_after_send) {
 114                rs485->delay_rts_after_send = 1;
 115                config |= RXW4C_IRA;
 116        }
 117
 118        if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) ==
 119                        (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND)))
 120                rs485->flags &= SER_RS485_ENABLED;
 121        else
 122                config |= RS485_URA;
 123
 124        if (rs485->flags & SER_RS485_RTS_ON_SEND)
 125                config |= RTS_INVERT;
 126
 127        if (fintek_8250_enter_key(pdata->base_port, pdata->key))
 128                return -EBUSY;
 129
 130        outb(LDN, pdata->base_port + ADDR_PORT);
 131        outb(pdata->index, pdata->base_port + DATA_PORT);
 132        outb(RS485, pdata->base_port + ADDR_PORT);
 133        outb(config, pdata->base_port + DATA_PORT);
 134        fintek_8250_exit_key(pdata->base_port);
 135
 136        port->rs485 = *rs485;
 137
 138        return 0;
 139}
 140
 141static int fintek_8250_base_port(u16 io_address, u8 *key, u8 *index)
 142{
 143        static const u16 addr[] = {0x4e, 0x2e};
 144        static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67};
 145        int i, j, k;
 146
 147        for (i = 0; i < ARRAY_SIZE(addr); i++) {
 148                for (j = 0; j < ARRAY_SIZE(keys); j++) {
 149
 150                        if (fintek_8250_enter_key(addr[i], keys[j]))
 151                                continue;
 152                        if (fintek_8250_check_id(addr[i])) {
 153                                fintek_8250_exit_key(addr[i]);
 154                                continue;
 155                        }
 156
 157                        for (k = 0; k < 4; k++) {
 158                                u16 aux;
 159
 160                                outb(LDN, addr[i] + ADDR_PORT);
 161                                outb(k, addr[i] + DATA_PORT);
 162
 163                                outb(IO_ADDR1, addr[i] + ADDR_PORT);
 164                                aux = inb(addr[i] + DATA_PORT);
 165                                outb(IO_ADDR2, addr[i] + ADDR_PORT);
 166                                aux |= inb(addr[i] + DATA_PORT) << 8;
 167                                if (aux != io_address)
 168                                        continue;
 169
 170                                fintek_8250_exit_key(addr[i]);
 171                                *key = keys[j];
 172                                *index = k;
 173                                return addr[i];
 174                        }
 175                        fintek_8250_exit_key(addr[i]);
 176                }
 177        }
 178
 179        return -ENODEV;
 180}
 181
 182static int
 183fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 184{
 185        struct uart_8250_port uart;
 186        struct fintek_8250 *pdata;
 187        int base_port;
 188        u8 key;
 189        u8 index;
 190
 191        if (!pnp_port_valid(dev, 0))
 192                return -ENODEV;
 193
 194        base_port = fintek_8250_base_port(pnp_port_start(dev, 0), &key, &index);
 195        if (base_port < 0)
 196                return -ENODEV;
 197
 198        memset(&uart, 0, sizeof(uart));
 199
 200        pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
 201        if (!pdata)
 202                return -ENOMEM;
 203        uart.port.private_data = pdata;
 204
 205        if (!pnp_irq_valid(dev, 0))
 206                return -ENODEV;
 207        uart.port.irq = pnp_irq(dev, 0);
 208        uart.port.iobase = pnp_port_start(dev, 0);
 209        uart.port.iotype = UPIO_PORT;
 210        uart.port.rs485_config = fintek_8250_rs485_config;
 211
 212        uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
 213        if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
 214                uart.port.flags |= UPF_SHARE_IRQ;
 215        uart.port.uartclk = 1843200;
 216        uart.port.dev = &dev->dev;
 217
 218        pdata->key = key;
 219        pdata->base_port = base_port;
 220        pdata->index = index;
 221        pdata->line = serial8250_register_8250_port(&uart);
 222        if (pdata->line < 0)
 223                return -ENODEV;
 224
 225        pnp_set_drvdata(dev, pdata);
 226        return 0;
 227}
 228
 229static void fintek_8250_remove(struct pnp_dev *dev)
 230{
 231        struct fintek_8250 *pdata = pnp_get_drvdata(dev);
 232
 233        if (pdata)
 234                serial8250_unregister_port(pdata->line);
 235}
 236
 237#ifdef CONFIG_PM
 238static int fintek_8250_suspend(struct pnp_dev *dev, pm_message_t state)
 239{
 240        struct fintek_8250 *pdata = pnp_get_drvdata(dev);
 241
 242        if (!pdata)
 243                return -ENODEV;
 244        serial8250_suspend_port(pdata->line);
 245        return 0;
 246}
 247
 248static int fintek_8250_resume(struct pnp_dev *dev)
 249{
 250        struct fintek_8250 *pdata = pnp_get_drvdata(dev);
 251
 252        if (!pdata)
 253                return -ENODEV;
 254        serial8250_resume_port(pdata->line);
 255        return 0;
 256}
 257#else
 258#define fintek_8250_suspend NULL
 259#define fintek_8250_resume NULL
 260#endif /* CONFIG_PM */
 261
 262static const struct pnp_device_id fintek_dev_table[] = {
 263        /* Qtechnology Panel PC / IO1000 */
 264        { "PNP0501"},
 265        {}
 266};
 267
 268MODULE_DEVICE_TABLE(pnp, fintek_dev_table);
 269
 270static struct pnp_driver fintek_8250_driver = {
 271        .name           = DRIVER_NAME,
 272        .probe          = fintek_8250_probe,
 273        .remove         = fintek_8250_remove,
 274        .suspend        = fintek_8250_suspend,
 275        .resume         = fintek_8250_resume,
 276        .id_table       = fintek_dev_table,
 277};
 278
 279module_pnp_driver(fintek_8250_driver);
 280MODULE_DESCRIPTION("Fintek F812164 module");
 281MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
 282MODULE_LICENSE("GPL");
 283