Skip to content

Commit

Permalink
serial/8250_fintek: Support for multiple base_ports
Browse files Browse the repository at this point in the history
Fintek chip can be connected at address 0x4e and also 0x2e.
Add some logic to find out the address of the chip.

Reported-by: Peter Hong <[email protected]>
Reviewed-by: Alan Cox <[email protected]>
Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
ribalda authored and gregkh committed Jul 23, 2015
1 parent 92a5f11 commit 017bec3
Showing 1 changed file with 52 additions and 35 deletions.
87 changes: 52 additions & 35 deletions drivers/tty/serial/8250/8250_fintek.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
#include <linux/serial_core.h>
#include "8250.h"

#define ADDR_PORT 0x4E
#define DATA_PORT 0x4F
#define ADDR_PORT 0
#define DATA_PORT 1
#define ENTRY_KEY 0x77
#define EXIT_KEY 0xAA
#define CHIP_ID1 0x20
Expand All @@ -40,24 +40,27 @@
#define DRIVER_NAME "8250_fintek"

struct fintek_8250 {
u16 base_port;
u8 index;
long line;
};

static int fintek_8250_enter_key(void){
static int fintek_8250_enter_key(u16 base_port)
{

if (!request_muxed_region(ADDR_PORT, 2, DRIVER_NAME))
if (!request_muxed_region(base_port, 2, DRIVER_NAME))
return -EBUSY;

outb(ENTRY_KEY, ADDR_PORT);
outb(ENTRY_KEY, ADDR_PORT);
outb(ENTRY_KEY, base_port + ADDR_PORT);
outb(ENTRY_KEY, base_port + ADDR_PORT);
return 0;
}

static void fintek_8250_exit_key(void){
static void fintek_8250_exit_key(u16 base_port)
{

outb(EXIT_KEY, ADDR_PORT);
release_region(ADDR_PORT, 2);
outb(EXIT_KEY, base_port + ADDR_PORT);
release_region(base_port + ADDR_PORT, 2);
}

static int fintek_8250_get_index(resource_size_t base_addr)
Expand All @@ -72,23 +75,23 @@ static int fintek_8250_get_index(resource_size_t base_addr)
return -ENODEV;
}

static int fintek_8250_check_id(void)
static int fintek_8250_check_id(u16 base_port)
{

outb(CHIP_ID1, ADDR_PORT);
if (inb(DATA_PORT) != CHIP_ID1_VAL)
outb(CHIP_ID1, base_port + ADDR_PORT);
if (inb(base_port + DATA_PORT) != CHIP_ID1_VAL)
return -ENODEV;

outb(CHIP_ID2, ADDR_PORT);
if (inb(DATA_PORT) != CHIP_ID2_VAL)
outb(CHIP_ID2, base_port + ADDR_PORT);
if (inb(base_port + DATA_PORT) != CHIP_ID2_VAL)
return -ENODEV;

outb(VENDOR_ID1, ADDR_PORT);
if (inb(DATA_PORT) != VENDOR_ID1_VAL)
outb(VENDOR_ID1, base_port + ADDR_PORT);
if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL)
return -ENODEV;

outb(VENDOR_ID2, ADDR_PORT);
if (inb(DATA_PORT) != VENDOR_ID2_VAL)
outb(VENDOR_ID2, base_port + ADDR_PORT);
if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL)
return -ENODEV;

return 0;
Expand Down Expand Up @@ -130,45 +133,58 @@ static int fintek_8250_rs485_config(struct uart_port *port,
if (rs485->flags & SER_RS485_RTS_ON_SEND)
config |= RTS_INVERT;

if (fintek_8250_enter_key())
if (fintek_8250_enter_key(pdata->base_port))
return -EBUSY;

outb(LDN, ADDR_PORT);
outb(pdata->index, DATA_PORT);
outb(RS485, ADDR_PORT);
outb(config, DATA_PORT);
fintek_8250_exit_key();
outb(LDN, pdata->base_port + ADDR_PORT);
outb(pdata->index, pdata->base_port + DATA_PORT);
outb(RS485, pdata->base_port + ADDR_PORT);
outb(config, pdata->base_port + DATA_PORT);
fintek_8250_exit_key(pdata->base_port);

port->rs485 = *rs485;

return 0;
}

static int fintek_8250_base_port(void)
{
static const u16 addr[] = {0x4e, 0x2e};
int i;

for (i = 0; i < ARRAY_SIZE(addr); i++) {
int ret;

if (fintek_8250_enter_key(addr[i]))
continue;
ret = fintek_8250_check_id(addr[i]);
fintek_8250_exit_key(addr[i]);
if (!ret)
return addr[i];
}

return -ENODEV;
}

static int
fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
struct uart_8250_port uart;
struct fintek_8250 *pdata;
int ret;
int index;
int base_port;

if (!pnp_port_valid(dev, 0))
return -ENODEV;

base_port = fintek_8250_base_port();
if (base_port < 0)
return -ENODEV;

index = fintek_8250_get_index(pnp_port_start(dev, 0));
if (index < 0)
return -ENODEV;

/* Enable configuration registers*/
if (fintek_8250_enter_key())
return -EBUSY;

/*Check ID*/
ret = fintek_8250_check_id();
fintek_8250_exit_key();
if (ret)
return ret;

memset(&uart, 0, sizeof(uart));

pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
Expand All @@ -189,6 +205,7 @@ fintek_8250_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
uart.port.uartclk = 1843200;
uart.port.dev = &dev->dev;

pdata->base_port = base_port;
pdata->index = index;
pdata->line = serial8250_register_8250_port(&uart);
if (pdata->line < 0)
Expand Down

0 comments on commit 017bec3

Please sign in to comment.