xref: /linux/drivers/gpio/gpio-winbond.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1a0d65009SMaciej S. Szmigiero // SPDX-License-Identifier: GPL-2.0+
2a0d65009SMaciej S. Szmigiero /*
3a0d65009SMaciej S. Szmigiero  * GPIO interface for Winbond Super I/O chips
4a0d65009SMaciej S. Szmigiero  * Currently, only W83627UHG (Nuvoton NCT6627UD) is supported.
5a0d65009SMaciej S. Szmigiero  *
6a0d65009SMaciej S. Szmigiero  * Author: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
7a0d65009SMaciej S. Szmigiero  */
8a0d65009SMaciej S. Szmigiero 
9a0d65009SMaciej S. Szmigiero #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10a0d65009SMaciej S. Szmigiero 
11a0d65009SMaciej S. Szmigiero #include <linux/gpio/driver.h>
12a0d65009SMaciej S. Szmigiero #include <linux/ioport.h>
13a0d65009SMaciej S. Szmigiero #include <linux/isa.h>
14a0d65009SMaciej S. Szmigiero #include <linux/module.h>
15a0d65009SMaciej S. Szmigiero 
16a0d65009SMaciej S. Szmigiero #define WB_GPIO_DRIVER_NAME		KBUILD_MODNAME
17a0d65009SMaciej S. Szmigiero 
18a0d65009SMaciej S. Szmigiero #define WB_SIO_BASE			0x2e
19a0d65009SMaciej S. Szmigiero #define WB_SIO_BASE_HIGH		0x4e
20a0d65009SMaciej S. Szmigiero 
21a0d65009SMaciej S. Szmigiero #define WB_SIO_EXT_ENTER_KEY		0x87
22a0d65009SMaciej S. Szmigiero #define WB_SIO_EXT_EXIT_KEY		0xaa
23a0d65009SMaciej S. Szmigiero 
24a0d65009SMaciej S. Szmigiero /* global chip registers */
25a0d65009SMaciej S. Szmigiero 
26a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_LOGICAL		0x07
27a0d65009SMaciej S. Szmigiero 
28a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_CHIP_MSB		0x20
29a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_CHIP_LSB		0x21
30a0d65009SMaciej S. Szmigiero 
31a0d65009SMaciej S. Szmigiero #define WB_SIO_CHIP_ID_W83627UHG	0xa230
32a0d65009SMaciej S. Szmigiero #define WB_SIO_CHIP_ID_W83627UHG_MASK	GENMASK(15, 4)
33a0d65009SMaciej S. Szmigiero 
34a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_DPD			0x22
35a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_DPD_UARTA		4
36a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_DPD_UARTB		5
37a0d65009SMaciej S. Szmigiero 
38a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_IDPD		0x23
39a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_IDPD_UARTC		4
40a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_IDPD_UARTD		5
41a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_IDPD_UARTE		6
42a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_IDPD_UARTF		7
43a0d65009SMaciej S. Szmigiero 
44a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_GLOBAL_OPT		0x24
45a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_GO_ENFDC		1
46a0d65009SMaciej S. Szmigiero 
47a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_OVTGPIO3456		0x29
48a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_OG3456_G3PP		3
49a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_OG3456_G4PP		4
50a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_OG3456_G5PP		5
51a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_OG3456_G6PP		7
52a0d65009SMaciej S. Szmigiero 
53a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_I2C_PS		0x2a
54a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_I2CPS_I2CFS		1
55a0d65009SMaciej S. Szmigiero 
56a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_GPIO1_MF		0x2c
57a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_G1PP		6
58a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_G2PP		7
59a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_FS_MASK	GENMASK(1, 0)
60a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_FS_IR_OFF	0
61a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_FS_IR		1
62a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_FS_GPIO1	2
63a0d65009SMaciej S. Szmigiero #define WB_SIO_REG_G1MF_FS_UARTB	3
64a0d65009SMaciej S. Szmigiero 
65a0d65009SMaciej S. Szmigiero /* not an actual device number, just a value meaning 'no device' */
66a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_NONE		0xff
67a0d65009SMaciej S. Szmigiero 
68a0d65009SMaciej S. Szmigiero /* registers with offsets >= 0x30 are specific for a particular device */
69a0d65009SMaciej S. Szmigiero 
70a0d65009SMaciej S. Szmigiero /* UART B logical device */
71a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_UARTB		0x03
72a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTB_REG_ENABLE	0x30
73a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTB_ENABLE_ON		0
74a0d65009SMaciej S. Szmigiero 
75a0d65009SMaciej S. Szmigiero /* UART C logical device */
76a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_UARTC		0x06
77a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTC_REG_ENABLE	0x30
78a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTC_ENABLE_ON		0
79a0d65009SMaciej S. Szmigiero 
80a0d65009SMaciej S. Szmigiero /* GPIO3, GPIO4 logical device */
81a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_GPIO34		0x07
82a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_ENABLE	0x30
83a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_ENABLE_3		0
84a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_ENABLE_4		1
85a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_IO3		0xe0
86a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_DATA3	0xe1
87a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_INV3		0xe2
88a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_IO4		0xe4
89a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_DATA4	0xe5
90a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO34_REG_INV4		0xe6
91a0d65009SMaciej S. Szmigiero 
92a0d65009SMaciej S. Szmigiero /* WDTO, PLED, GPIO5, GPIO6 logical device */
93a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_WDGPIO56		0x08
94a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_ENABLE	0x30
95a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_ENABLE_5	1
96a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_ENABLE_6	2
97a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_IO5	0xe0
98a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_DATA5	0xe1
99a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_INV5	0xe2
100a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_IO6	0xe4
101a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_DATA6	0xe5
102a0d65009SMaciej S. Szmigiero #define WB_SIO_WDGPIO56_REG_INV6	0xe6
103a0d65009SMaciej S. Szmigiero 
104a0d65009SMaciej S. Szmigiero /* GPIO1, GPIO2, SUSLED logical device */
105a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_GPIO12		0x09
106a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_ENABLE	0x30
107a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_ENABLE_1		0
108a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_ENABLE_2		1
109a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_IO1		0xe0
110a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_DATA1	0xe1
111a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_INV1		0xe2
112a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_IO2		0xe4
113a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_DATA2	0xe5
114a0d65009SMaciej S. Szmigiero #define WB_SIO_GPIO12_REG_INV2		0xe6
115a0d65009SMaciej S. Szmigiero 
116a0d65009SMaciej S. Szmigiero /* UART D logical device */
117a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_UARTD		0x0d
118a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTD_REG_ENABLE	0x30
119a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTD_ENABLE_ON		0
120a0d65009SMaciej S. Szmigiero 
121a0d65009SMaciej S. Szmigiero /* UART E logical device */
122a0d65009SMaciej S. Szmigiero #define WB_SIO_DEV_UARTE		0x0e
123a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTE_REG_ENABLE	0x30
124a0d65009SMaciej S. Szmigiero #define WB_SIO_UARTE_ENABLE_ON		0
125a0d65009SMaciej S. Szmigiero 
126a0d65009SMaciej S. Szmigiero /*
127a0d65009SMaciej S. Szmigiero  * for a description what a particular field of this struct means please see
128a0d65009SMaciej S. Szmigiero  * a description of the relevant module parameter at the bottom of this file
129a0d65009SMaciej S. Szmigiero  */
130a0d65009SMaciej S. Szmigiero struct winbond_gpio_params {
131a0d65009SMaciej S. Szmigiero 	unsigned long base;
132a0d65009SMaciej S. Szmigiero 	unsigned long gpios;
133a0d65009SMaciej S. Szmigiero 	unsigned long ppgpios;
134a0d65009SMaciej S. Szmigiero 	unsigned long odgpios;
135a0d65009SMaciej S. Szmigiero 	bool pledgpio;
136a0d65009SMaciej S. Szmigiero 	bool beepgpio;
137a0d65009SMaciej S. Szmigiero 	bool i2cgpio;
138a0d65009SMaciej S. Szmigiero };
139a0d65009SMaciej S. Szmigiero 
140a0d65009SMaciej S. Szmigiero static struct winbond_gpio_params params;
141a0d65009SMaciej S. Szmigiero 
winbond_sio_enter(unsigned long base)142a0d65009SMaciej S. Szmigiero static int winbond_sio_enter(unsigned long base)
143a0d65009SMaciej S. Szmigiero {
144a0d65009SMaciej S. Szmigiero 	if (!request_muxed_region(base, 2, WB_GPIO_DRIVER_NAME))
145a0d65009SMaciej S. Szmigiero 		return -EBUSY;
146a0d65009SMaciej S. Szmigiero 
147a0d65009SMaciej S. Szmigiero 	/*
148a0d65009SMaciej S. Szmigiero 	 * datasheet says two successive writes of the "key" value are needed
149a0d65009SMaciej S. Szmigiero 	 * in order for chip to enter the "Extended Function Mode"
150a0d65009SMaciej S. Szmigiero 	 */
151a0d65009SMaciej S. Szmigiero 	outb(WB_SIO_EXT_ENTER_KEY, base);
152a0d65009SMaciej S. Szmigiero 	outb(WB_SIO_EXT_ENTER_KEY, base);
153a0d65009SMaciej S. Szmigiero 
154a0d65009SMaciej S. Szmigiero 	return 0;
155a0d65009SMaciej S. Szmigiero }
156a0d65009SMaciej S. Szmigiero 
winbond_sio_select_logical(unsigned long base,u8 dev)157a0d65009SMaciej S. Szmigiero static void winbond_sio_select_logical(unsigned long base, u8 dev)
158a0d65009SMaciej S. Szmigiero {
159a0d65009SMaciej S. Szmigiero 	outb(WB_SIO_REG_LOGICAL, base);
160a0d65009SMaciej S. Szmigiero 	outb(dev, base + 1);
161a0d65009SMaciej S. Szmigiero }
162a0d65009SMaciej S. Szmigiero 
winbond_sio_leave(unsigned long base)163a0d65009SMaciej S. Szmigiero static void winbond_sio_leave(unsigned long base)
164a0d65009SMaciej S. Szmigiero {
165a0d65009SMaciej S. Szmigiero 	outb(WB_SIO_EXT_EXIT_KEY, base);
166a0d65009SMaciej S. Szmigiero 
167a0d65009SMaciej S. Szmigiero 	release_region(base, 2);
168a0d65009SMaciej S. Szmigiero }
169a0d65009SMaciej S. Szmigiero 
winbond_sio_reg_write(unsigned long base,u8 reg,u8 data)170a0d65009SMaciej S. Szmigiero static void winbond_sio_reg_write(unsigned long base, u8 reg, u8 data)
171a0d65009SMaciej S. Szmigiero {
172a0d65009SMaciej S. Szmigiero 	outb(reg, base);
173a0d65009SMaciej S. Szmigiero 	outb(data, base + 1);
174a0d65009SMaciej S. Szmigiero }
175a0d65009SMaciej S. Szmigiero 
winbond_sio_reg_read(unsigned long base,u8 reg)176a0d65009SMaciej S. Szmigiero static u8 winbond_sio_reg_read(unsigned long base, u8 reg)
177a0d65009SMaciej S. Szmigiero {
178a0d65009SMaciej S. Szmigiero 	outb(reg, base);
179a0d65009SMaciej S. Szmigiero 	return inb(base + 1);
180a0d65009SMaciej S. Szmigiero }
181a0d65009SMaciej S. Szmigiero 
winbond_sio_reg_bset(unsigned long base,u8 reg,u8 bit)182a0d65009SMaciej S. Szmigiero static void winbond_sio_reg_bset(unsigned long base, u8 reg, u8 bit)
183a0d65009SMaciej S. Szmigiero {
184a0d65009SMaciej S. Szmigiero 	u8 val;
185a0d65009SMaciej S. Szmigiero 
186a0d65009SMaciej S. Szmigiero 	val = winbond_sio_reg_read(base, reg);
187a0d65009SMaciej S. Szmigiero 	val |= BIT(bit);
188a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_write(base, reg, val);
189a0d65009SMaciej S. Szmigiero }
190a0d65009SMaciej S. Szmigiero 
winbond_sio_reg_bclear(unsigned long base,u8 reg,u8 bit)191a0d65009SMaciej S. Szmigiero static void winbond_sio_reg_bclear(unsigned long base, u8 reg, u8 bit)
192a0d65009SMaciej S. Szmigiero {
193a0d65009SMaciej S. Szmigiero 	u8 val;
194a0d65009SMaciej S. Szmigiero 
195a0d65009SMaciej S. Szmigiero 	val = winbond_sio_reg_read(base, reg);
196a0d65009SMaciej S. Szmigiero 	val &= ~BIT(bit);
197a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_write(base, reg, val);
198a0d65009SMaciej S. Szmigiero }
199a0d65009SMaciej S. Szmigiero 
winbond_sio_reg_btest(unsigned long base,u8 reg,u8 bit)200a0d65009SMaciej S. Szmigiero static bool winbond_sio_reg_btest(unsigned long base, u8 reg, u8 bit)
201a0d65009SMaciej S. Szmigiero {
202a0d65009SMaciej S. Szmigiero 	return winbond_sio_reg_read(base, reg) & BIT(bit);
203a0d65009SMaciej S. Szmigiero }
204a0d65009SMaciej S. Szmigiero 
205a0d65009SMaciej S. Szmigiero /**
206a0d65009SMaciej S. Szmigiero  * struct winbond_gpio_port_conflict - possibly conflicting device information
207a0d65009SMaciej S. Szmigiero  * @name:	device name (NULL means no conflicting device defined)
208a0d65009SMaciej S. Szmigiero  * @dev:	Super I/O logical device number where the testreg register
209a0d65009SMaciej S. Szmigiero  *		is located (or WB_SIO_DEV_NONE - don't select any
210a0d65009SMaciej S. Szmigiero  *		logical device)
211a0d65009SMaciej S. Szmigiero  * @testreg:	register number where the testbit bit is located
212a0d65009SMaciej S. Szmigiero  * @testbit:	index of a bit to check whether an actual conflict exists
213a0d65009SMaciej S. Szmigiero  * @warnonly:	if set then a conflict isn't fatal (just warn about it),
214a0d65009SMaciej S. Szmigiero  *		otherwise disable the particular GPIO port if a conflict
215a0d65009SMaciej S. Szmigiero  *		is detected
216a0d65009SMaciej S. Szmigiero  */
217a0d65009SMaciej S. Szmigiero struct winbond_gpio_port_conflict {
218a0d65009SMaciej S. Szmigiero 	const char *name;
219a0d65009SMaciej S. Szmigiero 	u8 dev;
220a0d65009SMaciej S. Szmigiero 	u8 testreg;
221a0d65009SMaciej S. Szmigiero 	u8 testbit;
222a0d65009SMaciej S. Szmigiero 	bool warnonly;
223a0d65009SMaciej S. Szmigiero };
224a0d65009SMaciej S. Szmigiero 
225a0d65009SMaciej S. Szmigiero /**
226a0d65009SMaciej S. Szmigiero  * struct winbond_gpio_info - information about a particular GPIO port (device)
227a0d65009SMaciej S. Szmigiero  * @dev:		Super I/O logical device number of the registers
228a0d65009SMaciej S. Szmigiero  *			specified below
229a0d65009SMaciej S. Szmigiero  * @enablereg:		port enable bit register number
230a0d65009SMaciej S. Szmigiero  * @enablebit:		index of a port enable bit
231a0d65009SMaciej S. Szmigiero  * @outputreg:		output driver mode bit register number
232a0d65009SMaciej S. Szmigiero  * @outputppbit:	index of a push-pull output driver mode bit
233a0d65009SMaciej S. Szmigiero  * @ioreg:		data direction register number
234a0d65009SMaciej S. Szmigiero  * @invreg:		pin data inversion register number
235a0d65009SMaciej S. Szmigiero  * @datareg:		pin data register number
236a0d65009SMaciej S. Szmigiero  * @conflict:		description of a device that possibly conflicts with
237a0d65009SMaciej S. Szmigiero  *			this port
238a0d65009SMaciej S. Szmigiero  */
239a0d65009SMaciej S. Szmigiero struct winbond_gpio_info {
240a0d65009SMaciej S. Szmigiero 	u8 dev;
241a0d65009SMaciej S. Szmigiero 	u8 enablereg;
242a0d65009SMaciej S. Szmigiero 	u8 enablebit;
243a0d65009SMaciej S. Szmigiero 	u8 outputreg;
244a0d65009SMaciej S. Szmigiero 	u8 outputppbit;
245a0d65009SMaciej S. Szmigiero 	u8 ioreg;
246a0d65009SMaciej S. Szmigiero 	u8 invreg;
247a0d65009SMaciej S. Szmigiero 	u8 datareg;
248a0d65009SMaciej S. Szmigiero 	struct winbond_gpio_port_conflict conflict;
249a0d65009SMaciej S. Szmigiero };
250a0d65009SMaciej S. Szmigiero 
251a0d65009SMaciej S. Szmigiero static const struct winbond_gpio_info winbond_gpio_infos[6] = {
252a0d65009SMaciej S. Szmigiero 	{ /* 0 */
253a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_GPIO12,
254a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_GPIO12_REG_ENABLE,
255a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_GPIO12_ENABLE_1,
256a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_GPIO1_MF,
257a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_G1MF_G1PP,
258a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_GPIO12_REG_IO1,
259a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_GPIO12_REG_INV1,
260a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_GPIO12_REG_DATA1,
261a0d65009SMaciej S. Szmigiero 		.conflict = {
262a0d65009SMaciej S. Szmigiero 			.name = "UARTB",
263a0d65009SMaciej S. Szmigiero 			.dev = WB_SIO_DEV_UARTB,
264a0d65009SMaciej S. Szmigiero 			.testreg = WB_SIO_UARTB_REG_ENABLE,
265a0d65009SMaciej S. Szmigiero 			.testbit = WB_SIO_UARTB_ENABLE_ON,
266a0d65009SMaciej S. Szmigiero 			.warnonly = true
267a0d65009SMaciej S. Szmigiero 		}
268a0d65009SMaciej S. Szmigiero 	},
269a0d65009SMaciej S. Szmigiero 	{ /* 1 */
270a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_GPIO12,
271a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_GPIO12_REG_ENABLE,
272a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_GPIO12_ENABLE_2,
273a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_GPIO1_MF,
274a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_G1MF_G2PP,
275a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_GPIO12_REG_IO2,
276a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_GPIO12_REG_INV2,
277a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_GPIO12_REG_DATA2
278a0d65009SMaciej S. Szmigiero 		/* special conflict handling so doesn't use conflict data */
279a0d65009SMaciej S. Szmigiero 	},
280a0d65009SMaciej S. Szmigiero 	{ /* 2 */
281a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_GPIO34,
282a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_GPIO34_REG_ENABLE,
283a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_GPIO34_ENABLE_3,
284a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_OVTGPIO3456,
285a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_OG3456_G3PP,
286a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_GPIO34_REG_IO3,
287a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_GPIO34_REG_INV3,
288a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_GPIO34_REG_DATA3,
289a0d65009SMaciej S. Szmigiero 		.conflict = {
290a0d65009SMaciej S. Szmigiero 			.name = "UARTC",
291a0d65009SMaciej S. Szmigiero 			.dev = WB_SIO_DEV_UARTC,
292a0d65009SMaciej S. Szmigiero 			.testreg = WB_SIO_UARTC_REG_ENABLE,
293a0d65009SMaciej S. Szmigiero 			.testbit = WB_SIO_UARTC_ENABLE_ON,
294a0d65009SMaciej S. Szmigiero 			.warnonly = true
295a0d65009SMaciej S. Szmigiero 		}
296a0d65009SMaciej S. Szmigiero 	},
297a0d65009SMaciej S. Szmigiero 	{ /* 3 */
298a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_GPIO34,
299a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_GPIO34_REG_ENABLE,
300a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_GPIO34_ENABLE_4,
301a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_OVTGPIO3456,
302a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_OG3456_G4PP,
303a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_GPIO34_REG_IO4,
304a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_GPIO34_REG_INV4,
305a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_GPIO34_REG_DATA4,
306a0d65009SMaciej S. Szmigiero 		.conflict = {
307a0d65009SMaciej S. Szmigiero 			.name = "UARTD",
308a0d65009SMaciej S. Szmigiero 			.dev = WB_SIO_DEV_UARTD,
309a0d65009SMaciej S. Szmigiero 			.testreg = WB_SIO_UARTD_REG_ENABLE,
310a0d65009SMaciej S. Szmigiero 			.testbit = WB_SIO_UARTD_ENABLE_ON,
311a0d65009SMaciej S. Szmigiero 			.warnonly = true
312a0d65009SMaciej S. Szmigiero 		}
313a0d65009SMaciej S. Szmigiero 	},
314a0d65009SMaciej S. Szmigiero 	{ /* 4 */
315a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_WDGPIO56,
316a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_WDGPIO56_REG_ENABLE,
317a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_WDGPIO56_ENABLE_5,
318a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_OVTGPIO3456,
319a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_OG3456_G5PP,
320a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_WDGPIO56_REG_IO5,
321a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_WDGPIO56_REG_INV5,
322a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_WDGPIO56_REG_DATA5,
323a0d65009SMaciej S. Szmigiero 		.conflict = {
324a0d65009SMaciej S. Szmigiero 			.name = "UARTE",
325a0d65009SMaciej S. Szmigiero 			.dev = WB_SIO_DEV_UARTE,
326a0d65009SMaciej S. Szmigiero 			.testreg = WB_SIO_UARTE_REG_ENABLE,
327a0d65009SMaciej S. Szmigiero 			.testbit = WB_SIO_UARTE_ENABLE_ON,
328a0d65009SMaciej S. Szmigiero 			.warnonly = true
329a0d65009SMaciej S. Szmigiero 		}
330a0d65009SMaciej S. Szmigiero 	},
331a0d65009SMaciej S. Szmigiero 	{ /* 5 */
332a0d65009SMaciej S. Szmigiero 		.dev = WB_SIO_DEV_WDGPIO56,
333a0d65009SMaciej S. Szmigiero 		.enablereg = WB_SIO_WDGPIO56_REG_ENABLE,
334a0d65009SMaciej S. Szmigiero 		.enablebit = WB_SIO_WDGPIO56_ENABLE_6,
335a0d65009SMaciej S. Szmigiero 		.outputreg = WB_SIO_REG_OVTGPIO3456,
336a0d65009SMaciej S. Szmigiero 		.outputppbit = WB_SIO_REG_OG3456_G6PP,
337a0d65009SMaciej S. Szmigiero 		.ioreg = WB_SIO_WDGPIO56_REG_IO6,
338a0d65009SMaciej S. Szmigiero 		.invreg = WB_SIO_WDGPIO56_REG_INV6,
339a0d65009SMaciej S. Szmigiero 		.datareg = WB_SIO_WDGPIO56_REG_DATA6,
340a0d65009SMaciej S. Szmigiero 		.conflict = {
341a0d65009SMaciej S. Szmigiero 			.name = "FDC",
342a0d65009SMaciej S. Szmigiero 			.dev = WB_SIO_DEV_NONE,
343a0d65009SMaciej S. Szmigiero 			.testreg = WB_SIO_REG_GLOBAL_OPT,
344a0d65009SMaciej S. Szmigiero 			.testbit = WB_SIO_REG_GO_ENFDC,
345a0d65009SMaciej S. Szmigiero 			.warnonly = false
346a0d65009SMaciej S. Szmigiero 		}
347a0d65009SMaciej S. Szmigiero 	}
348a0d65009SMaciej S. Szmigiero };
349a0d65009SMaciej S. Szmigiero 
350a0d65009SMaciej S. Szmigiero /* returns whether changing a pin is allowed */
winbond_gpio_get_info(unsigned int * gpio_num,const struct winbond_gpio_info ** info)351a0d65009SMaciej S. Szmigiero static bool winbond_gpio_get_info(unsigned int *gpio_num,
352a0d65009SMaciej S. Szmigiero 				  const struct winbond_gpio_info **info)
353a0d65009SMaciej S. Szmigiero {
354a0d65009SMaciej S. Szmigiero 	bool allow_changing = true;
355a0d65009SMaciej S. Szmigiero 	unsigned long i;
356a0d65009SMaciej S. Szmigiero 
357a0d65009SMaciej S. Szmigiero 	for_each_set_bit(i, &params.gpios, BITS_PER_LONG) {
358a0d65009SMaciej S. Szmigiero 		if (*gpio_num < 8)
359a0d65009SMaciej S. Szmigiero 			break;
360a0d65009SMaciej S. Szmigiero 
361a0d65009SMaciej S. Szmigiero 		*gpio_num -= 8;
362a0d65009SMaciej S. Szmigiero 	}
363a0d65009SMaciej S. Szmigiero 
364a0d65009SMaciej S. Szmigiero 	*info = &winbond_gpio_infos[i];
365a0d65009SMaciej S. Szmigiero 
366a0d65009SMaciej S. Szmigiero 	/*
367a0d65009SMaciej S. Szmigiero 	 * GPIO2 (the second port) shares some pins with a basic PC
368a0d65009SMaciej S. Szmigiero 	 * functionality, which is very likely controlled by the firmware.
369a0d65009SMaciej S. Szmigiero 	 * Don't allow changing these pins by default.
370a0d65009SMaciej S. Szmigiero 	 */
371a0d65009SMaciej S. Szmigiero 	if (i == 1) {
372a0d65009SMaciej S. Szmigiero 		if (*gpio_num == 0 && !params.pledgpio)
373a0d65009SMaciej S. Szmigiero 			allow_changing = false;
374a0d65009SMaciej S. Szmigiero 		else if (*gpio_num == 1 && !params.beepgpio)
375a0d65009SMaciej S. Szmigiero 			allow_changing = false;
376a0d65009SMaciej S. Szmigiero 		else if ((*gpio_num == 5 || *gpio_num == 6) && !params.i2cgpio)
377a0d65009SMaciej S. Szmigiero 			allow_changing = false;
378a0d65009SMaciej S. Szmigiero 	}
379a0d65009SMaciej S. Szmigiero 
380a0d65009SMaciej S. Szmigiero 	return allow_changing;
381a0d65009SMaciej S. Szmigiero }
382a0d65009SMaciej S. Szmigiero 
winbond_gpio_get(struct gpio_chip * gc,unsigned int offset)383a0d65009SMaciej S. Szmigiero static int winbond_gpio_get(struct gpio_chip *gc, unsigned int offset)
384a0d65009SMaciej S. Szmigiero {
385a0d65009SMaciej S. Szmigiero 	unsigned long *base = gpiochip_get_data(gc);
386a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_info *info;
387a0d65009SMaciej S. Szmigiero 	bool val;
388*9ca766eaSDan Carpenter 	int ret;
389a0d65009SMaciej S. Szmigiero 
390a0d65009SMaciej S. Szmigiero 	winbond_gpio_get_info(&offset, &info);
391a0d65009SMaciej S. Szmigiero 
392*9ca766eaSDan Carpenter 	ret = winbond_sio_enter(*base);
393*9ca766eaSDan Carpenter 	if (ret)
394*9ca766eaSDan Carpenter 		return ret;
395a0d65009SMaciej S. Szmigiero 
396a0d65009SMaciej S. Szmigiero 	winbond_sio_select_logical(*base, info->dev);
397a0d65009SMaciej S. Szmigiero 
398a0d65009SMaciej S. Szmigiero 	val = winbond_sio_reg_btest(*base, info->datareg, offset);
399a0d65009SMaciej S. Szmigiero 	if (winbond_sio_reg_btest(*base, info->invreg, offset))
400a0d65009SMaciej S. Szmigiero 		val = !val;
401a0d65009SMaciej S. Szmigiero 
402a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(*base);
403a0d65009SMaciej S. Szmigiero 
404a0d65009SMaciej S. Szmigiero 	return val;
405a0d65009SMaciej S. Szmigiero }
406a0d65009SMaciej S. Szmigiero 
winbond_gpio_direction_in(struct gpio_chip * gc,unsigned int offset)407a0d65009SMaciej S. Szmigiero static int winbond_gpio_direction_in(struct gpio_chip *gc, unsigned int offset)
408a0d65009SMaciej S. Szmigiero {
409a0d65009SMaciej S. Szmigiero 	unsigned long *base = gpiochip_get_data(gc);
410a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_info *info;
411a0d65009SMaciej S. Szmigiero 	int ret;
412a0d65009SMaciej S. Szmigiero 
413a0d65009SMaciej S. Szmigiero 	if (!winbond_gpio_get_info(&offset, &info))
414a0d65009SMaciej S. Szmigiero 		return -EACCES;
415a0d65009SMaciej S. Szmigiero 
416a0d65009SMaciej S. Szmigiero 	ret = winbond_sio_enter(*base);
417a0d65009SMaciej S. Szmigiero 	if (ret)
418a0d65009SMaciej S. Szmigiero 		return ret;
419a0d65009SMaciej S. Szmigiero 
420a0d65009SMaciej S. Szmigiero 	winbond_sio_select_logical(*base, info->dev);
421a0d65009SMaciej S. Szmigiero 
422a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_bset(*base, info->ioreg, offset);
423a0d65009SMaciej S. Szmigiero 
424a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(*base);
425a0d65009SMaciej S. Szmigiero 
426a0d65009SMaciej S. Szmigiero 	return 0;
427a0d65009SMaciej S. Szmigiero }
428a0d65009SMaciej S. Szmigiero 
winbond_gpio_direction_out(struct gpio_chip * gc,unsigned int offset,int val)429a0d65009SMaciej S. Szmigiero static int winbond_gpio_direction_out(struct gpio_chip *gc,
430a0d65009SMaciej S. Szmigiero 				      unsigned int offset,
431a0d65009SMaciej S. Szmigiero 				      int val)
432a0d65009SMaciej S. Szmigiero {
433a0d65009SMaciej S. Szmigiero 	unsigned long *base = gpiochip_get_data(gc);
434a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_info *info;
435a0d65009SMaciej S. Szmigiero 	int ret;
436a0d65009SMaciej S. Szmigiero 
437a0d65009SMaciej S. Szmigiero 	if (!winbond_gpio_get_info(&offset, &info))
438a0d65009SMaciej S. Szmigiero 		return -EACCES;
439a0d65009SMaciej S. Szmigiero 
440a0d65009SMaciej S. Szmigiero 	ret = winbond_sio_enter(*base);
441a0d65009SMaciej S. Szmigiero 	if (ret)
442a0d65009SMaciej S. Szmigiero 		return ret;
443a0d65009SMaciej S. Szmigiero 
444a0d65009SMaciej S. Szmigiero 	winbond_sio_select_logical(*base, info->dev);
445a0d65009SMaciej S. Szmigiero 
446a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_bclear(*base, info->ioreg, offset);
447a0d65009SMaciej S. Szmigiero 
448a0d65009SMaciej S. Szmigiero 	if (winbond_sio_reg_btest(*base, info->invreg, offset))
449a0d65009SMaciej S. Szmigiero 		val = !val;
450a0d65009SMaciej S. Szmigiero 
451a0d65009SMaciej S. Szmigiero 	if (val)
452a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bset(*base, info->datareg, offset);
453a0d65009SMaciej S. Szmigiero 	else
454a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bclear(*base, info->datareg, offset);
455a0d65009SMaciej S. Szmigiero 
456a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(*base);
457a0d65009SMaciej S. Szmigiero 
458a0d65009SMaciej S. Szmigiero 	return 0;
459a0d65009SMaciej S. Szmigiero }
460a0d65009SMaciej S. Szmigiero 
winbond_gpio_set(struct gpio_chip * gc,unsigned int offset,int val)461a0d65009SMaciej S. Szmigiero static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset,
462a0d65009SMaciej S. Szmigiero 			     int val)
463a0d65009SMaciej S. Szmigiero {
464a0d65009SMaciej S. Szmigiero 	unsigned long *base = gpiochip_get_data(gc);
465a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_info *info;
466a0d65009SMaciej S. Szmigiero 
467a0d65009SMaciej S. Szmigiero 	if (!winbond_gpio_get_info(&offset, &info))
468a0d65009SMaciej S. Szmigiero 		return;
469a0d65009SMaciej S. Szmigiero 
470a0d65009SMaciej S. Szmigiero 	if (winbond_sio_enter(*base) != 0)
471a0d65009SMaciej S. Szmigiero 		return;
472a0d65009SMaciej S. Szmigiero 
473a0d65009SMaciej S. Szmigiero 	winbond_sio_select_logical(*base, info->dev);
474a0d65009SMaciej S. Szmigiero 
475a0d65009SMaciej S. Szmigiero 	if (winbond_sio_reg_btest(*base, info->invreg, offset))
476a0d65009SMaciej S. Szmigiero 		val = !val;
477a0d65009SMaciej S. Szmigiero 
478a0d65009SMaciej S. Szmigiero 	if (val)
479a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bset(*base, info->datareg, offset);
480a0d65009SMaciej S. Szmigiero 	else
481a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bclear(*base, info->datareg, offset);
482a0d65009SMaciej S. Szmigiero 
483a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(*base);
484a0d65009SMaciej S. Szmigiero }
485a0d65009SMaciej S. Szmigiero 
486a0d65009SMaciej S. Szmigiero static struct gpio_chip winbond_gpio_chip = {
487a0d65009SMaciej S. Szmigiero 	.base			= -1,
488a0d65009SMaciej S. Szmigiero 	.label			= WB_GPIO_DRIVER_NAME,
489a0d65009SMaciej S. Szmigiero 	.owner			= THIS_MODULE,
490a0d65009SMaciej S. Szmigiero 	.can_sleep		= true,
491a0d65009SMaciej S. Szmigiero 	.get			= winbond_gpio_get,
492a0d65009SMaciej S. Szmigiero 	.direction_input	= winbond_gpio_direction_in,
493a0d65009SMaciej S. Szmigiero 	.set			= winbond_gpio_set,
494a0d65009SMaciej S. Szmigiero 	.direction_output	= winbond_gpio_direction_out,
495a0d65009SMaciej S. Szmigiero };
496a0d65009SMaciej S. Szmigiero 
winbond_gpio_configure_port0_pins(unsigned long base)497a0d65009SMaciej S. Szmigiero static void winbond_gpio_configure_port0_pins(unsigned long base)
498a0d65009SMaciej S. Szmigiero {
499a0d65009SMaciej S. Szmigiero 	unsigned int val;
500a0d65009SMaciej S. Szmigiero 
501a0d65009SMaciej S. Szmigiero 	val = winbond_sio_reg_read(base, WB_SIO_REG_GPIO1_MF);
502a0d65009SMaciej S. Szmigiero 	if ((val & WB_SIO_REG_G1MF_FS_MASK) == WB_SIO_REG_G1MF_FS_GPIO1)
503a0d65009SMaciej S. Szmigiero 		return;
504a0d65009SMaciej S. Szmigiero 
505a0d65009SMaciej S. Szmigiero 	pr_warn("GPIO1 pins were connected to something else (%.2x), fixing\n",
506a0d65009SMaciej S. Szmigiero 		val);
507a0d65009SMaciej S. Szmigiero 
508a0d65009SMaciej S. Szmigiero 	val &= ~WB_SIO_REG_G1MF_FS_MASK;
509a0d65009SMaciej S. Szmigiero 	val |= WB_SIO_REG_G1MF_FS_GPIO1;
510a0d65009SMaciej S. Szmigiero 
511a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_write(base, WB_SIO_REG_GPIO1_MF, val);
512a0d65009SMaciej S. Szmigiero }
513a0d65009SMaciej S. Szmigiero 
winbond_gpio_configure_port1_check_i2c(unsigned long base)514a0d65009SMaciej S. Szmigiero static void winbond_gpio_configure_port1_check_i2c(unsigned long base)
515a0d65009SMaciej S. Szmigiero {
516a0d65009SMaciej S. Szmigiero 	params.i2cgpio = !winbond_sio_reg_btest(base, WB_SIO_REG_I2C_PS,
517a0d65009SMaciej S. Szmigiero 						WB_SIO_REG_I2CPS_I2CFS);
518a0d65009SMaciej S. Szmigiero 	if (!params.i2cgpio)
519a0d65009SMaciej S. Szmigiero 		pr_warn("disabling GPIO2.5 and GPIO2.6 as I2C is enabled\n");
520a0d65009SMaciej S. Szmigiero }
521a0d65009SMaciej S. Szmigiero 
winbond_gpio_configure_port(unsigned long base,unsigned int idx)522a0d65009SMaciej S. Szmigiero static bool winbond_gpio_configure_port(unsigned long base, unsigned int idx)
523a0d65009SMaciej S. Szmigiero {
524a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_info *info = &winbond_gpio_infos[idx];
525a0d65009SMaciej S. Szmigiero 	const struct winbond_gpio_port_conflict *conflict = &info->conflict;
526a0d65009SMaciej S. Szmigiero 
527a0d65009SMaciej S. Szmigiero 	/* is there a possible conflicting device defined? */
528a0d65009SMaciej S. Szmigiero 	if (conflict->name != NULL) {
529a0d65009SMaciej S. Szmigiero 		if (conflict->dev != WB_SIO_DEV_NONE)
530a0d65009SMaciej S. Szmigiero 			winbond_sio_select_logical(base, conflict->dev);
531a0d65009SMaciej S. Szmigiero 
532a0d65009SMaciej S. Szmigiero 		if (winbond_sio_reg_btest(base, conflict->testreg,
533a0d65009SMaciej S. Szmigiero 					  conflict->testbit)) {
534a0d65009SMaciej S. Szmigiero 			if (conflict->warnonly)
535a0d65009SMaciej S. Szmigiero 				pr_warn("enabled GPIO%u share pins with active %s\n",
536a0d65009SMaciej S. Szmigiero 					idx + 1, conflict->name);
537a0d65009SMaciej S. Szmigiero 			else {
538a0d65009SMaciej S. Szmigiero 				pr_warn("disabling GPIO%u as %s is enabled\n",
539a0d65009SMaciej S. Szmigiero 					idx + 1, conflict->name);
540a0d65009SMaciej S. Szmigiero 				return false;
541a0d65009SMaciej S. Szmigiero 			}
542a0d65009SMaciej S. Szmigiero 		}
543a0d65009SMaciej S. Szmigiero 	}
544a0d65009SMaciej S. Szmigiero 
545a0d65009SMaciej S. Szmigiero 	/* GPIO1 and GPIO2 need some (additional) special handling */
546a0d65009SMaciej S. Szmigiero 	if (idx == 0)
547a0d65009SMaciej S. Szmigiero 		winbond_gpio_configure_port0_pins(base);
548a0d65009SMaciej S. Szmigiero 	else if (idx == 1)
549a0d65009SMaciej S. Szmigiero 		winbond_gpio_configure_port1_check_i2c(base);
550a0d65009SMaciej S. Szmigiero 
551a0d65009SMaciej S. Szmigiero 	winbond_sio_select_logical(base, info->dev);
552a0d65009SMaciej S. Szmigiero 
553a0d65009SMaciej S. Szmigiero 	winbond_sio_reg_bset(base, info->enablereg, info->enablebit);
554a0d65009SMaciej S. Szmigiero 
555a0d65009SMaciej S. Szmigiero 	if (params.ppgpios & BIT(idx))
556a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bset(base, info->outputreg,
557a0d65009SMaciej S. Szmigiero 				     info->outputppbit);
558a0d65009SMaciej S. Szmigiero 	else if (params.odgpios & BIT(idx))
559a0d65009SMaciej S. Szmigiero 		winbond_sio_reg_bclear(base, info->outputreg,
560a0d65009SMaciej S. Szmigiero 				       info->outputppbit);
561a0d65009SMaciej S. Szmigiero 	else
562a0d65009SMaciej S. Szmigiero 		pr_notice("GPIO%u pins are %s\n", idx + 1,
563a0d65009SMaciej S. Szmigiero 			  winbond_sio_reg_btest(base, info->outputreg,
564a0d65009SMaciej S. Szmigiero 						info->outputppbit) ?
565a0d65009SMaciej S. Szmigiero 			  "push-pull" :
566a0d65009SMaciej S. Szmigiero 			  "open drain");
567a0d65009SMaciej S. Szmigiero 
568a0d65009SMaciej S. Szmigiero 	return true;
569a0d65009SMaciej S. Szmigiero }
570a0d65009SMaciej S. Szmigiero 
winbond_gpio_configure(unsigned long base)571a0d65009SMaciej S. Szmigiero static int winbond_gpio_configure(unsigned long base)
572a0d65009SMaciej S. Szmigiero {
573a0d65009SMaciej S. Szmigiero 	unsigned long i;
574a0d65009SMaciej S. Szmigiero 
575a0d65009SMaciej S. Szmigiero 	for_each_set_bit(i, &params.gpios, BITS_PER_LONG)
576a0d65009SMaciej S. Szmigiero 		if (!winbond_gpio_configure_port(base, i))
577a0d65009SMaciej S. Szmigiero 			__clear_bit(i, &params.gpios);
578a0d65009SMaciej S. Szmigiero 
579a0d65009SMaciej S. Szmigiero 	if (!params.gpios) {
580a0d65009SMaciej S. Szmigiero 		pr_err("please use 'gpios' module parameter to select some active GPIO ports to enable\n");
581a0d65009SMaciej S. Szmigiero 		return -EINVAL;
582a0d65009SMaciej S. Szmigiero 	}
583a0d65009SMaciej S. Szmigiero 
584a0d65009SMaciej S. Szmigiero 	return 0;
585a0d65009SMaciej S. Szmigiero }
586a0d65009SMaciej S. Szmigiero 
winbond_gpio_check_chip(unsigned long base)587a0d65009SMaciej S. Szmigiero static int winbond_gpio_check_chip(unsigned long base)
588a0d65009SMaciej S. Szmigiero {
589a0d65009SMaciej S. Szmigiero 	int ret;
590a0d65009SMaciej S. Szmigiero 	unsigned int chip;
591a0d65009SMaciej S. Szmigiero 
592a0d65009SMaciej S. Szmigiero 	ret = winbond_sio_enter(base);
593a0d65009SMaciej S. Szmigiero 	if (ret)
594a0d65009SMaciej S. Szmigiero 		return ret;
595a0d65009SMaciej S. Szmigiero 
596a0d65009SMaciej S. Szmigiero 	chip = winbond_sio_reg_read(base, WB_SIO_REG_CHIP_MSB) << 8;
597a0d65009SMaciej S. Szmigiero 	chip |= winbond_sio_reg_read(base, WB_SIO_REG_CHIP_LSB);
598a0d65009SMaciej S. Szmigiero 
599a0d65009SMaciej S. Szmigiero 	pr_notice("chip ID at %lx is %.4x\n", base, chip);
600a0d65009SMaciej S. Szmigiero 
601a0d65009SMaciej S. Szmigiero 	if ((chip & WB_SIO_CHIP_ID_W83627UHG_MASK) !=
602a0d65009SMaciej S. Szmigiero 	    WB_SIO_CHIP_ID_W83627UHG) {
603a0d65009SMaciej S. Szmigiero 		pr_err("not an our chip\n");
604a0d65009SMaciej S. Szmigiero 		ret = -ENODEV;
605a0d65009SMaciej S. Szmigiero 	}
606a0d65009SMaciej S. Szmigiero 
607a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(base);
608a0d65009SMaciej S. Szmigiero 
609a0d65009SMaciej S. Szmigiero 	return ret;
610a0d65009SMaciej S. Szmigiero }
611a0d65009SMaciej S. Szmigiero 
winbond_gpio_imatch(struct device * dev,unsigned int id)612a0d65009SMaciej S. Szmigiero static int winbond_gpio_imatch(struct device *dev, unsigned int id)
613a0d65009SMaciej S. Szmigiero {
614a0d65009SMaciej S. Szmigiero 	unsigned long gpios_rem;
615a0d65009SMaciej S. Szmigiero 	int ret;
616a0d65009SMaciej S. Szmigiero 
617a0d65009SMaciej S. Szmigiero 	gpios_rem = params.gpios & ~GENMASK(ARRAY_SIZE(winbond_gpio_infos) - 1,
618a0d65009SMaciej S. Szmigiero 					    0);
619a0d65009SMaciej S. Szmigiero 	if (gpios_rem) {
620a0d65009SMaciej S. Szmigiero 		pr_warn("unknown ports (%lx) enabled in GPIO ports bitmask\n",
621a0d65009SMaciej S. Szmigiero 			gpios_rem);
622a0d65009SMaciej S. Szmigiero 		params.gpios &= ~gpios_rem;
623a0d65009SMaciej S. Szmigiero 	}
624a0d65009SMaciej S. Szmigiero 
625a0d65009SMaciej S. Szmigiero 	if (params.ppgpios & params.odgpios) {
626a0d65009SMaciej S. Szmigiero 		pr_err("some GPIO ports are set both to push-pull and open drain mode at the same time\n");
627a0d65009SMaciej S. Szmigiero 		return 0;
628a0d65009SMaciej S. Szmigiero 	}
629a0d65009SMaciej S. Szmigiero 
630a0d65009SMaciej S. Szmigiero 	if (params.base != 0)
631a0d65009SMaciej S. Szmigiero 		return winbond_gpio_check_chip(params.base) == 0;
632a0d65009SMaciej S. Szmigiero 
633a0d65009SMaciej S. Szmigiero 	/*
634a0d65009SMaciej S. Szmigiero 	 * if the 'base' module parameter is unset probe two chip default
635a0d65009SMaciej S. Szmigiero 	 * I/O port bases
636a0d65009SMaciej S. Szmigiero 	 */
637a0d65009SMaciej S. Szmigiero 	params.base = WB_SIO_BASE;
638a0d65009SMaciej S. Szmigiero 	ret = winbond_gpio_check_chip(params.base);
639a0d65009SMaciej S. Szmigiero 	if (ret == 0)
640a0d65009SMaciej S. Szmigiero 		return 1;
641a0d65009SMaciej S. Szmigiero 	if (ret != -ENODEV && ret != -EBUSY)
642a0d65009SMaciej S. Szmigiero 		return 0;
643a0d65009SMaciej S. Szmigiero 
644a0d65009SMaciej S. Szmigiero 	params.base = WB_SIO_BASE_HIGH;
645a0d65009SMaciej S. Szmigiero 	return winbond_gpio_check_chip(params.base) == 0;
646a0d65009SMaciej S. Szmigiero }
647a0d65009SMaciej S. Szmigiero 
winbond_gpio_iprobe(struct device * dev,unsigned int id)648a0d65009SMaciej S. Szmigiero static int winbond_gpio_iprobe(struct device *dev, unsigned int id)
649a0d65009SMaciej S. Szmigiero {
650a0d65009SMaciej S. Szmigiero 	int ret;
651a0d65009SMaciej S. Szmigiero 
652a0d65009SMaciej S. Szmigiero 	if (params.base == 0)
653a0d65009SMaciej S. Szmigiero 		return -EINVAL;
654a0d65009SMaciej S. Szmigiero 
655a0d65009SMaciej S. Szmigiero 	ret = winbond_sio_enter(params.base);
656a0d65009SMaciej S. Szmigiero 	if (ret)
657a0d65009SMaciej S. Szmigiero 		return ret;
658a0d65009SMaciej S. Szmigiero 
659a0d65009SMaciej S. Szmigiero 	ret = winbond_gpio_configure(params.base);
660a0d65009SMaciej S. Szmigiero 
661a0d65009SMaciej S. Szmigiero 	winbond_sio_leave(params.base);
662a0d65009SMaciej S. Szmigiero 
663a0d65009SMaciej S. Szmigiero 	if (ret)
664a0d65009SMaciej S. Szmigiero 		return ret;
665a0d65009SMaciej S. Szmigiero 
666a0d65009SMaciej S. Szmigiero 	/*
667a0d65009SMaciej S. Szmigiero 	 * Add 8 gpios for every GPIO port that was enabled in gpios
668a0d65009SMaciej S. Szmigiero 	 * module parameter (that wasn't disabled earlier in
669a0d65009SMaciej S. Szmigiero 	 * winbond_gpio_configure() & co. due to, for example, a pin conflict).
670a0d65009SMaciej S. Szmigiero 	 */
671a0d65009SMaciej S. Szmigiero 	winbond_gpio_chip.ngpio = hweight_long(params.gpios) * 8;
672a0d65009SMaciej S. Szmigiero 
673a0d65009SMaciej S. Szmigiero 	/*
674a0d65009SMaciej S. Szmigiero 	 * GPIO6 port has only 5 pins, so if it is enabled we have to adjust
675a0d65009SMaciej S. Szmigiero 	 * the total count appropriately
676a0d65009SMaciej S. Szmigiero 	 */
677a0d65009SMaciej S. Szmigiero 	if (params.gpios & BIT(5))
678a0d65009SMaciej S. Szmigiero 		winbond_gpio_chip.ngpio -= (8 - 5);
679a0d65009SMaciej S. Szmigiero 
680a0d65009SMaciej S. Szmigiero 	winbond_gpio_chip.parent = dev;
681a0d65009SMaciej S. Szmigiero 
682a0d65009SMaciej S. Szmigiero 	return devm_gpiochip_add_data(dev, &winbond_gpio_chip, &params.base);
683a0d65009SMaciej S. Szmigiero }
684a0d65009SMaciej S. Szmigiero 
685a0d65009SMaciej S. Szmigiero static struct isa_driver winbond_gpio_idriver = {
686a0d65009SMaciej S. Szmigiero 	.driver = {
687a0d65009SMaciej S. Szmigiero 		.name	= WB_GPIO_DRIVER_NAME,
688a0d65009SMaciej S. Szmigiero 	},
689a0d65009SMaciej S. Szmigiero 	.match	= winbond_gpio_imatch,
690a0d65009SMaciej S. Szmigiero 	.probe	= winbond_gpio_iprobe,
691a0d65009SMaciej S. Szmigiero };
692a0d65009SMaciej S. Szmigiero 
693a0d65009SMaciej S. Szmigiero module_isa_driver(winbond_gpio_idriver, 1);
694a0d65009SMaciej S. Szmigiero 
695a0d65009SMaciej S. Szmigiero module_param_named(base, params.base, ulong, 0444);
696a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(base,
697a0d65009SMaciej S. Szmigiero 		 "I/O port base (when unset - probe chip default ones)");
698a0d65009SMaciej S. Szmigiero 
699a0d65009SMaciej S. Szmigiero /* This parameter sets which GPIO devices (ports) we enable */
700a0d65009SMaciej S. Szmigiero module_param_named(gpios, params.gpios, ulong, 0444);
701a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(gpios,
702a0d65009SMaciej S. Szmigiero 		 "bitmask of GPIO ports to enable (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
703a0d65009SMaciej S. Szmigiero 
704a0d65009SMaciej S. Szmigiero /*
705a0d65009SMaciej S. Szmigiero  * These two parameters below set how we configure GPIO ports output drivers.
706a0d65009SMaciej S. Szmigiero  * It can't be a one bitmask since we need three values per port: push-pull,
707a0d65009SMaciej S. Szmigiero  * open-drain and keep as-is (this is the default).
708a0d65009SMaciej S. Szmigiero  */
709a0d65009SMaciej S. Szmigiero module_param_named(ppgpios, params.ppgpios, ulong, 0444);
710a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(ppgpios,
711a0d65009SMaciej S. Szmigiero 		 "bitmask of GPIO ports to set to push-pull mode (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
712a0d65009SMaciej S. Szmigiero 
713a0d65009SMaciej S. Szmigiero module_param_named(odgpios, params.odgpios, ulong, 0444);
714a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(odgpios,
715a0d65009SMaciej S. Szmigiero 		 "bitmask of GPIO ports to set to open drain mode (bit 0 - GPIO1, bit 1 - GPIO2, etc.");
716a0d65009SMaciej S. Szmigiero 
717a0d65009SMaciej S. Szmigiero /*
718a0d65009SMaciej S. Szmigiero  * GPIO2.0 and GPIO2.1 control a basic PC functionality that we
719a0d65009SMaciej S. Szmigiero  * don't allow tinkering with by default (it is very likely that the
720a0d65009SMaciej S. Szmigiero  * firmware owns these pins).
721a0d65009SMaciej S. Szmigiero  * These two parameters below allow overriding these prohibitions.
722a0d65009SMaciej S. Szmigiero  */
723a0d65009SMaciej S. Szmigiero module_param_named(pledgpio, params.pledgpio, bool, 0644);
724a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(pledgpio,
725a0d65009SMaciej S. Szmigiero 		 "enable changing value of GPIO2.0 bit (Power LED), default no.");
726a0d65009SMaciej S. Szmigiero 
727a0d65009SMaciej S. Szmigiero module_param_named(beepgpio, params.beepgpio, bool, 0644);
728a0d65009SMaciej S. Szmigiero MODULE_PARM_DESC(beepgpio,
729a0d65009SMaciej S. Szmigiero 		 "enable changing value of GPIO2.1 bit (BEEP), default no.");
730a0d65009SMaciej S. Szmigiero 
731a0d65009SMaciej S. Szmigiero MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>");
732a0d65009SMaciej S. Szmigiero MODULE_DESCRIPTION("GPIO interface for Winbond Super I/O chips");
733a0d65009SMaciej S. Szmigiero MODULE_LICENSE("GPL");
734