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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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