162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  GPIO interface for IT87xx Super I/O chips
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Author: Diego Elio Pettenò <flameeyes@flameeyes.eu>
662306a36Sopenharmony_ci *  Copyright (c) 2017 Google, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  Based on it87_wdt.c     by Oliver Schuster
962306a36Sopenharmony_ci *           gpio-it8761e.c by Denis Turischev
1062306a36Sopenharmony_ci *           gpio-stmpe.c   by Rabin Vincent
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/errno.h>
2062306a36Sopenharmony_ci#include <linux/ioport.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/gpio/driver.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Chip Id numbers */
2562306a36Sopenharmony_ci#define NO_DEV_ID	0xffff
2662306a36Sopenharmony_ci#define IT8613_ID	0x8613
2762306a36Sopenharmony_ci#define IT8620_ID	0x8620
2862306a36Sopenharmony_ci#define IT8628_ID	0x8628
2962306a36Sopenharmony_ci#define IT8718_ID       0x8718
3062306a36Sopenharmony_ci#define IT8728_ID	0x8728
3162306a36Sopenharmony_ci#define IT8732_ID	0x8732
3262306a36Sopenharmony_ci#define IT8761_ID	0x8761
3362306a36Sopenharmony_ci#define IT8772_ID	0x8772
3462306a36Sopenharmony_ci#define IT8786_ID	0x8786
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* IO Ports */
3762306a36Sopenharmony_ci#define REG		0x2e
3862306a36Sopenharmony_ci#define VAL		0x2f
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Logical device Numbers LDN */
4162306a36Sopenharmony_ci#define GPIO		0x07
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Configuration Registers and Functions */
4462306a36Sopenharmony_ci#define LDNREG		0x07
4562306a36Sopenharmony_ci#define CHIPID		0x20
4662306a36Sopenharmony_ci#define CHIPREV		0x22
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/**
4962306a36Sopenharmony_ci * struct it87_gpio - it87-specific GPIO chip
5062306a36Sopenharmony_ci * @chip: the underlying gpio_chip structure
5162306a36Sopenharmony_ci * @lock: a lock to avoid races between operations
5262306a36Sopenharmony_ci * @io_base: base address for gpio ports
5362306a36Sopenharmony_ci * @io_size: size of the port rage starting from io_base.
5462306a36Sopenharmony_ci * @output_base: Super I/O register address for Output Enable register
5562306a36Sopenharmony_ci * @simple_base: Super I/O 'Simple I/O' Enable register
5662306a36Sopenharmony_ci * @simple_size: Super IO 'Simple I/O' Enable register size; this is
5762306a36Sopenharmony_ci *	required because IT87xx chips might only provide Simple I/O
5862306a36Sopenharmony_ci *	switches on a subset of lines, whereas the others keep the
5962306a36Sopenharmony_ci *	same status all time.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistruct it87_gpio {
6262306a36Sopenharmony_ci	struct gpio_chip chip;
6362306a36Sopenharmony_ci	spinlock_t lock;
6462306a36Sopenharmony_ci	u16 io_base;
6562306a36Sopenharmony_ci	u16 io_size;
6662306a36Sopenharmony_ci	u8 output_base;
6762306a36Sopenharmony_ci	u8 simple_base;
6862306a36Sopenharmony_ci	u8 simple_size;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct it87_gpio it87_gpio_chip = {
7262306a36Sopenharmony_ci	.lock = __SPIN_LOCK_UNLOCKED(it87_gpio_chip.lock),
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* Superio chip access functions; copied from wdt_it87 */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic inline int superio_enter(void)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * Try to reserve REG and REG + 1 for exclusive access.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	if (!request_muxed_region(REG, 2, KBUILD_MODNAME))
8362306a36Sopenharmony_ci		return -EBUSY;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	outb(0x87, REG);
8662306a36Sopenharmony_ci	outb(0x01, REG);
8762306a36Sopenharmony_ci	outb(0x55, REG);
8862306a36Sopenharmony_ci	outb(0x55, REG);
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline void superio_exit(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	outb(0x02, REG);
9562306a36Sopenharmony_ci	outb(0x02, VAL);
9662306a36Sopenharmony_ci	release_region(REG, 2);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic inline void superio_select(int ldn)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	outb(LDNREG, REG);
10262306a36Sopenharmony_ci	outb(ldn, VAL);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic inline int superio_inb(int reg)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	outb(reg, REG);
10862306a36Sopenharmony_ci	return inb(VAL);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline void superio_outb(int val, int reg)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	outb(reg, REG);
11462306a36Sopenharmony_ci	outb(val, VAL);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic inline int superio_inw(int reg)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int val;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	outb(reg++, REG);
12262306a36Sopenharmony_ci	val = inb(VAL) << 8;
12362306a36Sopenharmony_ci	outb(reg, REG);
12462306a36Sopenharmony_ci	val |= inb(VAL);
12562306a36Sopenharmony_ci	return val;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline void superio_set_mask(int mask, int reg)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	u8 curr_val = superio_inb(reg);
13162306a36Sopenharmony_ci	u8 new_val = curr_val | mask;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (curr_val != new_val)
13462306a36Sopenharmony_ci		superio_outb(new_val, reg);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void superio_clear_mask(int mask, int reg)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u8 curr_val = superio_inb(reg);
14062306a36Sopenharmony_ci	u8 new_val = curr_val & ~mask;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (curr_val != new_val)
14362306a36Sopenharmony_ci		superio_outb(new_val, reg);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int it87_gpio_request(struct gpio_chip *chip, unsigned gpio_num)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	u8 mask, group;
14962306a36Sopenharmony_ci	int rc = 0;
15062306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	mask = 1 << (gpio_num % 8);
15362306a36Sopenharmony_ci	group = (gpio_num / 8);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	spin_lock(&it87_gpio->lock);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	rc = superio_enter();
15862306a36Sopenharmony_ci	if (rc)
15962306a36Sopenharmony_ci		goto exit;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* not all the IT87xx chips support Simple I/O and not all of
16262306a36Sopenharmony_ci	 * them allow all the lines to be set/unset to Simple I/O.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	if (group < it87_gpio->simple_size)
16562306a36Sopenharmony_ci		superio_set_mask(mask, group + it87_gpio->simple_base);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* clear output enable, setting the pin to input, as all the
16862306a36Sopenharmony_ci	 * newly-exported GPIO interfaces are set to input.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	superio_clear_mask(mask, group + it87_gpio->output_base);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	superio_exit();
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciexit:
17562306a36Sopenharmony_ci	spin_unlock(&it87_gpio->lock);
17662306a36Sopenharmony_ci	return rc;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int it87_gpio_get(struct gpio_chip *chip, unsigned gpio_num)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	u16 reg;
18262306a36Sopenharmony_ci	u8 mask;
18362306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	mask = 1 << (gpio_num % 8);
18662306a36Sopenharmony_ci	reg = (gpio_num / 8) + it87_gpio->io_base;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return !!(inb(reg) & mask);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int it87_gpio_direction_in(struct gpio_chip *chip, unsigned gpio_num)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	u8 mask, group;
19462306a36Sopenharmony_ci	int rc = 0;
19562306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	mask = 1 << (gpio_num % 8);
19862306a36Sopenharmony_ci	group = (gpio_num / 8);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	spin_lock(&it87_gpio->lock);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	rc = superio_enter();
20362306a36Sopenharmony_ci	if (rc)
20462306a36Sopenharmony_ci		goto exit;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* clear the output enable bit */
20762306a36Sopenharmony_ci	superio_clear_mask(mask, group + it87_gpio->output_base);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	superio_exit();
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciexit:
21262306a36Sopenharmony_ci	spin_unlock(&it87_gpio->lock);
21362306a36Sopenharmony_ci	return rc;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void it87_gpio_set(struct gpio_chip *chip,
21762306a36Sopenharmony_ci			  unsigned gpio_num, int val)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	u8 mask, curr_vals;
22062306a36Sopenharmony_ci	u16 reg;
22162306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	mask = 1 << (gpio_num % 8);
22462306a36Sopenharmony_ci	reg = (gpio_num / 8) + it87_gpio->io_base;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	curr_vals = inb(reg);
22762306a36Sopenharmony_ci	if (val)
22862306a36Sopenharmony_ci		outb(curr_vals | mask, reg);
22962306a36Sopenharmony_ci	else
23062306a36Sopenharmony_ci		outb(curr_vals & ~mask, reg);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int it87_gpio_direction_out(struct gpio_chip *chip,
23462306a36Sopenharmony_ci				   unsigned gpio_num, int val)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	u8 mask, group;
23762306a36Sopenharmony_ci	int rc = 0;
23862306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	mask = 1 << (gpio_num % 8);
24162306a36Sopenharmony_ci	group = (gpio_num / 8);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	spin_lock(&it87_gpio->lock);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	rc = superio_enter();
24662306a36Sopenharmony_ci	if (rc)
24762306a36Sopenharmony_ci		goto exit;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* set the output enable bit */
25062306a36Sopenharmony_ci	superio_set_mask(mask, group + it87_gpio->output_base);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	it87_gpio_set(chip, gpio_num, val);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	superio_exit();
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciexit:
25762306a36Sopenharmony_ci	spin_unlock(&it87_gpio->lock);
25862306a36Sopenharmony_ci	return rc;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct gpio_chip it87_template_chip = {
26262306a36Sopenharmony_ci	.label			= KBUILD_MODNAME,
26362306a36Sopenharmony_ci	.owner			= THIS_MODULE,
26462306a36Sopenharmony_ci	.request		= it87_gpio_request,
26562306a36Sopenharmony_ci	.get			= it87_gpio_get,
26662306a36Sopenharmony_ci	.direction_input	= it87_gpio_direction_in,
26762306a36Sopenharmony_ci	.set			= it87_gpio_set,
26862306a36Sopenharmony_ci	.direction_output	= it87_gpio_direction_out,
26962306a36Sopenharmony_ci	.base			= -1
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int __init it87_gpio_init(void)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	int rc = 0, i;
27562306a36Sopenharmony_ci	u16 chip_type;
27662306a36Sopenharmony_ci	u8 chip_rev, gpio_ba_reg;
27762306a36Sopenharmony_ci	char *labels, **labels_table;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = &it87_gpio_chip;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rc = superio_enter();
28262306a36Sopenharmony_ci	if (rc)
28362306a36Sopenharmony_ci		return rc;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	chip_type = superio_inw(CHIPID);
28662306a36Sopenharmony_ci	chip_rev  = superio_inb(CHIPREV) & 0x0f;
28762306a36Sopenharmony_ci	superio_exit();
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	it87_gpio->chip = it87_template_chip;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	switch (chip_type) {
29262306a36Sopenharmony_ci	case IT8613_ID:
29362306a36Sopenharmony_ci		gpio_ba_reg = 0x62;
29462306a36Sopenharmony_ci		it87_gpio->io_size = 8;  /* it8613 only needs 6, use 8 for alignment */
29562306a36Sopenharmony_ci		it87_gpio->output_base = 0xc8;
29662306a36Sopenharmony_ci		it87_gpio->simple_base = 0xc0;
29762306a36Sopenharmony_ci		it87_gpio->simple_size = 6;
29862306a36Sopenharmony_ci		it87_gpio->chip.ngpio = 64;  /* has 48, use 64 for convenient calc */
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	case IT8620_ID:
30162306a36Sopenharmony_ci	case IT8628_ID:
30262306a36Sopenharmony_ci		gpio_ba_reg = 0x62;
30362306a36Sopenharmony_ci		it87_gpio->io_size = 11;
30462306a36Sopenharmony_ci		it87_gpio->output_base = 0xc8;
30562306a36Sopenharmony_ci		it87_gpio->simple_size = 0;
30662306a36Sopenharmony_ci		it87_gpio->chip.ngpio = 64;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case IT8718_ID:
30962306a36Sopenharmony_ci	case IT8728_ID:
31062306a36Sopenharmony_ci	case IT8732_ID:
31162306a36Sopenharmony_ci	case IT8772_ID:
31262306a36Sopenharmony_ci	case IT8786_ID:
31362306a36Sopenharmony_ci		gpio_ba_reg = 0x62;
31462306a36Sopenharmony_ci		it87_gpio->io_size = 8;
31562306a36Sopenharmony_ci		it87_gpio->output_base = 0xc8;
31662306a36Sopenharmony_ci		it87_gpio->simple_base = 0xc0;
31762306a36Sopenharmony_ci		it87_gpio->simple_size = 5;
31862306a36Sopenharmony_ci		it87_gpio->chip.ngpio = 64;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case IT8761_ID:
32162306a36Sopenharmony_ci		gpio_ba_reg = 0x60;
32262306a36Sopenharmony_ci		it87_gpio->io_size = 4;
32362306a36Sopenharmony_ci		it87_gpio->output_base = 0xf0;
32462306a36Sopenharmony_ci		it87_gpio->simple_size = 0;
32562306a36Sopenharmony_ci		it87_gpio->chip.ngpio = 16;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case NO_DEV_ID:
32862306a36Sopenharmony_ci		pr_err("no device\n");
32962306a36Sopenharmony_ci		return -ENODEV;
33062306a36Sopenharmony_ci	default:
33162306a36Sopenharmony_ci		pr_err("Unknown Chip found, Chip %04x Revision %x\n",
33262306a36Sopenharmony_ci		       chip_type, chip_rev);
33362306a36Sopenharmony_ci		return -ENODEV;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	rc = superio_enter();
33762306a36Sopenharmony_ci	if (rc)
33862306a36Sopenharmony_ci		return rc;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	superio_select(GPIO);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* fetch GPIO base address */
34362306a36Sopenharmony_ci	it87_gpio->io_base = superio_inw(gpio_ba_reg);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	superio_exit();
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	pr_info("Found Chip IT%04x rev %x. %u GPIO lines starting at %04xh\n",
34862306a36Sopenharmony_ci		chip_type, chip_rev, it87_gpio->chip.ngpio,
34962306a36Sopenharmony_ci		it87_gpio->io_base);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!request_region(it87_gpio->io_base, it87_gpio->io_size,
35262306a36Sopenharmony_ci							KBUILD_MODNAME))
35362306a36Sopenharmony_ci		return -EBUSY;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Set up aliases for the GPIO connection.
35662306a36Sopenharmony_ci	 *
35762306a36Sopenharmony_ci	 * ITE documentation for recent chips such as the IT8728F
35862306a36Sopenharmony_ci	 * refers to the GPIO lines as GPxy, with a coordinates system
35962306a36Sopenharmony_ci	 * where x is the GPIO group (starting from 1) and y is the
36062306a36Sopenharmony_ci	 * bit within the group.
36162306a36Sopenharmony_ci	 *
36262306a36Sopenharmony_ci	 * By creating these aliases, we make it easier to understand
36362306a36Sopenharmony_ci	 * to which GPIO pin we're referring to.
36462306a36Sopenharmony_ci	 */
36562306a36Sopenharmony_ci	labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
36662306a36Sopenharmony_ci								GFP_KERNEL);
36762306a36Sopenharmony_ci	labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
36862306a36Sopenharmony_ci								GFP_KERNEL);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (!labels || !labels_table) {
37162306a36Sopenharmony_ci		rc = -ENOMEM;
37262306a36Sopenharmony_ci		goto labels_free;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	for (i = 0; i < it87_gpio->chip.ngpio; i++) {
37662306a36Sopenharmony_ci		char *label = &labels[i * sizeof("it87_gpXY")];
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		sprintf(label, "it87_gp%u%u", 1+(i/8), i%8);
37962306a36Sopenharmony_ci		labels_table[i] = label;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	it87_gpio->chip.names = (const char *const*)labels_table;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	rc = gpiochip_add_data(&it87_gpio->chip, it87_gpio);
38562306a36Sopenharmony_ci	if (rc)
38662306a36Sopenharmony_ci		goto labels_free;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cilabels_free:
39162306a36Sopenharmony_ci	kfree(labels_table);
39262306a36Sopenharmony_ci	kfree(labels);
39362306a36Sopenharmony_ci	release_region(it87_gpio->io_base, it87_gpio->io_size);
39462306a36Sopenharmony_ci	return rc;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void __exit it87_gpio_exit(void)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct it87_gpio *it87_gpio = &it87_gpio_chip;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	gpiochip_remove(&it87_gpio->chip);
40262306a36Sopenharmony_ci	release_region(it87_gpio->io_base, it87_gpio->io_size);
40362306a36Sopenharmony_ci	kfree(it87_gpio->chip.names[0]);
40462306a36Sopenharmony_ci	kfree(it87_gpio->chip.names);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cimodule_init(it87_gpio_init);
40862306a36Sopenharmony_cimodule_exit(it87_gpio_exit);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ciMODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>");
41162306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
41262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
413