18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882, F71889 and F81866
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2013 LaCie
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Simon Guinot <simon.guinot@sequanux.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define DRVNAME "gpio-f7188x"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * Super-I/O registers
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#define SIO_LDSEL		0x07	/* Logical device select */
238c2ecf20Sopenharmony_ci#define SIO_DEVID		0x20	/* Device ID (2 bytes) */
248c2ecf20Sopenharmony_ci#define SIO_DEVREV		0x22	/* Device revision */
258c2ecf20Sopenharmony_ci#define SIO_MANID		0x23	/* Fintek ID (2 bytes) */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SIO_LD_GPIO		0x06	/* GPIO logical device */
288c2ecf20Sopenharmony_ci#define SIO_UNLOCK_KEY		0x87	/* Key to enable Super-I/O */
298c2ecf20Sopenharmony_ci#define SIO_LOCK_KEY		0xAA	/* Key to disable Super-I/O */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SIO_FINTEK_ID		0x1934	/* Manufacturer ID */
328c2ecf20Sopenharmony_ci#define SIO_F71869_ID		0x0814	/* F71869 chipset ID */
338c2ecf20Sopenharmony_ci#define SIO_F71869A_ID		0x1007	/* F71869A chipset ID */
348c2ecf20Sopenharmony_ci#define SIO_F71882_ID		0x0541	/* F71882 chipset ID */
358c2ecf20Sopenharmony_ci#define SIO_F71889_ID		0x0909	/* F71889 chipset ID */
368c2ecf20Sopenharmony_ci#define SIO_F71889A_ID		0x1005	/* F71889A chipset ID */
378c2ecf20Sopenharmony_ci#define SIO_F81866_ID		0x1010	/* F81866 chipset ID */
388c2ecf20Sopenharmony_ci#define SIO_F81804_ID		0x1502  /* F81804 chipset ID, same for f81966 */
398c2ecf20Sopenharmony_ci#define SIO_F81865_ID		0x0704	/* F81865 chipset ID */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cienum chips {
438c2ecf20Sopenharmony_ci	f71869,
448c2ecf20Sopenharmony_ci	f71869a,
458c2ecf20Sopenharmony_ci	f71882fg,
468c2ecf20Sopenharmony_ci	f71889a,
478c2ecf20Sopenharmony_ci	f71889f,
488c2ecf20Sopenharmony_ci	f81866,
498c2ecf20Sopenharmony_ci	f81804,
508c2ecf20Sopenharmony_ci	f81865,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const char * const f7188x_names[] = {
548c2ecf20Sopenharmony_ci	"f71869",
558c2ecf20Sopenharmony_ci	"f71869a",
568c2ecf20Sopenharmony_ci	"f71882fg",
578c2ecf20Sopenharmony_ci	"f71889a",
588c2ecf20Sopenharmony_ci	"f71889f",
598c2ecf20Sopenharmony_ci	"f81866",
608c2ecf20Sopenharmony_ci	"f81804",
618c2ecf20Sopenharmony_ci	"f81865",
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct f7188x_sio {
658c2ecf20Sopenharmony_ci	int addr;
668c2ecf20Sopenharmony_ci	enum chips type;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct f7188x_gpio_bank {
708c2ecf20Sopenharmony_ci	struct gpio_chip chip;
718c2ecf20Sopenharmony_ci	unsigned int regbase;
728c2ecf20Sopenharmony_ci	struct f7188x_gpio_data *data;
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct f7188x_gpio_data {
768c2ecf20Sopenharmony_ci	struct f7188x_sio *sio;
778c2ecf20Sopenharmony_ci	int nr_bank;
788c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank;
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * Super-I/O functions.
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic inline int superio_inb(int base, int reg)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	outb(reg, base);
888c2ecf20Sopenharmony_ci	return inb(base + 1);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int superio_inw(int base, int reg)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int val;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	outb(reg++, base);
968c2ecf20Sopenharmony_ci	val = inb(base + 1) << 8;
978c2ecf20Sopenharmony_ci	outb(reg, base);
988c2ecf20Sopenharmony_ci	val |= inb(base + 1);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return val;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic inline void superio_outb(int base, int reg, int val)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	outb(reg, base);
1068c2ecf20Sopenharmony_ci	outb(val, base + 1);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic inline int superio_enter(int base)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	/* Don't step on other drivers' I/O space by accident. */
1128c2ecf20Sopenharmony_ci	if (!request_muxed_region(base, 2, DRVNAME)) {
1138c2ecf20Sopenharmony_ci		pr_err(DRVNAME "I/O address 0x%04x already in use\n", base);
1148c2ecf20Sopenharmony_ci		return -EBUSY;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* According to the datasheet the key must be send twice. */
1188c2ecf20Sopenharmony_ci	outb(SIO_UNLOCK_KEY, base);
1198c2ecf20Sopenharmony_ci	outb(SIO_UNLOCK_KEY, base);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic inline void superio_select(int base, int ld)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	outb(SIO_LDSEL, base);
1278c2ecf20Sopenharmony_ci	outb(ld, base + 1);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline void superio_exit(int base)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	outb(SIO_LOCK_KEY, base);
1338c2ecf20Sopenharmony_ci	release_region(base, 2);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * GPIO chip.
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset);
1418c2ecf20Sopenharmony_cistatic int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
1428c2ecf20Sopenharmony_cistatic int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
1438c2ecf20Sopenharmony_cistatic int f7188x_gpio_direction_out(struct gpio_chip *chip,
1448c2ecf20Sopenharmony_ci				     unsigned offset, int value);
1458c2ecf20Sopenharmony_cistatic void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
1468c2ecf20Sopenharmony_cistatic int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset,
1478c2ecf20Sopenharmony_ci				  unsigned long config);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#define F7188X_GPIO_BANK(_base, _ngpio, _regbase)			\
1508c2ecf20Sopenharmony_ci	{								\
1518c2ecf20Sopenharmony_ci		.chip = {						\
1528c2ecf20Sopenharmony_ci			.label            = DRVNAME,			\
1538c2ecf20Sopenharmony_ci			.owner            = THIS_MODULE,		\
1548c2ecf20Sopenharmony_ci			.get_direction    = f7188x_gpio_get_direction,	\
1558c2ecf20Sopenharmony_ci			.direction_input  = f7188x_gpio_direction_in,	\
1568c2ecf20Sopenharmony_ci			.get              = f7188x_gpio_get,		\
1578c2ecf20Sopenharmony_ci			.direction_output = f7188x_gpio_direction_out,	\
1588c2ecf20Sopenharmony_ci			.set              = f7188x_gpio_set,		\
1598c2ecf20Sopenharmony_ci			.set_config	  = f7188x_gpio_set_config,	\
1608c2ecf20Sopenharmony_ci			.base             = _base,			\
1618c2ecf20Sopenharmony_ci			.ngpio            = _ngpio,			\
1628c2ecf20Sopenharmony_ci			.can_sleep        = true,			\
1638c2ecf20Sopenharmony_ci		},							\
1648c2ecf20Sopenharmony_ci		.regbase = _regbase,					\
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci#define gpio_dir(base) (base + 0)
1688c2ecf20Sopenharmony_ci#define gpio_data_out(base) (base + 1)
1698c2ecf20Sopenharmony_ci#define gpio_data_in(base) (base + 2)
1708c2ecf20Sopenharmony_ci/* Output mode register (0:open drain 1:push-pull). */
1718c2ecf20Sopenharmony_ci#define gpio_out_mode(base) (base + 3)
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f71869_gpio_bank[] = {
1748c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 6, 0xF0),
1758c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
1768c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
1778c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
1788c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
1798c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 5, 0xA0),
1808c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 6, 0x90),
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f71869a_gpio_bank[] = {
1848c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 6, 0xF0),
1858c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
1868c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
1878c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
1888c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
1898c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 5, 0xA0),
1908c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 8, 0x90),
1918c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(70, 8, 0x80),
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f71882_gpio_bank[] = {
1958c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 8, 0xF0),
1968c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
1978c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
1988c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 4, 0xC0),
1998c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 4, 0xB0),
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f71889a_gpio_bank[] = {
2038c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 7, 0xF0),
2048c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 7, 0xE0),
2058c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
2068c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
2078c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
2088c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 5, 0xA0),
2098c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 8, 0x90),
2108c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(70, 8, 0x80),
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f71889_gpio_bank[] = {
2148c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 7, 0xF0),
2158c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 7, 0xE0),
2168c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
2178c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
2188c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
2198c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 5, 0xA0),
2208c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 8, 0x90),
2218c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(70, 8, 0x80),
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f81866_gpio_bank[] = {
2258c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 8, 0xF0),
2268c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
2278c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
2288c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
2298c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
2308c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 8, 0xA0),
2318c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 8, 0x90),
2328c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(70, 8, 0x80),
2338c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(80, 8, 0x88),
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f81804_gpio_bank[] = {
2388c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 8, 0xF0),
2398c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
2408c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
2418c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 8, 0xA0),
2428c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 8, 0x90),
2438c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(70, 8, 0x80),
2448c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(90, 8, 0x98),
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct f7188x_gpio_bank f81865_gpio_bank[] = {
2488c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(0, 8, 0xF0),
2498c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(10, 8, 0xE0),
2508c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(20, 8, 0xD0),
2518c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(30, 8, 0xC0),
2528c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(40, 8, 0xB0),
2538c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(50, 8, 0xA0),
2548c2ecf20Sopenharmony_ci	F7188X_GPIO_BANK(60, 5, 0x90),
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int err;
2608c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
2618c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
2628c2ecf20Sopenharmony_ci	u8 dir;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
2658c2ecf20Sopenharmony_ci	if (err)
2668c2ecf20Sopenharmony_ci		return err;
2678c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (dir & 1 << offset)
2748c2ecf20Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int err;
2828c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
2838c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
2848c2ecf20Sopenharmony_ci	u8 dir;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
2878c2ecf20Sopenharmony_ci	if (err)
2888c2ecf20Sopenharmony_ci		return err;
2898c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
2928c2ecf20Sopenharmony_ci	dir &= ~BIT(offset);
2938c2ecf20Sopenharmony_ci	superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	int err;
3038c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
3048c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
3058c2ecf20Sopenharmony_ci	u8 dir, data;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
3088c2ecf20Sopenharmony_ci	if (err)
3098c2ecf20Sopenharmony_ci		return err;
3108c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
3138c2ecf20Sopenharmony_ci	dir = !!(dir & BIT(offset));
3148c2ecf20Sopenharmony_ci	if (dir)
3158c2ecf20Sopenharmony_ci		data = superio_inb(sio->addr, gpio_data_out(bank->regbase));
3168c2ecf20Sopenharmony_ci	else
3178c2ecf20Sopenharmony_ci		data = superio_inb(sio->addr, gpio_data_in(bank->regbase));
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return !!(data & BIT(offset));
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int f7188x_gpio_direction_out(struct gpio_chip *chip,
3258c2ecf20Sopenharmony_ci				     unsigned offset, int value)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	int err;
3288c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
3298c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
3308c2ecf20Sopenharmony_ci	u8 dir, data_out;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
3338c2ecf20Sopenharmony_ci	if (err)
3348c2ecf20Sopenharmony_ci		return err;
3358c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
3388c2ecf20Sopenharmony_ci	if (value)
3398c2ecf20Sopenharmony_ci		data_out |= BIT(offset);
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		data_out &= ~BIT(offset);
3428c2ecf20Sopenharmony_ci	superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
3458c2ecf20Sopenharmony_ci	dir |= BIT(offset);
3468c2ecf20Sopenharmony_ci	superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int err;
3568c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
3578c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
3588c2ecf20Sopenharmony_ci	u8 data_out;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
3618c2ecf20Sopenharmony_ci	if (err)
3628c2ecf20Sopenharmony_ci		return;
3638c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
3668c2ecf20Sopenharmony_ci	if (value)
3678c2ecf20Sopenharmony_ci		data_out |= BIT(offset);
3688c2ecf20Sopenharmony_ci	else
3698c2ecf20Sopenharmony_ci		data_out &= ~BIT(offset);
3708c2ecf20Sopenharmony_ci	superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset,
3768c2ecf20Sopenharmony_ci				  unsigned long config)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int err;
3798c2ecf20Sopenharmony_ci	enum pin_config_param param = pinconf_to_config_param(config);
3808c2ecf20Sopenharmony_ci	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);
3818c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = bank->data->sio;
3828c2ecf20Sopenharmony_ci	u8 data;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (param != PIN_CONFIG_DRIVE_OPEN_DRAIN &&
3858c2ecf20Sopenharmony_ci	    param != PIN_CONFIG_DRIVE_PUSH_PULL)
3868c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	err = superio_enter(sio->addr);
3898c2ecf20Sopenharmony_ci	if (err)
3908c2ecf20Sopenharmony_ci		return err;
3918c2ecf20Sopenharmony_ci	superio_select(sio->addr, SIO_LD_GPIO);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	data = superio_inb(sio->addr, gpio_out_mode(bank->regbase));
3948c2ecf20Sopenharmony_ci	if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)
3958c2ecf20Sopenharmony_ci		data &= ~BIT(offset);
3968c2ecf20Sopenharmony_ci	else
3978c2ecf20Sopenharmony_ci		data |= BIT(offset);
3988c2ecf20Sopenharmony_ci	superio_outb(sio->addr, gpio_out_mode(bank->regbase), data);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	superio_exit(sio->addr);
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/*
4058c2ecf20Sopenharmony_ci * Platform device and driver.
4068c2ecf20Sopenharmony_ci */
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int f7188x_gpio_probe(struct platform_device *pdev)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	int err;
4118c2ecf20Sopenharmony_ci	int i;
4128c2ecf20Sopenharmony_ci	struct f7188x_sio *sio = dev_get_platdata(&pdev->dev);
4138c2ecf20Sopenharmony_ci	struct f7188x_gpio_data *data;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
4168c2ecf20Sopenharmony_ci	if (!data)
4178c2ecf20Sopenharmony_ci		return -ENOMEM;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (sio->type) {
4208c2ecf20Sopenharmony_ci	case f71869:
4218c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f71869_gpio_bank);
4228c2ecf20Sopenharmony_ci		data->bank = f71869_gpio_bank;
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	case f71869a:
4258c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f71869a_gpio_bank);
4268c2ecf20Sopenharmony_ci		data->bank = f71869a_gpio_bank;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case f71882fg:
4298c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
4308c2ecf20Sopenharmony_ci		data->bank = f71882_gpio_bank;
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	case f71889a:
4338c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f71889a_gpio_bank);
4348c2ecf20Sopenharmony_ci		data->bank = f71889a_gpio_bank;
4358c2ecf20Sopenharmony_ci		break;
4368c2ecf20Sopenharmony_ci	case f71889f:
4378c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
4388c2ecf20Sopenharmony_ci		data->bank = f71889_gpio_bank;
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	case f81866:
4418c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f81866_gpio_bank);
4428c2ecf20Sopenharmony_ci		data->bank = f81866_gpio_bank;
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	case  f81804:
4458c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);
4468c2ecf20Sopenharmony_ci		data->bank = f81804_gpio_bank;
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	case f81865:
4498c2ecf20Sopenharmony_ci		data->nr_bank = ARRAY_SIZE(f81865_gpio_bank);
4508c2ecf20Sopenharmony_ci		data->bank = f81865_gpio_bank;
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	default:
4538c2ecf20Sopenharmony_ci		return -ENODEV;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	data->sio = sio;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* For each GPIO bank, register a GPIO chip. */
4608c2ecf20Sopenharmony_ci	for (i = 0; i < data->nr_bank; i++) {
4618c2ecf20Sopenharmony_ci		struct f7188x_gpio_bank *bank = &data->bank[i];
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		bank->chip.parent = &pdev->dev;
4648c2ecf20Sopenharmony_ci		bank->data = data;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank);
4678c2ecf20Sopenharmony_ci		if (err) {
4688c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
4698c2ecf20Sopenharmony_ci				"Failed to register gpiochip %d: %d\n",
4708c2ecf20Sopenharmony_ci				i, err);
4718c2ecf20Sopenharmony_ci			return err;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int __init f7188x_find(int addr, struct f7188x_sio *sio)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	int err;
4818c2ecf20Sopenharmony_ci	u16 devid;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	err = superio_enter(addr);
4848c2ecf20Sopenharmony_ci	if (err)
4858c2ecf20Sopenharmony_ci		return err;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	err = -ENODEV;
4888c2ecf20Sopenharmony_ci	devid = superio_inw(addr, SIO_MANID);
4898c2ecf20Sopenharmony_ci	if (devid != SIO_FINTEK_ID) {
4908c2ecf20Sopenharmony_ci		pr_debug(DRVNAME ": Not a Fintek device at 0x%08x\n", addr);
4918c2ecf20Sopenharmony_ci		goto err;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	devid = superio_inw(addr, SIO_DEVID);
4958c2ecf20Sopenharmony_ci	switch (devid) {
4968c2ecf20Sopenharmony_ci	case SIO_F71869_ID:
4978c2ecf20Sopenharmony_ci		sio->type = f71869;
4988c2ecf20Sopenharmony_ci		break;
4998c2ecf20Sopenharmony_ci	case SIO_F71869A_ID:
5008c2ecf20Sopenharmony_ci		sio->type = f71869a;
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci	case SIO_F71882_ID:
5038c2ecf20Sopenharmony_ci		sio->type = f71882fg;
5048c2ecf20Sopenharmony_ci		break;
5058c2ecf20Sopenharmony_ci	case SIO_F71889A_ID:
5068c2ecf20Sopenharmony_ci		sio->type = f71889a;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	case SIO_F71889_ID:
5098c2ecf20Sopenharmony_ci		sio->type = f71889f;
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	case SIO_F81866_ID:
5128c2ecf20Sopenharmony_ci		sio->type = f81866;
5138c2ecf20Sopenharmony_ci		break;
5148c2ecf20Sopenharmony_ci	case SIO_F81804_ID:
5158c2ecf20Sopenharmony_ci		sio->type = f81804;
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci	case SIO_F81865_ID:
5188c2ecf20Sopenharmony_ci		sio->type = f81865;
5198c2ecf20Sopenharmony_ci		break;
5208c2ecf20Sopenharmony_ci	default:
5218c2ecf20Sopenharmony_ci		pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
5228c2ecf20Sopenharmony_ci		goto err;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	sio->addr = addr;
5258c2ecf20Sopenharmony_ci	err = 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	pr_info(DRVNAME ": Found %s at %#x, revision %d\n",
5288c2ecf20Sopenharmony_ci		f7188x_names[sio->type],
5298c2ecf20Sopenharmony_ci		(unsigned int) addr,
5308c2ecf20Sopenharmony_ci		(int) superio_inb(addr, SIO_DEVREV));
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cierr:
5338c2ecf20Sopenharmony_ci	superio_exit(addr);
5348c2ecf20Sopenharmony_ci	return err;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic struct platform_device *f7188x_gpio_pdev;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int __init
5408c2ecf20Sopenharmony_cif7188x_gpio_device_add(const struct f7188x_sio *sio)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	int err;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);
5458c2ecf20Sopenharmony_ci	if (!f7188x_gpio_pdev)
5468c2ecf20Sopenharmony_ci		return -ENOMEM;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	err = platform_device_add_data(f7188x_gpio_pdev,
5498c2ecf20Sopenharmony_ci				       sio, sizeof(*sio));
5508c2ecf20Sopenharmony_ci	if (err) {
5518c2ecf20Sopenharmony_ci		pr_err(DRVNAME "Platform data allocation failed\n");
5528c2ecf20Sopenharmony_ci		goto err;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	err = platform_device_add(f7188x_gpio_pdev);
5568c2ecf20Sopenharmony_ci	if (err) {
5578c2ecf20Sopenharmony_ci		pr_err(DRVNAME "Device addition failed\n");
5588c2ecf20Sopenharmony_ci		goto err;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return 0;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cierr:
5648c2ecf20Sopenharmony_ci	platform_device_put(f7188x_gpio_pdev);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return err;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/*
5708c2ecf20Sopenharmony_ci * Try to match a supported Fintek device by reading the (hard-wired)
5718c2ecf20Sopenharmony_ci * configuration I/O ports. If available, then register both the platform
5728c2ecf20Sopenharmony_ci * device and driver to support the GPIOs.
5738c2ecf20Sopenharmony_ci */
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic struct platform_driver f7188x_gpio_driver = {
5768c2ecf20Sopenharmony_ci	.driver = {
5778c2ecf20Sopenharmony_ci		.name	= DRVNAME,
5788c2ecf20Sopenharmony_ci	},
5798c2ecf20Sopenharmony_ci	.probe		= f7188x_gpio_probe,
5808c2ecf20Sopenharmony_ci};
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic int __init f7188x_gpio_init(void)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	int err;
5858c2ecf20Sopenharmony_ci	struct f7188x_sio sio;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (f7188x_find(0x2e, &sio) &&
5888c2ecf20Sopenharmony_ci	    f7188x_find(0x4e, &sio))
5898c2ecf20Sopenharmony_ci		return -ENODEV;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	err = platform_driver_register(&f7188x_gpio_driver);
5928c2ecf20Sopenharmony_ci	if (!err) {
5938c2ecf20Sopenharmony_ci		err = f7188x_gpio_device_add(&sio);
5948c2ecf20Sopenharmony_ci		if (err)
5958c2ecf20Sopenharmony_ci			platform_driver_unregister(&f7188x_gpio_driver);
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return err;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_cisubsys_initcall(f7188x_gpio_init);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic void __exit f7188x_gpio_exit(void)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	platform_device_unregister(f7188x_gpio_pdev);
6058c2ecf20Sopenharmony_ci	platform_driver_unregister(&f7188x_gpio_driver);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_cimodule_exit(f7188x_gpio_exit);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG, F71889A, F71889F and F81866");
6108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
6118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
612