1170530Ssam// SPDX-License-Identifier: GPL-2.0-only
2178354Ssam/*
3170530Ssam *  GPIO interface for IT87xx Super I/O chips
4170530Ssam *
5170530Ssam *  Author: Diego Elio Petten�� <flameeyes@flameeyes.eu>
6170530Ssam *  Copyright (c) 2017 Google, Inc.
7170530Ssam *
8170530Ssam *  Based on it87_wdt.c     by Oliver Schuster
9170530Ssam *           gpio-it8761e.c by Denis Turischev
10170530Ssam *           gpio-stmpe.c   by Rabin Vincent
11170530Ssam */
12170530Ssam
13170530Ssam#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14170530Ssam
15170530Ssam#include <linux/init.h>
16170530Ssam#include <linux/kernel.h>
17170530Ssam#include <linux/module.h>
18170530Ssam#include <linux/io.h>
19170530Ssam#include <linux/errno.h>
20170530Ssam#include <linux/ioport.h>
21170530Ssam#include <linux/slab.h>
22170530Ssam#include <linux/gpio/driver.h>
23170530Ssam
24170530Ssam/* Chip Id numbers */
25170530Ssam#define NO_DEV_ID	0xffff
26170530Ssam#define IT8613_ID	0x8613
27170530Ssam#define IT8620_ID	0x8620
28170530Ssam#define IT8628_ID	0x8628
29170530Ssam#define IT8718_ID       0x8718
30170530Ssam#define IT8728_ID	0x8728
31170530Ssam#define IT8732_ID	0x8732
32170530Ssam#define IT8761_ID	0x8761
33170530Ssam#define IT8772_ID	0x8772
34170530Ssam#define IT8786_ID	0x8786
35170530Ssam
36178354Ssam/* IO Ports */
37170530Ssam#define REG		0x2e
38170530Ssam#define VAL		0x2f
39170530Ssam
40170530Ssam/* Logical device Numbers LDN */
41170530Ssam#define GPIO		0x07
42170530Ssam
43170530Ssam/* Configuration Registers and Functions */
44170530Ssam#define LDNREG		0x07
45170530Ssam#define CHIPID		0x20
46170530Ssam#define CHIPREV		0x22
47170530Ssam
48170530Ssam/**
49170530Ssam * struct it87_gpio - it87-specific GPIO chip
50178354Ssam * @chip: the underlying gpio_chip structure
51170530Ssam * @lock: a lock to avoid races between operations
52170530Ssam * @io_base: base address for gpio ports
53170530Ssam * @io_size: size of the port rage starting from io_base.
54170530Ssam * @output_base: Super I/O register address for Output Enable register
55170530Ssam * @simple_base: Super I/O 'Simple I/O' Enable register
56178354Ssam * @simple_size: Super IO 'Simple I/O' Enable register size; this is
57178354Ssam *	required because IT87xx chips might only provide Simple I/O
58178354Ssam *	switches on a subset of lines, whereas the others keep the
59178354Ssam *	same status all time.
60178354Ssam */
61178354Ssamstruct it87_gpio {
62178354Ssam	struct gpio_chip chip;
63178354Ssam	spinlock_t lock;
64178354Ssam	u16 io_base;
65178354Ssam	u16 io_size;
66178354Ssam	u8 output_base;
67178354Ssam	u8 simple_base;
68178354Ssam	u8 simple_size;
69178354Ssam};
70178354Ssam
71178354Ssamstatic struct it87_gpio it87_gpio_chip = {
72178354Ssam	.lock = __SPIN_LOCK_UNLOCKED(it87_gpio_chip.lock),
73170530Ssam};
74170530Ssam
75170530Ssam/* Superio chip access functions; copied from wdt_it87 */
76170530Ssam
77170530Ssamstatic inline int superio_enter(void)
78170530Ssam{
79170530Ssam	/*
80170530Ssam	 * Try to reserve REG and REG + 1 for exclusive access.
81173273Ssam	 */
82173273Ssam	if (!request_muxed_region(REG, 2, KBUILD_MODNAME))
83173273Ssam		return -EBUSY;
84173273Ssam
85173273Ssam	outb(0x87, REG);
86178354Ssam	outb(0x01, REG);
87178354Ssam	outb(0x55, REG);
88178354Ssam	outb(0x55, REG);
89173273Ssam	return 0;
90178354Ssam}
91178354Ssam
92178354Ssamstatic inline void superio_exit(void)
93178354Ssam{
94178354Ssam	outb(0x02, REG);
95178354Ssam	outb(0x02, VAL);
96178354Ssam	release_region(REG, 2);
97178354Ssam}
98178354Ssam
99178354Ssamstatic inline void superio_select(int ldn)
100178354Ssam{
101178354Ssam	outb(LDNREG, REG);
102178354Ssam	outb(ldn, VAL);
103170530Ssam}
104178354Ssam
105178354Ssamstatic inline int superio_inb(int reg)
106170530Ssam{
107170530Ssam	outb(reg, REG);
108170530Ssam	return inb(VAL);
109170530Ssam}
110170530Ssam
111170530Ssamstatic inline void superio_outb(int val, int reg)
112170530Ssam{
113170530Ssam	outb(reg, REG);
114170530Ssam	outb(val, VAL);
115170530Ssam}
116170530Ssam
117170530Ssamstatic inline int superio_inw(int reg)
118170530Ssam{
119170530Ssam	int val;
120170530Ssam
121170530Ssam	outb(reg++, REG);
122170530Ssam	val = inb(VAL) << 8;
123178354Ssam	outb(reg, REG);
124170530Ssam	val |= inb(VAL);
125170530Ssam	return val;
126170530Ssam}
127170530Ssam
128173273Ssamstatic inline void superio_set_mask(int mask, int reg)
129173273Ssam{
130178354Ssam	u8 curr_val = superio_inb(reg);
131173273Ssam	u8 new_val = curr_val | mask;
132178354Ssam
133178354Ssam	if (curr_val != new_val)
134178354Ssam		superio_outb(new_val, reg);
135178354Ssam}
136173273Ssam
137178354Ssamstatic inline void superio_clear_mask(int mask, int reg)
138178354Ssam{
139178354Ssam	u8 curr_val = superio_inb(reg);
140178354Ssam	u8 new_val = curr_val & ~mask;
141178354Ssam
142178354Ssam	if (curr_val != new_val)
143178354Ssam		superio_outb(new_val, reg);
144178354Ssam}
145178354Ssam
146178354Ssamstatic int it87_gpio_request(struct gpio_chip *chip, unsigned gpio_num)
147178354Ssam{
148178354Ssam	u8 mask, group;
149178354Ssam	int rc = 0;
150178354Ssam	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
151178354Ssam
152178354Ssam	mask = 1 << (gpio_num % 8);
153170530Ssam	group = (gpio_num / 8);
154173273Ssam
155173273Ssam	spin_lock(&it87_gpio->lock);
156170530Ssam
157170530Ssam	rc = superio_enter();
158178354Ssam	if (rc)
159178354Ssam		goto exit;
160178354Ssam
161178354Ssam	/* not all the IT87xx chips support Simple I/O and not all of
162178354Ssam	 * them allow all the lines to be set/unset to Simple I/O.
163173273Ssam	 */
164178354Ssam	if (group < it87_gpio->simple_size)
165178354Ssam		superio_set_mask(mask, group + it87_gpio->simple_base);
166178354Ssam
167178354Ssam	/* clear output enable, setting the pin to input, as all the
168170530Ssam	 * newly-exported GPIO interfaces are set to input.
169170530Ssam	 */
170178354Ssam	superio_clear_mask(mask, group + it87_gpio->output_base);
171178354Ssam
172178354Ssam	superio_exit();
173178354Ssam
174178354Ssamexit:
175178354Ssam	spin_unlock(&it87_gpio->lock);
176170530Ssam	return rc;
177178354Ssam}
178178354Ssam
179178354Ssamstatic int it87_gpio_get(struct gpio_chip *chip, unsigned gpio_num)
180170530Ssam{
181170530Ssam	u16 reg;
182170530Ssam	u8 mask;
183178354Ssam	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
184170530Ssam
185170530Ssam	mask = 1 << (gpio_num % 8);
186170530Ssam	reg = (gpio_num / 8) + it87_gpio->io_base;
187170530Ssam
188170530Ssam	return !!(inb(reg) & mask);
189170530Ssam}
190170530Ssam
191170530Ssamstatic int it87_gpio_direction_in(struct gpio_chip *chip, unsigned gpio_num)
192170530Ssam{
193170530Ssam	u8 mask, group;
194170530Ssam	int rc = 0;
195170530Ssam	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
196172226Ssam
197172226Ssam	mask = 1 << (gpio_num % 8);
198170530Ssam	group = (gpio_num / 8);
199170530Ssam
200178354Ssam	spin_lock(&it87_gpio->lock);
201170530Ssam
202170530Ssam	rc = superio_enter();
203170530Ssam	if (rc)
204170530Ssam		goto exit;
205170530Ssam
206170530Ssam	/* clear the output enable bit */
207170530Ssam	superio_clear_mask(mask, group + it87_gpio->output_base);
208170530Ssam
209170530Ssam	superio_exit();
210170530Ssam
211170530Ssamexit:
212170530Ssam	spin_unlock(&it87_gpio->lock);
213170530Ssam	return rc;
214170530Ssam}
215170530Ssam
216170530Ssamstatic void it87_gpio_set(struct gpio_chip *chip,
217170530Ssam			  unsigned gpio_num, int val)
218170530Ssam{
219170530Ssam	u8 mask, curr_vals;
220173273Ssam	u16 reg;
221170530Ssam	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
222170530Ssam
223170530Ssam	mask = 1 << (gpio_num % 8);
224170530Ssam	reg = (gpio_num / 8) + it87_gpio->io_base;
225170530Ssam
226170530Ssam	curr_vals = inb(reg);
227170530Ssam	if (val)
228170530Ssam		outb(curr_vals | mask, reg);
229170530Ssam	else
230170530Ssam		outb(curr_vals & ~mask, reg);
231170530Ssam}
232170530Ssam
233170530Ssamstatic int it87_gpio_direction_out(struct gpio_chip *chip,
234170530Ssam				   unsigned gpio_num, int val)
235178354Ssam{
236173462Ssam	u8 mask, group;
237170530Ssam	int rc = 0;
238170530Ssam	struct it87_gpio *it87_gpio = gpiochip_get_data(chip);
239170530Ssam
240170530Ssam	mask = 1 << (gpio_num % 8);
241170530Ssam	group = (gpio_num / 8);
242178354Ssam
243170530Ssam	spin_lock(&it87_gpio->lock);
244170530Ssam
245170530Ssam	rc = superio_enter();
246170530Ssam	if (rc)
247170530Ssam		goto exit;
248170530Ssam
249170530Ssam	/* set the output enable bit */
250170530Ssam	superio_set_mask(mask, group + it87_gpio->output_base);
251170530Ssam
252170530Ssam	it87_gpio_set(chip, gpio_num, val);
253178354Ssam
254173462Ssam	superio_exit();
255178354Ssam
256170530Ssamexit:
257170530Ssam	spin_unlock(&it87_gpio->lock);
258173462Ssam	return rc;
259170530Ssam}
260170530Ssam
261170530Ssamstatic const struct gpio_chip it87_template_chip = {
262178354Ssam	.label			= KBUILD_MODNAME,
263170530Ssam	.owner			= THIS_MODULE,
264170530Ssam	.request		= it87_gpio_request,
265178354Ssam	.get			= it87_gpio_get,
266170530Ssam	.direction_input	= it87_gpio_direction_in,
267170530Ssam	.set			= it87_gpio_set,
268170530Ssam	.direction_output	= it87_gpio_direction_out,
269178354Ssam	.base			= -1
270170530Ssam};
271170530Ssam
272170530Ssamstatic int __init it87_gpio_init(void)
273170530Ssam{
274170530Ssam	int rc = 0, i;
275170530Ssam	u16 chip_type;
276170530Ssam	u8 chip_rev, gpio_ba_reg;
277170530Ssam	char *labels, **labels_table;
278170530Ssam
279170530Ssam	struct it87_gpio *it87_gpio = &it87_gpio_chip;
280170530Ssam
281170530Ssam	rc = superio_enter();
282170530Ssam	if (rc)
283170530Ssam		return rc;
284170530Ssam
285170530Ssam	chip_type = superio_inw(CHIPID);
286170530Ssam	chip_rev  = superio_inb(CHIPREV) & 0x0f;
287170530Ssam	superio_exit();
288170530Ssam
289170530Ssam	it87_gpio->chip = it87_template_chip;
290170530Ssam
291170530Ssam	switch (chip_type) {
292170530Ssam	case IT8613_ID:
293170530Ssam		gpio_ba_reg = 0x62;
294170530Ssam		it87_gpio->io_size = 8;  /* it8613 only needs 6, use 8 for alignment */
295170530Ssam		it87_gpio->output_base = 0xc8;
296170530Ssam		it87_gpio->simple_base = 0xc0;
297170530Ssam		it87_gpio->simple_size = 6;
298170530Ssam		it87_gpio->chip.ngpio = 64;  /* has 48, use 64 for convenient calc */
299170530Ssam		break;
300170530Ssam	case IT8620_ID:
301170530Ssam	case IT8628_ID:
302170530Ssam		gpio_ba_reg = 0x62;
303170530Ssam		it87_gpio->io_size = 11;
304170530Ssam		it87_gpio->output_base = 0xc8;
305170530Ssam		it87_gpio->simple_size = 0;
306178354Ssam		it87_gpio->chip.ngpio = 64;
307178354Ssam		break;
308178354Ssam	case IT8718_ID:
309178354Ssam	case IT8728_ID:
310178354Ssam	case IT8732_ID:
311178354Ssam	case IT8772_ID:
312178354Ssam	case IT8786_ID:
313178354Ssam		gpio_ba_reg = 0x62;
314178354Ssam		it87_gpio->io_size = 8;
315178354Ssam		it87_gpio->output_base = 0xc8;
316178354Ssam		it87_gpio->simple_base = 0xc0;
317178354Ssam		it87_gpio->simple_size = 5;
318178354Ssam		it87_gpio->chip.ngpio = 64;
319178354Ssam		break;
320178354Ssam	case IT8761_ID:
321178354Ssam		gpio_ba_reg = 0x60;
322178354Ssam		it87_gpio->io_size = 4;
323178354Ssam		it87_gpio->output_base = 0xf0;
324178354Ssam		it87_gpio->simple_size = 0;
325178354Ssam		it87_gpio->chip.ngpio = 16;
326170530Ssam		break;
327170530Ssam	case NO_DEV_ID:
328170530Ssam		pr_err("no device\n");
329170530Ssam		return -ENODEV;
330170530Ssam	default:
331170530Ssam		pr_err("Unknown Chip found, Chip %04x Revision %x\n",
332178354Ssam		       chip_type, chip_rev);
333170530Ssam		return -ENODEV;
334170530Ssam	}
335170530Ssam
336170530Ssam	rc = superio_enter();
337170530Ssam	if (rc)
338170530Ssam		return rc;
339170530Ssam
340170530Ssam	superio_select(GPIO);
341170530Ssam
342170530Ssam	/* fetch GPIO base address */
343170530Ssam	it87_gpio->io_base = superio_inw(gpio_ba_reg);
344170530Ssam
345170530Ssam	superio_exit();
346178354Ssam
347170530Ssam	pr_info("Found Chip IT%04x rev %x. %u GPIO lines starting at %04xh\n",
348170530Ssam		chip_type, chip_rev, it87_gpio->chip.ngpio,
349170530Ssam		it87_gpio->io_base);
350170530Ssam
351170530Ssam	if (!request_region(it87_gpio->io_base, it87_gpio->io_size,
352170530Ssam							KBUILD_MODNAME))
353170530Ssam		return -EBUSY;
354170530Ssam
355170530Ssam	/* Set up aliases for the GPIO connection.
356170530Ssam	 *
357170530Ssam	 * ITE documentation for recent chips such as the IT8728F
358170530Ssam	 * refers to the GPIO lines as GPxy, with a coordinates system
359170530Ssam	 * where x is the GPIO group (starting from 1) and y is the
360178354Ssam	 * bit within the group.
361170530Ssam	 *
362170530Ssam	 * By creating these aliases, we make it easier to understand
363170530Ssam	 * to which GPIO pin we're referring to.
364170530Ssam	 */
365170530Ssam	labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
366170530Ssam								GFP_KERNEL);
367170530Ssam	labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
368170530Ssam								GFP_KERNEL);
369170530Ssam
370170530Ssam	if (!labels || !labels_table) {
371170530Ssam		rc = -ENOMEM;
372170530Ssam		goto labels_free;
373170530Ssam	}
374170530Ssam
375170530Ssam	for (i = 0; i < it87_gpio->chip.ngpio; i++) {
376170530Ssam		char *label = &labels[i * sizeof("it87_gpXY")];
377170530Ssam
378170530Ssam		sprintf(label, "it87_gp%u%u", 1+(i/8), i%8);
379170530Ssam		labels_table[i] = label;
380170530Ssam	}
381170530Ssam
382170530Ssam	it87_gpio->chip.names = (const char *const*)labels_table;
383170530Ssam
384170530Ssam	rc = gpiochip_add_data(&it87_gpio->chip, it87_gpio);
385170530Ssam	if (rc)
386170530Ssam		goto labels_free;
387170530Ssam
388170530Ssam	return 0;
389170530Ssam
390178354Ssamlabels_free:
391170530Ssam	kfree(labels_table);
392173273Ssam	kfree(labels);
393173273Ssam	release_region(it87_gpio->io_base, it87_gpio->io_size);
394173273Ssam	return rc;
395173273Ssam}
396173273Ssam
397178354Ssamstatic void __exit it87_gpio_exit(void)
398170530Ssam{
399170530Ssam	struct it87_gpio *it87_gpio = &it87_gpio_chip;
400173273Ssam
401170530Ssam	gpiochip_remove(&it87_gpio->chip);
402173273Ssam	release_region(it87_gpio->io_base, it87_gpio->io_size);
403170530Ssam	kfree(it87_gpio->chip.names[0]);
404170530Ssam	kfree(it87_gpio->chip.names);
405173273Ssam}
406170530Ssam
407178354Ssammodule_init(it87_gpio_init);
408170530Ssammodule_exit(it87_gpio_exit);
409170530Ssam
410170530SsamMODULE_AUTHOR("Diego Elio Petten�� <flameeyes@flameeyes.eu>");
411173273SsamMODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
412170530SsamMODULE_LICENSE("GPL");
413170530Ssam