162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GPIO driver for Fintek and Nuvoton Super-I/O chips 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010-2013 LaCie 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Simon Guinot <simon.guinot@sequanux.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define DRVNAME "gpio-f7188x" 1162306a36Sopenharmony_ci#define pr_fmt(fmt) DRVNAME ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1862306a36Sopenharmony_ci#include <linux/bitops.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Super-I/O registers 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define SIO_LDSEL 0x07 /* Logical device select */ 2462306a36Sopenharmony_ci#define SIO_DEVID 0x20 /* Device ID (2 bytes) */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ 2762306a36Sopenharmony_ci#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Fintek devices. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define SIO_FINTEK_DEVREV 0x22 /* Fintek Device revision */ 3362306a36Sopenharmony_ci#define SIO_FINTEK_MANID 0x23 /* Fintek ID (2 bytes) */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define SIO_FINTEK_ID 0x1934 /* Manufacturer ID */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define SIO_F71869_ID 0x0814 /* F71869 chipset ID */ 3862306a36Sopenharmony_ci#define SIO_F71869A_ID 0x1007 /* F71869A chipset ID */ 3962306a36Sopenharmony_ci#define SIO_F71882_ID 0x0541 /* F71882 chipset ID */ 4062306a36Sopenharmony_ci#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */ 4162306a36Sopenharmony_ci#define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */ 4262306a36Sopenharmony_ci#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */ 4362306a36Sopenharmony_ci#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for F81966 */ 4462306a36Sopenharmony_ci#define SIO_F81865_ID 0x0704 /* F81865 chipset ID */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define SIO_LD_GPIO_FINTEK 0x06 /* GPIO logical device */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Nuvoton devices. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cienum chips { 5762306a36Sopenharmony_ci f71869, 5862306a36Sopenharmony_ci f71869a, 5962306a36Sopenharmony_ci f71882fg, 6062306a36Sopenharmony_ci f71889a, 6162306a36Sopenharmony_ci f71889f, 6262306a36Sopenharmony_ci f81866, 6362306a36Sopenharmony_ci f81804, 6462306a36Sopenharmony_ci f81865, 6562306a36Sopenharmony_ci nct6126d, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const char * const f7188x_names[] = { 6962306a36Sopenharmony_ci "f71869", 7062306a36Sopenharmony_ci "f71869a", 7162306a36Sopenharmony_ci "f71882fg", 7262306a36Sopenharmony_ci "f71889a", 7362306a36Sopenharmony_ci "f71889f", 7462306a36Sopenharmony_ci "f81866", 7562306a36Sopenharmony_ci "f81804", 7662306a36Sopenharmony_ci "f81865", 7762306a36Sopenharmony_ci "nct6126d", 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct f7188x_sio { 8162306a36Sopenharmony_ci int addr; 8262306a36Sopenharmony_ci int device; 8362306a36Sopenharmony_ci enum chips type; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct f7188x_gpio_bank { 8762306a36Sopenharmony_ci struct gpio_chip chip; 8862306a36Sopenharmony_ci unsigned int regbase; 8962306a36Sopenharmony_ci struct f7188x_gpio_data *data; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct f7188x_gpio_data { 9362306a36Sopenharmony_ci struct f7188x_sio *sio; 9462306a36Sopenharmony_ci int nr_bank; 9562306a36Sopenharmony_ci struct f7188x_gpio_bank *bank; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Super-I/O functions. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic inline int superio_inb(int base, int reg) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci outb(reg, base); 10562306a36Sopenharmony_ci return inb(base + 1); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int superio_inw(int base, int reg) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int val; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci outb(reg++, base); 11362306a36Sopenharmony_ci val = inb(base + 1) << 8; 11462306a36Sopenharmony_ci outb(reg, base); 11562306a36Sopenharmony_ci val |= inb(base + 1); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return val; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic inline void superio_outb(int base, int reg, int val) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci outb(reg, base); 12362306a36Sopenharmony_ci outb(val, base + 1); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline int superio_enter(int base) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci /* Don't step on other drivers' I/O space by accident. */ 12962306a36Sopenharmony_ci if (!request_muxed_region(base, 2, DRVNAME)) { 13062306a36Sopenharmony_ci pr_err("I/O address 0x%04x already in use\n", base); 13162306a36Sopenharmony_ci return -EBUSY; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* According to the datasheet the key must be send twice. */ 13562306a36Sopenharmony_ci outb(SIO_UNLOCK_KEY, base); 13662306a36Sopenharmony_ci outb(SIO_UNLOCK_KEY, base); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void superio_select(int base, int ld) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci outb(SIO_LDSEL, base); 14462306a36Sopenharmony_ci outb(ld, base + 1); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic inline void superio_exit(int base) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci outb(SIO_LOCK_KEY, base); 15062306a36Sopenharmony_ci release_region(base, 2); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * GPIO chip. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset); 15862306a36Sopenharmony_cistatic int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset); 15962306a36Sopenharmony_cistatic int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset); 16062306a36Sopenharmony_cistatic int f7188x_gpio_direction_out(struct gpio_chip *chip, 16162306a36Sopenharmony_ci unsigned offset, int value); 16262306a36Sopenharmony_cistatic void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value); 16362306a36Sopenharmony_cistatic int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, 16462306a36Sopenharmony_ci unsigned long config); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define F7188X_GPIO_BANK(_base, _ngpio, _regbase, _label) \ 16762306a36Sopenharmony_ci { \ 16862306a36Sopenharmony_ci .chip = { \ 16962306a36Sopenharmony_ci .label = _label, \ 17062306a36Sopenharmony_ci .owner = THIS_MODULE, \ 17162306a36Sopenharmony_ci .get_direction = f7188x_gpio_get_direction, \ 17262306a36Sopenharmony_ci .direction_input = f7188x_gpio_direction_in, \ 17362306a36Sopenharmony_ci .get = f7188x_gpio_get, \ 17462306a36Sopenharmony_ci .direction_output = f7188x_gpio_direction_out, \ 17562306a36Sopenharmony_ci .set = f7188x_gpio_set, \ 17662306a36Sopenharmony_ci .set_config = f7188x_gpio_set_config, \ 17762306a36Sopenharmony_ci .base = _base, \ 17862306a36Sopenharmony_ci .ngpio = _ngpio, \ 17962306a36Sopenharmony_ci .can_sleep = true, \ 18062306a36Sopenharmony_ci }, \ 18162306a36Sopenharmony_ci .regbase = _regbase, \ 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#define f7188x_gpio_dir(base) ((base) + 0) 18562306a36Sopenharmony_ci#define f7188x_gpio_data_out(base) ((base) + 1) 18662306a36Sopenharmony_ci#define f7188x_gpio_data_in(base) ((base) + 2) 18762306a36Sopenharmony_ci/* Output mode register (0:open drain 1:push-pull). */ 18862306a36Sopenharmony_ci#define f7188x_gpio_out_mode(base) ((base) + 3) 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#define f7188x_gpio_dir_invert(type) ((type) == nct6126d) 19162306a36Sopenharmony_ci#define f7188x_gpio_data_single(type) ((type) == nct6126d) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct f7188x_gpio_bank f71869_gpio_bank[] = { 19462306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 6, 0xF0, DRVNAME "-0"), 19562306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 19662306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 19762306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 19862306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 19962306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 5, 0xA0, DRVNAME "-5"), 20062306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 6, 0x90, DRVNAME "-6"), 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct f7188x_gpio_bank f71869a_gpio_bank[] = { 20462306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 6, 0xF0, DRVNAME "-0"), 20562306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 20662306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 20762306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 20862306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 20962306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 5, 0xA0, DRVNAME "-5"), 21062306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0x90, DRVNAME "-6"), 21162306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0x80, DRVNAME "-7"), 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic struct f7188x_gpio_bank f71882_gpio_bank[] = { 21562306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 8, 0xF0, DRVNAME "-0"), 21662306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 21762306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 21862306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 4, 0xC0, DRVNAME "-3"), 21962306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 4, 0xB0, DRVNAME "-4"), 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic struct f7188x_gpio_bank f71889a_gpio_bank[] = { 22362306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 7, 0xF0, DRVNAME "-0"), 22462306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 7, 0xE0, DRVNAME "-1"), 22562306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 22662306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 22762306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 22862306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 5, 0xA0, DRVNAME "-5"), 22962306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0x90, DRVNAME "-6"), 23062306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0x80, DRVNAME "-7"), 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic struct f7188x_gpio_bank f71889_gpio_bank[] = { 23462306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 7, 0xF0, DRVNAME "-0"), 23562306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 7, 0xE0, DRVNAME "-1"), 23662306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 23762306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 23862306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 23962306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 5, 0xA0, DRVNAME "-5"), 24062306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0x90, DRVNAME "-6"), 24162306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0x80, DRVNAME "-7"), 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct f7188x_gpio_bank f81866_gpio_bank[] = { 24562306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 8, 0xF0, DRVNAME "-0"), 24662306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 24762306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 24862306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 24962306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 25062306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 8, 0xA0, DRVNAME "-5"), 25162306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0x90, DRVNAME "-6"), 25262306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0x80, DRVNAME "-7"), 25362306a36Sopenharmony_ci F7188X_GPIO_BANK(80, 8, 0x88, DRVNAME "-8"), 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct f7188x_gpio_bank f81804_gpio_bank[] = { 25862306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 8, 0xF0, DRVNAME "-0"), 25962306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 26062306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 26162306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 8, 0xA0, DRVNAME "-3"), 26262306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0x90, DRVNAME "-4"), 26362306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0x80, DRVNAME "-5"), 26462306a36Sopenharmony_ci F7188X_GPIO_BANK(90, 8, 0x98, DRVNAME "-6"), 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic struct f7188x_gpio_bank f81865_gpio_bank[] = { 26862306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 8, 0xF0, DRVNAME "-0"), 26962306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE0, DRVNAME "-1"), 27062306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xD0, DRVNAME "-2"), 27162306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xC0, DRVNAME "-3"), 27262306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xB0, DRVNAME "-4"), 27362306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 8, 0xA0, DRVNAME "-5"), 27462306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 5, 0x90, DRVNAME "-6"), 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct f7188x_gpio_bank nct6126d_gpio_bank[] = { 27862306a36Sopenharmony_ci F7188X_GPIO_BANK(0, 8, 0xE0, DRVNAME "-0"), 27962306a36Sopenharmony_ci F7188X_GPIO_BANK(10, 8, 0xE4, DRVNAME "-1"), 28062306a36Sopenharmony_ci F7188X_GPIO_BANK(20, 8, 0xE8, DRVNAME "-2"), 28162306a36Sopenharmony_ci F7188X_GPIO_BANK(30, 8, 0xEC, DRVNAME "-3"), 28262306a36Sopenharmony_ci F7188X_GPIO_BANK(40, 8, 0xF0, DRVNAME "-4"), 28362306a36Sopenharmony_ci F7188X_GPIO_BANK(50, 8, 0xF4, DRVNAME "-5"), 28462306a36Sopenharmony_ci F7188X_GPIO_BANK(60, 8, 0xF8, DRVNAME "-6"), 28562306a36Sopenharmony_ci F7188X_GPIO_BANK(70, 8, 0xFC, DRVNAME "-7"), 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int err; 29162306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 29262306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 29362306a36Sopenharmony_ci u8 dir; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci err = superio_enter(sio->addr); 29662306a36Sopenharmony_ci if (err) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase)); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci superio_exit(sio->addr); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (f7188x_gpio_dir_invert(sio->type)) 30562306a36Sopenharmony_ci dir = ~dir; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (dir & BIT(offset)) 30862306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int err; 31662306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 31762306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 31862306a36Sopenharmony_ci u8 dir; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci err = superio_enter(sio->addr); 32162306a36Sopenharmony_ci if (err) 32262306a36Sopenharmony_ci return err; 32362306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (f7188x_gpio_dir_invert(sio->type)) 32862306a36Sopenharmony_ci dir |= BIT(offset); 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci dir &= ~BIT(offset); 33162306a36Sopenharmony_ci superio_outb(sio->addr, f7188x_gpio_dir(bank->regbase), dir); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci superio_exit(sio->addr); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int err; 34162306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 34262306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 34362306a36Sopenharmony_ci u8 dir, data; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci err = superio_enter(sio->addr); 34662306a36Sopenharmony_ci if (err) 34762306a36Sopenharmony_ci return err; 34862306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase)); 35162306a36Sopenharmony_ci dir = !!(dir & BIT(offset)); 35262306a36Sopenharmony_ci if (f7188x_gpio_data_single(sio->type) || dir) 35362306a36Sopenharmony_ci data = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase)); 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci data = superio_inb(sio->addr, f7188x_gpio_data_in(bank->regbase)); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci superio_exit(sio->addr); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return !!(data & BIT(offset)); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int f7188x_gpio_direction_out(struct gpio_chip *chip, 36362306a36Sopenharmony_ci unsigned offset, int value) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int err; 36662306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 36762306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 36862306a36Sopenharmony_ci u8 dir, data_out; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci err = superio_enter(sio->addr); 37162306a36Sopenharmony_ci if (err) 37262306a36Sopenharmony_ci return err; 37362306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase)); 37662306a36Sopenharmony_ci if (value) 37762306a36Sopenharmony_ci data_out |= BIT(offset); 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci data_out &= ~BIT(offset); 38062306a36Sopenharmony_ci superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase)); 38362306a36Sopenharmony_ci if (f7188x_gpio_dir_invert(sio->type)) 38462306a36Sopenharmony_ci dir &= ~BIT(offset); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci dir |= BIT(offset); 38762306a36Sopenharmony_ci superio_outb(sio->addr, f7188x_gpio_dir(bank->regbase), dir); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci superio_exit(sio->addr); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int err; 39762306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 39862306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 39962306a36Sopenharmony_ci u8 data_out; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci err = superio_enter(sio->addr); 40262306a36Sopenharmony_ci if (err) 40362306a36Sopenharmony_ci return; 40462306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase)); 40762306a36Sopenharmony_ci if (value) 40862306a36Sopenharmony_ci data_out |= BIT(offset); 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci data_out &= ~BIT(offset); 41162306a36Sopenharmony_ci superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci superio_exit(sio->addr); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, 41762306a36Sopenharmony_ci unsigned long config) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int err; 42062306a36Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(config); 42162306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); 42262306a36Sopenharmony_ci struct f7188x_sio *sio = bank->data->sio; 42362306a36Sopenharmony_ci u8 data; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (param != PIN_CONFIG_DRIVE_OPEN_DRAIN && 42662306a36Sopenharmony_ci param != PIN_CONFIG_DRIVE_PUSH_PULL) 42762306a36Sopenharmony_ci return -ENOTSUPP; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci err = superio_enter(sio->addr); 43062306a36Sopenharmony_ci if (err) 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci superio_select(sio->addr, sio->device); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci data = superio_inb(sio->addr, f7188x_gpio_out_mode(bank->regbase)); 43562306a36Sopenharmony_ci if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) 43662306a36Sopenharmony_ci data &= ~BIT(offset); 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci data |= BIT(offset); 43962306a36Sopenharmony_ci superio_outb(sio->addr, f7188x_gpio_out_mode(bank->regbase), data); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci superio_exit(sio->addr); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * Platform device and driver. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int f7188x_gpio_probe(struct platform_device *pdev) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int err; 45262306a36Sopenharmony_ci int i; 45362306a36Sopenharmony_ci struct f7188x_sio *sio = dev_get_platdata(&pdev->dev); 45462306a36Sopenharmony_ci struct f7188x_gpio_data *data; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 45762306a36Sopenharmony_ci if (!data) 45862306a36Sopenharmony_ci return -ENOMEM; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (sio->type) { 46162306a36Sopenharmony_ci case f71869: 46262306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f71869_gpio_bank); 46362306a36Sopenharmony_ci data->bank = f71869_gpio_bank; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case f71869a: 46662306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f71869a_gpio_bank); 46762306a36Sopenharmony_ci data->bank = f71869a_gpio_bank; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case f71882fg: 47062306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f71882_gpio_bank); 47162306a36Sopenharmony_ci data->bank = f71882_gpio_bank; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case f71889a: 47462306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f71889a_gpio_bank); 47562306a36Sopenharmony_ci data->bank = f71889a_gpio_bank; 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case f71889f: 47862306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f71889_gpio_bank); 47962306a36Sopenharmony_ci data->bank = f71889_gpio_bank; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci case f81866: 48262306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f81866_gpio_bank); 48362306a36Sopenharmony_ci data->bank = f81866_gpio_bank; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case f81804: 48662306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f81804_gpio_bank); 48762306a36Sopenharmony_ci data->bank = f81804_gpio_bank; 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci case f81865: 49062306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(f81865_gpio_bank); 49162306a36Sopenharmony_ci data->bank = f81865_gpio_bank; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case nct6126d: 49462306a36Sopenharmony_ci data->nr_bank = ARRAY_SIZE(nct6126d_gpio_bank); 49562306a36Sopenharmony_ci data->bank = nct6126d_gpio_bank; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci default: 49862306a36Sopenharmony_ci return -ENODEV; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci data->sio = sio; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* For each GPIO bank, register a GPIO chip. */ 50562306a36Sopenharmony_ci for (i = 0; i < data->nr_bank; i++) { 50662306a36Sopenharmony_ci struct f7188x_gpio_bank *bank = &data->bank[i]; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci bank->chip.parent = &pdev->dev; 50962306a36Sopenharmony_ci bank->data = data; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank); 51262306a36Sopenharmony_ci if (err) { 51362306a36Sopenharmony_ci dev_err(&pdev->dev, 51462306a36Sopenharmony_ci "Failed to register gpiochip %d: %d\n", 51562306a36Sopenharmony_ci i, err); 51662306a36Sopenharmony_ci return err; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int __init f7188x_find(int addr, struct f7188x_sio *sio) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int err; 52662306a36Sopenharmony_ci u16 devid; 52762306a36Sopenharmony_ci u16 manid; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci err = superio_enter(addr); 53062306a36Sopenharmony_ci if (err) 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci err = -ENODEV; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci sio->device = SIO_LD_GPIO_FINTEK; 53662306a36Sopenharmony_ci devid = superio_inw(addr, SIO_DEVID); 53762306a36Sopenharmony_ci switch (devid) { 53862306a36Sopenharmony_ci case SIO_F71869_ID: 53962306a36Sopenharmony_ci sio->type = f71869; 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci case SIO_F71869A_ID: 54262306a36Sopenharmony_ci sio->type = f71869a; 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case SIO_F71882_ID: 54562306a36Sopenharmony_ci sio->type = f71882fg; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case SIO_F71889A_ID: 54862306a36Sopenharmony_ci sio->type = f71889a; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case SIO_F71889_ID: 55162306a36Sopenharmony_ci sio->type = f71889f; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case SIO_F81866_ID: 55462306a36Sopenharmony_ci sio->type = f81866; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci case SIO_F81804_ID: 55762306a36Sopenharmony_ci sio->type = f81804; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case SIO_F81865_ID: 56062306a36Sopenharmony_ci sio->type = f81865; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci case SIO_NCT6126D_ID: 56362306a36Sopenharmony_ci sio->device = SIO_LD_GPIO_NUVOTON; 56462306a36Sopenharmony_ci sio->type = nct6126d; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci default: 56762306a36Sopenharmony_ci pr_info("Unsupported Fintek device 0x%04x\n", devid); 56862306a36Sopenharmony_ci goto err; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* double check manufacturer where possible */ 57262306a36Sopenharmony_ci if (sio->type != nct6126d) { 57362306a36Sopenharmony_ci manid = superio_inw(addr, SIO_FINTEK_MANID); 57462306a36Sopenharmony_ci if (manid != SIO_FINTEK_ID) { 57562306a36Sopenharmony_ci pr_debug("Not a Fintek device at 0x%08x\n", addr); 57662306a36Sopenharmony_ci goto err; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci sio->addr = addr; 58162306a36Sopenharmony_ci err = 0; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci pr_info("Found %s at %#x\n", f7188x_names[sio->type], (unsigned int)addr); 58462306a36Sopenharmony_ci if (sio->type != nct6126d) 58562306a36Sopenharmony_ci pr_info(" revision %d\n", superio_inb(addr, SIO_FINTEK_DEVREV)); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cierr: 58862306a36Sopenharmony_ci superio_exit(addr); 58962306a36Sopenharmony_ci return err; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct platform_device *f7188x_gpio_pdev; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic int __init 59562306a36Sopenharmony_cif7188x_gpio_device_add(const struct f7188x_sio *sio) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci int err; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1); 60062306a36Sopenharmony_ci if (!f7188x_gpio_pdev) 60162306a36Sopenharmony_ci return -ENOMEM; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci err = platform_device_add_data(f7188x_gpio_pdev, 60462306a36Sopenharmony_ci sio, sizeof(*sio)); 60562306a36Sopenharmony_ci if (err) { 60662306a36Sopenharmony_ci pr_err("Platform data allocation failed\n"); 60762306a36Sopenharmony_ci goto err; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci err = platform_device_add(f7188x_gpio_pdev); 61162306a36Sopenharmony_ci if (err) { 61262306a36Sopenharmony_ci pr_err("Device addition failed\n"); 61362306a36Sopenharmony_ci goto err; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cierr: 61962306a36Sopenharmony_ci platform_device_put(f7188x_gpio_pdev); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* 62562306a36Sopenharmony_ci * Try to match a supported Fintek device by reading the (hard-wired) 62662306a36Sopenharmony_ci * configuration I/O ports. If available, then register both the platform 62762306a36Sopenharmony_ci * device and driver to support the GPIOs. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic struct platform_driver f7188x_gpio_driver = { 63162306a36Sopenharmony_ci .driver = { 63262306a36Sopenharmony_ci .name = DRVNAME, 63362306a36Sopenharmony_ci }, 63462306a36Sopenharmony_ci .probe = f7188x_gpio_probe, 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int __init f7188x_gpio_init(void) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci int err; 64062306a36Sopenharmony_ci struct f7188x_sio sio; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (f7188x_find(0x2e, &sio) && 64362306a36Sopenharmony_ci f7188x_find(0x4e, &sio)) 64462306a36Sopenharmony_ci return -ENODEV; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci err = platform_driver_register(&f7188x_gpio_driver); 64762306a36Sopenharmony_ci if (!err) { 64862306a36Sopenharmony_ci err = f7188x_gpio_device_add(&sio); 64962306a36Sopenharmony_ci if (err) 65062306a36Sopenharmony_ci platform_driver_unregister(&f7188x_gpio_driver); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return err; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_cisubsys_initcall(f7188x_gpio_init); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void __exit f7188x_gpio_exit(void) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci platform_device_unregister(f7188x_gpio_pdev); 66062306a36Sopenharmony_ci platform_driver_unregister(&f7188x_gpio_driver); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_cimodule_exit(f7188x_gpio_exit); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG, F71889A, F71889F and F81866"); 66562306a36Sopenharmony_ciMODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>"); 66662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 667