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, ¶ms.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, ¶ms.gpios, BITS_PER_LONG)
576a0d65009SMaciej S. Szmigiero if (!winbond_gpio_configure_port(base, i))
577a0d65009SMaciej S. Szmigiero __clear_bit(i, ¶ms.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, ¶ms.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