brintos

brintos / linux-shallow public Read only

0
0
Text · 16.1 KiB · 3875fd9 Raw
667 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * GPIO driver for Fintek and Nuvoton Super-I/O chips4 *5 * Copyright (C) 2010-2013 LaCie6 *7 * Author: Simon Guinot <simon.guinot@sequanux.org>8 */9 10#define DRVNAME "gpio-f7188x"11#define pr_fmt(fmt) DRVNAME ": " fmt12 13#include <linux/module.h>14#include <linux/init.h>15#include <linux/platform_device.h>16#include <linux/io.h>17#include <linux/gpio/driver.h>18#include <linux/bitops.h>19 20/*21 * Super-I/O registers22 */23#define SIO_LDSEL		0x07	/* Logical device select */24#define SIO_DEVID		0x20	/* Device ID (2 bytes) */25 26#define SIO_UNLOCK_KEY		0x87	/* Key to enable Super-I/O */27#define SIO_LOCK_KEY		0xAA	/* Key to disable Super-I/O */28 29/*30 * Fintek devices.31 */32#define SIO_FINTEK_DEVREV	0x22	/* Fintek Device revision */33#define SIO_FINTEK_MANID	0x23    /* Fintek ID (2 bytes) */34 35#define SIO_FINTEK_ID		0x1934  /* Manufacturer ID */36 37#define SIO_F71869_ID		0x0814	/* F71869 chipset ID */38#define SIO_F71869A_ID		0x1007	/* F71869A chipset ID */39#define SIO_F71882_ID		0x0541	/* F71882 chipset ID */40#define SIO_F71889_ID		0x0909	/* F71889 chipset ID */41#define SIO_F71889A_ID		0x1005	/* F71889A chipset ID */42#define SIO_F81866_ID		0x1010	/* F81866 chipset ID */43#define SIO_F81804_ID		0x1502  /* F81804 chipset ID, same for F81966 */44#define SIO_F81865_ID		0x0704	/* F81865 chipset ID */45 46#define SIO_LD_GPIO_FINTEK	0x06	/* GPIO logical device */47 48/*49 * Nuvoton devices.50 */51#define SIO_NCT6126D_ID		0xD283  /* NCT6126D chipset ID */52 53#define SIO_LD_GPIO_NUVOTON	0x07	/* GPIO logical device */54 55 56enum chips {57	f71869,58	f71869a,59	f71882fg,60	f71889a,61	f71889f,62	f81866,63	f81804,64	f81865,65	nct6126d,66};67 68static const char * const f7188x_names[] = {69	"f71869",70	"f71869a",71	"f71882fg",72	"f71889a",73	"f71889f",74	"f81866",75	"f81804",76	"f81865",77	"nct6126d",78};79 80struct f7188x_sio {81	int addr;82	int device;83	enum chips type;84};85 86struct f7188x_gpio_bank {87	struct gpio_chip chip;88	unsigned int regbase;89	struct f7188x_gpio_data *data;90};91 92struct f7188x_gpio_data {93	struct f7188x_sio *sio;94	int nr_bank;95	struct f7188x_gpio_bank *bank;96};97 98/*99 * Super-I/O functions.100 */101 102static inline int superio_inb(int base, int reg)103{104	outb(reg, base);105	return inb(base + 1);106}107 108static int superio_inw(int base, int reg)109{110	int val;111 112	outb(reg++, base);113	val = inb(base + 1) << 8;114	outb(reg, base);115	val |= inb(base + 1);116 117	return val;118}119 120static inline void superio_outb(int base, int reg, int val)121{122	outb(reg, base);123	outb(val, base + 1);124}125 126static inline int superio_enter(int base)127{128	/* Don't step on other drivers' I/O space by accident. */129	if (!request_muxed_region(base, 2, DRVNAME)) {130		pr_err("I/O address 0x%04x already in use\n", base);131		return -EBUSY;132	}133 134	/* According to the datasheet the key must be send twice. */135	outb(SIO_UNLOCK_KEY, base);136	outb(SIO_UNLOCK_KEY, base);137 138	return 0;139}140 141static inline void superio_select(int base, int ld)142{143	outb(SIO_LDSEL, base);144	outb(ld, base + 1);145}146 147static inline void superio_exit(int base)148{149	outb(SIO_LOCK_KEY, base);150	release_region(base, 2);151}152 153/*154 * GPIO chip.155 */156 157static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset);158static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);159static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);160static int f7188x_gpio_direction_out(struct gpio_chip *chip,161				     unsigned offset, int value);162static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);163static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset,164				  unsigned long config);165 166#define F7188X_GPIO_BANK(_ngpio, _regbase, _label)			\167	{								\168		.chip = {						\169			.label            = _label,			\170			.owner            = THIS_MODULE,		\171			.get_direction    = f7188x_gpio_get_direction,	\172			.direction_input  = f7188x_gpio_direction_in,	\173			.get              = f7188x_gpio_get,		\174			.direction_output = f7188x_gpio_direction_out,	\175			.set              = f7188x_gpio_set,		\176			.set_config	  = f7188x_gpio_set_config,	\177			.base             = -1,				\178			.ngpio            = _ngpio,			\179			.can_sleep        = true,			\180		},							\181		.regbase = _regbase,					\182	}183 184#define f7188x_gpio_dir(base) ((base) + 0)185#define f7188x_gpio_data_out(base) ((base) + 1)186#define f7188x_gpio_data_in(base) ((base) + 2)187/* Output mode register (0:open drain 1:push-pull). */188#define f7188x_gpio_out_mode(base) ((base) + 3)189 190#define f7188x_gpio_dir_invert(type)	((type) == nct6126d)191#define f7188x_gpio_data_single(type)	((type) == nct6126d)192 193static struct f7188x_gpio_bank f71869_gpio_bank[] = {194	F7188X_GPIO_BANK(6, 0xF0, DRVNAME "-0"),195	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),196	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),197	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),198	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),199	F7188X_GPIO_BANK(5, 0xA0, DRVNAME "-5"),200	F7188X_GPIO_BANK(6, 0x90, DRVNAME "-6"),201};202 203static struct f7188x_gpio_bank f71869a_gpio_bank[] = {204	F7188X_GPIO_BANK(6, 0xF0, DRVNAME "-0"),205	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),206	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),207	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),208	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),209	F7188X_GPIO_BANK(5, 0xA0, DRVNAME "-5"),210	F7188X_GPIO_BANK(8, 0x90, DRVNAME "-6"),211	F7188X_GPIO_BANK(8, 0x80, DRVNAME "-7"),212};213 214static struct f7188x_gpio_bank f71882_gpio_bank[] = {215	F7188X_GPIO_BANK(8, 0xF0, DRVNAME "-0"),216	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),217	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),218	F7188X_GPIO_BANK(4, 0xC0, DRVNAME "-3"),219	F7188X_GPIO_BANK(4, 0xB0, DRVNAME "-4"),220};221 222static struct f7188x_gpio_bank f71889a_gpio_bank[] = {223	F7188X_GPIO_BANK(7, 0xF0, DRVNAME "-0"),224	F7188X_GPIO_BANK(7, 0xE0, DRVNAME "-1"),225	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),226	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),227	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),228	F7188X_GPIO_BANK(5, 0xA0, DRVNAME "-5"),229	F7188X_GPIO_BANK(8, 0x90, DRVNAME "-6"),230	F7188X_GPIO_BANK(8, 0x80, DRVNAME "-7"),231};232 233static struct f7188x_gpio_bank f71889_gpio_bank[] = {234	F7188X_GPIO_BANK(7, 0xF0, DRVNAME "-0"),235	F7188X_GPIO_BANK(7, 0xE0, DRVNAME "-1"),236	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),237	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),238	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),239	F7188X_GPIO_BANK(5, 0xA0, DRVNAME "-5"),240	F7188X_GPIO_BANK(8, 0x90, DRVNAME "-6"),241	F7188X_GPIO_BANK(8, 0x80, DRVNAME "-7"),242};243 244static struct f7188x_gpio_bank f81866_gpio_bank[] = {245	F7188X_GPIO_BANK(8, 0xF0, DRVNAME "-0"),246	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),247	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),248	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),249	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),250	F7188X_GPIO_BANK(8, 0xA0, DRVNAME "-5"),251	F7188X_GPIO_BANK(8, 0x90, DRVNAME "-6"),252	F7188X_GPIO_BANK(8, 0x80, DRVNAME "-7"),253	F7188X_GPIO_BANK(8, 0x88, DRVNAME "-8"),254};255 256 257static struct f7188x_gpio_bank f81804_gpio_bank[] = {258	F7188X_GPIO_BANK(8, 0xF0, DRVNAME "-0"),259	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),260	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),261	F7188X_GPIO_BANK(8, 0xA0, DRVNAME "-3"),262	F7188X_GPIO_BANK(8, 0x90, DRVNAME "-4"),263	F7188X_GPIO_BANK(8, 0x80, DRVNAME "-5"),264	F7188X_GPIO_BANK(8, 0x98, DRVNAME "-6"),265};266 267static struct f7188x_gpio_bank f81865_gpio_bank[] = {268	F7188X_GPIO_BANK(8, 0xF0, DRVNAME "-0"),269	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-1"),270	F7188X_GPIO_BANK(8, 0xD0, DRVNAME "-2"),271	F7188X_GPIO_BANK(8, 0xC0, DRVNAME "-3"),272	F7188X_GPIO_BANK(8, 0xB0, DRVNAME "-4"),273	F7188X_GPIO_BANK(8, 0xA0, DRVNAME "-5"),274	F7188X_GPIO_BANK(5, 0x90, DRVNAME "-6"),275};276 277static struct f7188x_gpio_bank nct6126d_gpio_bank[] = {278	F7188X_GPIO_BANK(8, 0xE0, DRVNAME "-0"),279	F7188X_GPIO_BANK(8, 0xE4, DRVNAME "-1"),280	F7188X_GPIO_BANK(8, 0xE8, DRVNAME "-2"),281	F7188X_GPIO_BANK(8, 0xEC, DRVNAME "-3"),282	F7188X_GPIO_BANK(8, 0xF0, DRVNAME "-4"),283	F7188X_GPIO_BANK(8, 0xF4, DRVNAME "-5"),284	F7188X_GPIO_BANK(8, 0xF8, DRVNAME "-6"),285	F7188X_GPIO_BANK(8, 0xFC, DRVNAME "-7"),286};287 288static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)289{290	int err;291	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);292	struct f7188x_sio *sio = bank->data->sio;293	u8 dir;294 295	err = superio_enter(sio->addr);296	if (err)297		return err;298	superio_select(sio->addr, sio->device);299 300	dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase));301 302	superio_exit(sio->addr);303 304	if (f7188x_gpio_dir_invert(sio->type))305		dir = ~dir;306 307	if (dir & BIT(offset))308		return GPIO_LINE_DIRECTION_OUT;309 310	return GPIO_LINE_DIRECTION_IN;311}312 313static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)314{315	int err;316	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);317	struct f7188x_sio *sio = bank->data->sio;318	u8 dir;319 320	err = superio_enter(sio->addr);321	if (err)322		return err;323	superio_select(sio->addr, sio->device);324 325	dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase));326 327	if (f7188x_gpio_dir_invert(sio->type))328		dir |= BIT(offset);329	else330		dir &= ~BIT(offset);331	superio_outb(sio->addr, f7188x_gpio_dir(bank->regbase), dir);332 333	superio_exit(sio->addr);334 335	return 0;336}337 338static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)339{340	int err;341	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);342	struct f7188x_sio *sio = bank->data->sio;343	u8 dir, data;344 345	err = superio_enter(sio->addr);346	if (err)347		return err;348	superio_select(sio->addr, sio->device);349 350	dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase));351	dir = !!(dir & BIT(offset));352	if (f7188x_gpio_data_single(sio->type) || dir)353		data = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase));354	else355		data = superio_inb(sio->addr, f7188x_gpio_data_in(bank->regbase));356 357	superio_exit(sio->addr);358 359	return !!(data & BIT(offset));360}361 362static int f7188x_gpio_direction_out(struct gpio_chip *chip,363				     unsigned offset, int value)364{365	int err;366	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);367	struct f7188x_sio *sio = bank->data->sio;368	u8 dir, data_out;369 370	err = superio_enter(sio->addr);371	if (err)372		return err;373	superio_select(sio->addr, sio->device);374 375	data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase));376	if (value)377		data_out |= BIT(offset);378	else379		data_out &= ~BIT(offset);380	superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out);381 382	dir = superio_inb(sio->addr, f7188x_gpio_dir(bank->regbase));383	if (f7188x_gpio_dir_invert(sio->type))384		dir &= ~BIT(offset);385	else386		dir |= BIT(offset);387	superio_outb(sio->addr, f7188x_gpio_dir(bank->regbase), dir);388 389	superio_exit(sio->addr);390 391	return 0;392}393 394static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)395{396	int err;397	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);398	struct f7188x_sio *sio = bank->data->sio;399	u8 data_out;400 401	err = superio_enter(sio->addr);402	if (err)403		return;404	superio_select(sio->addr, sio->device);405 406	data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase));407	if (value)408		data_out |= BIT(offset);409	else410		data_out &= ~BIT(offset);411	superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out);412 413	superio_exit(sio->addr);414}415 416static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset,417				  unsigned long config)418{419	int err;420	enum pin_config_param param = pinconf_to_config_param(config);421	struct f7188x_gpio_bank *bank = gpiochip_get_data(chip);422	struct f7188x_sio *sio = bank->data->sio;423	u8 data;424 425	if (param != PIN_CONFIG_DRIVE_OPEN_DRAIN &&426	    param != PIN_CONFIG_DRIVE_PUSH_PULL)427		return -ENOTSUPP;428 429	err = superio_enter(sio->addr);430	if (err)431		return err;432	superio_select(sio->addr, sio->device);433 434	data = superio_inb(sio->addr, f7188x_gpio_out_mode(bank->regbase));435	if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN)436		data &= ~BIT(offset);437	else438		data |= BIT(offset);439	superio_outb(sio->addr, f7188x_gpio_out_mode(bank->regbase), data);440 441	superio_exit(sio->addr);442	return 0;443}444 445/*446 * Platform device and driver.447 */448 449static int f7188x_gpio_probe(struct platform_device *pdev)450{451	int err;452	int i;453	struct f7188x_sio *sio = dev_get_platdata(&pdev->dev);454	struct f7188x_gpio_data *data;455 456	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);457	if (!data)458		return -ENOMEM;459 460	switch (sio->type) {461	case f71869:462		data->nr_bank = ARRAY_SIZE(f71869_gpio_bank);463		data->bank = f71869_gpio_bank;464		break;465	case f71869a:466		data->nr_bank = ARRAY_SIZE(f71869a_gpio_bank);467		data->bank = f71869a_gpio_bank;468		break;469	case f71882fg:470		data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);471		data->bank = f71882_gpio_bank;472		break;473	case f71889a:474		data->nr_bank = ARRAY_SIZE(f71889a_gpio_bank);475		data->bank = f71889a_gpio_bank;476		break;477	case f71889f:478		data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);479		data->bank = f71889_gpio_bank;480		break;481	case f81866:482		data->nr_bank = ARRAY_SIZE(f81866_gpio_bank);483		data->bank = f81866_gpio_bank;484		break;485	case  f81804:486		data->nr_bank = ARRAY_SIZE(f81804_gpio_bank);487		data->bank = f81804_gpio_bank;488		break;489	case f81865:490		data->nr_bank = ARRAY_SIZE(f81865_gpio_bank);491		data->bank = f81865_gpio_bank;492		break;493	case nct6126d:494		data->nr_bank = ARRAY_SIZE(nct6126d_gpio_bank);495		data->bank = nct6126d_gpio_bank;496		break;497	default:498		return -ENODEV;499	}500	data->sio = sio;501 502	platform_set_drvdata(pdev, data);503 504	/* For each GPIO bank, register a GPIO chip. */505	for (i = 0; i < data->nr_bank; i++) {506		struct f7188x_gpio_bank *bank = &data->bank[i];507 508		bank->chip.parent = &pdev->dev;509		bank->data = data;510 511		err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank);512		if (err) {513			dev_err(&pdev->dev,514				"Failed to register gpiochip %d: %d\n",515				i, err);516			return err;517		}518	}519 520	return 0;521}522 523static int __init f7188x_find(int addr, struct f7188x_sio *sio)524{525	int err;526	u16 devid;527	u16 manid;528 529	err = superio_enter(addr);530	if (err)531		return err;532 533	err = -ENODEV;534 535	sio->device = SIO_LD_GPIO_FINTEK;536	devid = superio_inw(addr, SIO_DEVID);537	switch (devid) {538	case SIO_F71869_ID:539		sio->type = f71869;540		break;541	case SIO_F71869A_ID:542		sio->type = f71869a;543		break;544	case SIO_F71882_ID:545		sio->type = f71882fg;546		break;547	case SIO_F71889A_ID:548		sio->type = f71889a;549		break;550	case SIO_F71889_ID:551		sio->type = f71889f;552		break;553	case SIO_F81866_ID:554		sio->type = f81866;555		break;556	case SIO_F81804_ID:557		sio->type = f81804;558		break;559	case SIO_F81865_ID:560		sio->type = f81865;561		break;562	case SIO_NCT6126D_ID:563		sio->device = SIO_LD_GPIO_NUVOTON;564		sio->type = nct6126d;565		break;566	default:567		pr_info("Unsupported Fintek device 0x%04x\n", devid);568		goto err;569	}570 571	/* double check manufacturer where possible */572	if (sio->type != nct6126d) {573		manid = superio_inw(addr, SIO_FINTEK_MANID);574		if (manid != SIO_FINTEK_ID) {575			pr_debug("Not a Fintek device at 0x%08x\n", addr);576			goto err;577		}578	}579 580	sio->addr = addr;581	err = 0;582 583	pr_info("Found %s at %#x\n", f7188x_names[sio->type], (unsigned int)addr);584	if (sio->type != nct6126d)585		pr_info("   revision %d\n", superio_inb(addr, SIO_FINTEK_DEVREV));586 587err:588	superio_exit(addr);589	return err;590}591 592static struct platform_device *f7188x_gpio_pdev;593 594static int __init595f7188x_gpio_device_add(const struct f7188x_sio *sio)596{597	int err;598 599	f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);600	if (!f7188x_gpio_pdev)601		return -ENOMEM;602 603	err = platform_device_add_data(f7188x_gpio_pdev,604				       sio, sizeof(*sio));605	if (err) {606		pr_err("Platform data allocation failed\n");607		goto err;608	}609 610	err = platform_device_add(f7188x_gpio_pdev);611	if (err) {612		pr_err("Device addition failed\n");613		goto err;614	}615 616	return 0;617 618err:619	platform_device_put(f7188x_gpio_pdev);620 621	return err;622}623 624/*625 * Try to match a supported Fintek device by reading the (hard-wired)626 * configuration I/O ports. If available, then register both the platform627 * device and driver to support the GPIOs.628 */629 630static struct platform_driver f7188x_gpio_driver = {631	.driver = {632		.name	= DRVNAME,633	},634	.probe		= f7188x_gpio_probe,635};636 637static int __init f7188x_gpio_init(void)638{639	int err;640	struct f7188x_sio sio;641 642	if (f7188x_find(0x2e, &sio) &&643	    f7188x_find(0x4e, &sio))644		return -ENODEV;645 646	err = platform_driver_register(&f7188x_gpio_driver);647	if (!err) {648		err = f7188x_gpio_device_add(&sio);649		if (err)650			platform_driver_unregister(&f7188x_gpio_driver);651	}652 653	return err;654}655subsys_initcall(f7188x_gpio_init);656 657static void __exit f7188x_gpio_exit(void)658{659	platform_device_unregister(f7188x_gpio_pdev);660	platform_driver_unregister(&f7188x_gpio_driver);661}662module_exit(f7188x_gpio_exit);663 664MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG, F71889A, F71889F and F81866");665MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");666MODULE_LICENSE("GPL");667