18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO interface for Winbond Super I/O chips 48c2ecf20Sopenharmony_ci * Currently, only W83627UHG (Nuvoton NCT6627UD) is supported. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Maciej S. Szmigiero <mail@maciej.szmigiero.name> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/isa.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define WB_GPIO_DRIVER_NAME KBUILD_MODNAME 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define WB_SIO_BASE 0x2e 198c2ecf20Sopenharmony_ci#define WB_SIO_BASE_HIGH 0x4e 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define WB_SIO_EXT_ENTER_KEY 0x87 228c2ecf20Sopenharmony_ci#define WB_SIO_EXT_EXIT_KEY 0xaa 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* global chip registers */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define WB_SIO_REG_LOGICAL 0x07 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define WB_SIO_REG_CHIP_MSB 0x20 298c2ecf20Sopenharmony_ci#define WB_SIO_REG_CHIP_LSB 0x21 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define WB_SIO_CHIP_ID_W83627UHG 0xa230 328c2ecf20Sopenharmony_ci#define WB_SIO_CHIP_ID_W83627UHG_MASK GENMASK(15, 4) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define WB_SIO_REG_DPD 0x22 358c2ecf20Sopenharmony_ci#define WB_SIO_REG_DPD_UARTA 4 368c2ecf20Sopenharmony_ci#define WB_SIO_REG_DPD_UARTB 5 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define WB_SIO_REG_IDPD 0x23 398c2ecf20Sopenharmony_ci#define WB_SIO_REG_IDPD_UARTC 4 408c2ecf20Sopenharmony_ci#define WB_SIO_REG_IDPD_UARTD 5 418c2ecf20Sopenharmony_ci#define WB_SIO_REG_IDPD_UARTE 6 428c2ecf20Sopenharmony_ci#define WB_SIO_REG_IDPD_UARTF 7 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define WB_SIO_REG_GLOBAL_OPT 0x24 458c2ecf20Sopenharmony_ci#define WB_SIO_REG_GO_ENFDC 1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define WB_SIO_REG_OVTGPIO3456 0x29 488c2ecf20Sopenharmony_ci#define WB_SIO_REG_OG3456_G3PP 3 498c2ecf20Sopenharmony_ci#define WB_SIO_REG_OG3456_G4PP 4 508c2ecf20Sopenharmony_ci#define WB_SIO_REG_OG3456_G5PP 5 518c2ecf20Sopenharmony_ci#define WB_SIO_REG_OG3456_G6PP 7 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define WB_SIO_REG_I2C_PS 0x2a 548c2ecf20Sopenharmony_ci#define WB_SIO_REG_I2CPS_I2CFS 1 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define WB_SIO_REG_GPIO1_MF 0x2c 578c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_G1PP 6 588c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_G2PP 7 598c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_FS_MASK GENMASK(1, 0) 608c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_FS_IR_OFF 0 618c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_FS_IR 1 628c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_FS_GPIO1 2 638c2ecf20Sopenharmony_ci#define WB_SIO_REG_G1MF_FS_UARTB 3 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* not an actual device number, just a value meaning 'no device' */ 668c2ecf20Sopenharmony_ci#define WB_SIO_DEV_NONE 0xff 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* registers with offsets >= 0x30 are specific for a particular device */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* UART B logical device */ 718c2ecf20Sopenharmony_ci#define WB_SIO_DEV_UARTB 0x03 728c2ecf20Sopenharmony_ci#define WB_SIO_UARTB_REG_ENABLE 0x30 738c2ecf20Sopenharmony_ci#define WB_SIO_UARTB_ENABLE_ON 0 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* UART C logical device */ 768c2ecf20Sopenharmony_ci#define WB_SIO_DEV_UARTC 0x06 778c2ecf20Sopenharmony_ci#define WB_SIO_UARTC_REG_ENABLE 0x30 788c2ecf20Sopenharmony_ci#define WB_SIO_UARTC_ENABLE_ON 0 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* GPIO3, GPIO4 logical device */ 818c2ecf20Sopenharmony_ci#define WB_SIO_DEV_GPIO34 0x07 828c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_ENABLE 0x30 838c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_ENABLE_3 0 848c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_ENABLE_4 1 858c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_IO3 0xe0 868c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_DATA3 0xe1 878c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_INV3 0xe2 888c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_IO4 0xe4 898c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_DATA4 0xe5 908c2ecf20Sopenharmony_ci#define WB_SIO_GPIO34_REG_INV4 0xe6 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* WDTO, PLED, GPIO5, GPIO6 logical device */ 938c2ecf20Sopenharmony_ci#define WB_SIO_DEV_WDGPIO56 0x08 948c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_ENABLE 0x30 958c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_ENABLE_5 1 968c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_ENABLE_6 2 978c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_IO5 0xe0 988c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_DATA5 0xe1 998c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_INV5 0xe2 1008c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_IO6 0xe4 1018c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_DATA6 0xe5 1028c2ecf20Sopenharmony_ci#define WB_SIO_WDGPIO56_REG_INV6 0xe6 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* GPIO1, GPIO2, SUSLED logical device */ 1058c2ecf20Sopenharmony_ci#define WB_SIO_DEV_GPIO12 0x09 1068c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_ENABLE 0x30 1078c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_ENABLE_1 0 1088c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_ENABLE_2 1 1098c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_IO1 0xe0 1108c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_DATA1 0xe1 1118c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_INV1 0xe2 1128c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_IO2 0xe4 1138c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_DATA2 0xe5 1148c2ecf20Sopenharmony_ci#define WB_SIO_GPIO12_REG_INV2 0xe6 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* UART D logical device */ 1178c2ecf20Sopenharmony_ci#define WB_SIO_DEV_UARTD 0x0d 1188c2ecf20Sopenharmony_ci#define WB_SIO_UARTD_REG_ENABLE 0x30 1198c2ecf20Sopenharmony_ci#define WB_SIO_UARTD_ENABLE_ON 0 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* UART E logical device */ 1228c2ecf20Sopenharmony_ci#define WB_SIO_DEV_UARTE 0x0e 1238c2ecf20Sopenharmony_ci#define WB_SIO_UARTE_REG_ENABLE 0x30 1248c2ecf20Sopenharmony_ci#define WB_SIO_UARTE_ENABLE_ON 0 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * for a description what a particular field of this struct means please see 1288c2ecf20Sopenharmony_ci * a description of the relevant module parameter at the bottom of this file 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistruct winbond_gpio_params { 1318c2ecf20Sopenharmony_ci unsigned long base; 1328c2ecf20Sopenharmony_ci unsigned long gpios; 1338c2ecf20Sopenharmony_ci unsigned long ppgpios; 1348c2ecf20Sopenharmony_ci unsigned long odgpios; 1358c2ecf20Sopenharmony_ci bool pledgpio; 1368c2ecf20Sopenharmony_ci bool beepgpio; 1378c2ecf20Sopenharmony_ci bool i2cgpio; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct winbond_gpio_params params; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int winbond_sio_enter(unsigned long base) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci if (!request_muxed_region(base, 2, WB_GPIO_DRIVER_NAME)) 1458c2ecf20Sopenharmony_ci return -EBUSY; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * datasheet says two successive writes of the "key" value are needed 1498c2ecf20Sopenharmony_ci * in order for chip to enter the "Extended Function Mode" 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci outb(WB_SIO_EXT_ENTER_KEY, base); 1528c2ecf20Sopenharmony_ci outb(WB_SIO_EXT_ENTER_KEY, base); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void winbond_sio_select_logical(unsigned long base, u8 dev) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci outb(WB_SIO_REG_LOGICAL, base); 1608c2ecf20Sopenharmony_ci outb(dev, base + 1); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void winbond_sio_leave(unsigned long base) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci outb(WB_SIO_EXT_EXIT_KEY, base); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci release_region(base, 2); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void winbond_sio_reg_write(unsigned long base, u8 reg, u8 data) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci outb(reg, base); 1738c2ecf20Sopenharmony_ci outb(data, base + 1); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic u8 winbond_sio_reg_read(unsigned long base, u8 reg) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci outb(reg, base); 1798c2ecf20Sopenharmony_ci return inb(base + 1); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void winbond_sio_reg_bset(unsigned long base, u8 reg, u8 bit) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u8 val; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci val = winbond_sio_reg_read(base, reg); 1878c2ecf20Sopenharmony_ci val |= BIT(bit); 1888c2ecf20Sopenharmony_ci winbond_sio_reg_write(base, reg, val); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void winbond_sio_reg_bclear(unsigned long base, u8 reg, u8 bit) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci u8 val; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci val = winbond_sio_reg_read(base, reg); 1968c2ecf20Sopenharmony_ci val &= ~BIT(bit); 1978c2ecf20Sopenharmony_ci winbond_sio_reg_write(base, reg, val); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic bool winbond_sio_reg_btest(unsigned long base, u8 reg, u8 bit) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return winbond_sio_reg_read(base, reg) & BIT(bit); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/** 2068c2ecf20Sopenharmony_ci * struct winbond_gpio_port_conflict - possibly conflicting device information 2078c2ecf20Sopenharmony_ci * @name: device name (NULL means no conflicting device defined) 2088c2ecf20Sopenharmony_ci * @dev: Super I/O logical device number where the testreg register 2098c2ecf20Sopenharmony_ci * is located (or WB_SIO_DEV_NONE - don't select any 2108c2ecf20Sopenharmony_ci * logical device) 2118c2ecf20Sopenharmony_ci * @testreg: register number where the testbit bit is located 2128c2ecf20Sopenharmony_ci * @testbit: index of a bit to check whether an actual conflict exists 2138c2ecf20Sopenharmony_ci * @warnonly: if set then a conflict isn't fatal (just warn about it), 2148c2ecf20Sopenharmony_ci * otherwise disable the particular GPIO port if a conflict 2158c2ecf20Sopenharmony_ci * is detected 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_cistruct winbond_gpio_port_conflict { 2188c2ecf20Sopenharmony_ci const char *name; 2198c2ecf20Sopenharmony_ci u8 dev; 2208c2ecf20Sopenharmony_ci u8 testreg; 2218c2ecf20Sopenharmony_ci u8 testbit; 2228c2ecf20Sopenharmony_ci bool warnonly; 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/** 2268c2ecf20Sopenharmony_ci * struct winbond_gpio_info - information about a particular GPIO port (device) 2278c2ecf20Sopenharmony_ci * @dev: Super I/O logical device number of the registers 2288c2ecf20Sopenharmony_ci * specified below 2298c2ecf20Sopenharmony_ci * @enablereg: port enable bit register number 2308c2ecf20Sopenharmony_ci * @enablebit: index of a port enable bit 2318c2ecf20Sopenharmony_ci * @outputreg: output driver mode bit register number 2328c2ecf20Sopenharmony_ci * @outputppbit: index of a push-pull output driver mode bit 2338c2ecf20Sopenharmony_ci * @ioreg: data direction register number 2348c2ecf20Sopenharmony_ci * @invreg: pin data inversion register number 2358c2ecf20Sopenharmony_ci * @datareg: pin data register number 2368c2ecf20Sopenharmony_ci * @conflict: description of a device that possibly conflicts with 2378c2ecf20Sopenharmony_ci * this port 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistruct winbond_gpio_info { 2408c2ecf20Sopenharmony_ci u8 dev; 2418c2ecf20Sopenharmony_ci u8 enablereg; 2428c2ecf20Sopenharmony_ci u8 enablebit; 2438c2ecf20Sopenharmony_ci u8 outputreg; 2448c2ecf20Sopenharmony_ci u8 outputppbit; 2458c2ecf20Sopenharmony_ci u8 ioreg; 2468c2ecf20Sopenharmony_ci u8 invreg; 2478c2ecf20Sopenharmony_ci u8 datareg; 2488c2ecf20Sopenharmony_ci struct winbond_gpio_port_conflict conflict; 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic const struct winbond_gpio_info winbond_gpio_infos[6] = { 2528c2ecf20Sopenharmony_ci { /* 0 */ 2538c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_GPIO12, 2548c2ecf20Sopenharmony_ci .enablereg = WB_SIO_GPIO12_REG_ENABLE, 2558c2ecf20Sopenharmony_ci .enablebit = WB_SIO_GPIO12_ENABLE_1, 2568c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_GPIO1_MF, 2578c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_G1MF_G1PP, 2588c2ecf20Sopenharmony_ci .ioreg = WB_SIO_GPIO12_REG_IO1, 2598c2ecf20Sopenharmony_ci .invreg = WB_SIO_GPIO12_REG_INV1, 2608c2ecf20Sopenharmony_ci .datareg = WB_SIO_GPIO12_REG_DATA1, 2618c2ecf20Sopenharmony_ci .conflict = { 2628c2ecf20Sopenharmony_ci .name = "UARTB", 2638c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_UARTB, 2648c2ecf20Sopenharmony_ci .testreg = WB_SIO_UARTB_REG_ENABLE, 2658c2ecf20Sopenharmony_ci .testbit = WB_SIO_UARTB_ENABLE_ON, 2668c2ecf20Sopenharmony_ci .warnonly = true 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci }, 2698c2ecf20Sopenharmony_ci { /* 1 */ 2708c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_GPIO12, 2718c2ecf20Sopenharmony_ci .enablereg = WB_SIO_GPIO12_REG_ENABLE, 2728c2ecf20Sopenharmony_ci .enablebit = WB_SIO_GPIO12_ENABLE_2, 2738c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_GPIO1_MF, 2748c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_G1MF_G2PP, 2758c2ecf20Sopenharmony_ci .ioreg = WB_SIO_GPIO12_REG_IO2, 2768c2ecf20Sopenharmony_ci .invreg = WB_SIO_GPIO12_REG_INV2, 2778c2ecf20Sopenharmony_ci .datareg = WB_SIO_GPIO12_REG_DATA2 2788c2ecf20Sopenharmony_ci /* special conflict handling so doesn't use conflict data */ 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci { /* 2 */ 2818c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_GPIO34, 2828c2ecf20Sopenharmony_ci .enablereg = WB_SIO_GPIO34_REG_ENABLE, 2838c2ecf20Sopenharmony_ci .enablebit = WB_SIO_GPIO34_ENABLE_3, 2848c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_OVTGPIO3456, 2858c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_OG3456_G3PP, 2868c2ecf20Sopenharmony_ci .ioreg = WB_SIO_GPIO34_REG_IO3, 2878c2ecf20Sopenharmony_ci .invreg = WB_SIO_GPIO34_REG_INV3, 2888c2ecf20Sopenharmony_ci .datareg = WB_SIO_GPIO34_REG_DATA3, 2898c2ecf20Sopenharmony_ci .conflict = { 2908c2ecf20Sopenharmony_ci .name = "UARTC", 2918c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_UARTC, 2928c2ecf20Sopenharmony_ci .testreg = WB_SIO_UARTC_REG_ENABLE, 2938c2ecf20Sopenharmony_ci .testbit = WB_SIO_UARTC_ENABLE_ON, 2948c2ecf20Sopenharmony_ci .warnonly = true 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci }, 2978c2ecf20Sopenharmony_ci { /* 3 */ 2988c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_GPIO34, 2998c2ecf20Sopenharmony_ci .enablereg = WB_SIO_GPIO34_REG_ENABLE, 3008c2ecf20Sopenharmony_ci .enablebit = WB_SIO_GPIO34_ENABLE_4, 3018c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_OVTGPIO3456, 3028c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_OG3456_G4PP, 3038c2ecf20Sopenharmony_ci .ioreg = WB_SIO_GPIO34_REG_IO4, 3048c2ecf20Sopenharmony_ci .invreg = WB_SIO_GPIO34_REG_INV4, 3058c2ecf20Sopenharmony_ci .datareg = WB_SIO_GPIO34_REG_DATA4, 3068c2ecf20Sopenharmony_ci .conflict = { 3078c2ecf20Sopenharmony_ci .name = "UARTD", 3088c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_UARTD, 3098c2ecf20Sopenharmony_ci .testreg = WB_SIO_UARTD_REG_ENABLE, 3108c2ecf20Sopenharmony_ci .testbit = WB_SIO_UARTD_ENABLE_ON, 3118c2ecf20Sopenharmony_ci .warnonly = true 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci }, 3148c2ecf20Sopenharmony_ci { /* 4 */ 3158c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_WDGPIO56, 3168c2ecf20Sopenharmony_ci .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, 3178c2ecf20Sopenharmony_ci .enablebit = WB_SIO_WDGPIO56_ENABLE_5, 3188c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_OVTGPIO3456, 3198c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_OG3456_G5PP, 3208c2ecf20Sopenharmony_ci .ioreg = WB_SIO_WDGPIO56_REG_IO5, 3218c2ecf20Sopenharmony_ci .invreg = WB_SIO_WDGPIO56_REG_INV5, 3228c2ecf20Sopenharmony_ci .datareg = WB_SIO_WDGPIO56_REG_DATA5, 3238c2ecf20Sopenharmony_ci .conflict = { 3248c2ecf20Sopenharmony_ci .name = "UARTE", 3258c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_UARTE, 3268c2ecf20Sopenharmony_ci .testreg = WB_SIO_UARTE_REG_ENABLE, 3278c2ecf20Sopenharmony_ci .testbit = WB_SIO_UARTE_ENABLE_ON, 3288c2ecf20Sopenharmony_ci .warnonly = true 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci }, 3318c2ecf20Sopenharmony_ci { /* 5 */ 3328c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_WDGPIO56, 3338c2ecf20Sopenharmony_ci .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, 3348c2ecf20Sopenharmony_ci .enablebit = WB_SIO_WDGPIO56_ENABLE_6, 3358c2ecf20Sopenharmony_ci .outputreg = WB_SIO_REG_OVTGPIO3456, 3368c2ecf20Sopenharmony_ci .outputppbit = WB_SIO_REG_OG3456_G6PP, 3378c2ecf20Sopenharmony_ci .ioreg = WB_SIO_WDGPIO56_REG_IO6, 3388c2ecf20Sopenharmony_ci .invreg = WB_SIO_WDGPIO56_REG_INV6, 3398c2ecf20Sopenharmony_ci .datareg = WB_SIO_WDGPIO56_REG_DATA6, 3408c2ecf20Sopenharmony_ci .conflict = { 3418c2ecf20Sopenharmony_ci .name = "FDC", 3428c2ecf20Sopenharmony_ci .dev = WB_SIO_DEV_NONE, 3438c2ecf20Sopenharmony_ci .testreg = WB_SIO_REG_GLOBAL_OPT, 3448c2ecf20Sopenharmony_ci .testbit = WB_SIO_REG_GO_ENFDC, 3458c2ecf20Sopenharmony_ci .warnonly = false 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci}; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* returns whether changing a pin is allowed */ 3518c2ecf20Sopenharmony_cistatic bool winbond_gpio_get_info(unsigned int *gpio_num, 3528c2ecf20Sopenharmony_ci const struct winbond_gpio_info **info) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci bool allow_changing = true; 3558c2ecf20Sopenharmony_ci unsigned long i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) { 3588c2ecf20Sopenharmony_ci if (*gpio_num < 8) 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci *gpio_num -= 8; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci *info = &winbond_gpio_infos[i]; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * GPIO2 (the second port) shares some pins with a basic PC 3688c2ecf20Sopenharmony_ci * functionality, which is very likely controlled by the firmware. 3698c2ecf20Sopenharmony_ci * Don't allow changing these pins by default. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci if (i == 1) { 3728c2ecf20Sopenharmony_ci if (*gpio_num == 0 && !params.pledgpio) 3738c2ecf20Sopenharmony_ci allow_changing = false; 3748c2ecf20Sopenharmony_ci else if (*gpio_num == 1 && !params.beepgpio) 3758c2ecf20Sopenharmony_ci allow_changing = false; 3768c2ecf20Sopenharmony_ci else if ((*gpio_num == 5 || *gpio_num == 6) && !params.i2cgpio) 3778c2ecf20Sopenharmony_ci allow_changing = false; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return allow_changing; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int winbond_gpio_get(struct gpio_chip *gc, unsigned int offset) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci unsigned long *base = gpiochip_get_data(gc); 3868c2ecf20Sopenharmony_ci const struct winbond_gpio_info *info; 3878c2ecf20Sopenharmony_ci bool val; 3888c2ecf20Sopenharmony_ci int ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci winbond_gpio_get_info(&offset, &info); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = winbond_sio_enter(*base); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci winbond_sio_select_logical(*base, info->dev); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci val = winbond_sio_reg_btest(*base, info->datareg, offset); 3998c2ecf20Sopenharmony_ci if (winbond_sio_reg_btest(*base, info->invreg, offset)) 4008c2ecf20Sopenharmony_ci val = !val; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci winbond_sio_leave(*base); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return val; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int winbond_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned long *base = gpiochip_get_data(gc); 4108c2ecf20Sopenharmony_ci const struct winbond_gpio_info *info; 4118c2ecf20Sopenharmony_ci int ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!winbond_gpio_get_info(&offset, &info)) 4148c2ecf20Sopenharmony_ci return -EACCES; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = winbond_sio_enter(*base); 4178c2ecf20Sopenharmony_ci if (ret) 4188c2ecf20Sopenharmony_ci return ret; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci winbond_sio_select_logical(*base, info->dev); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci winbond_sio_reg_bset(*base, info->ioreg, offset); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci winbond_sio_leave(*base); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int winbond_gpio_direction_out(struct gpio_chip *gc, 4308c2ecf20Sopenharmony_ci unsigned int offset, 4318c2ecf20Sopenharmony_ci int val) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci unsigned long *base = gpiochip_get_data(gc); 4348c2ecf20Sopenharmony_ci const struct winbond_gpio_info *info; 4358c2ecf20Sopenharmony_ci int ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!winbond_gpio_get_info(&offset, &info)) 4388c2ecf20Sopenharmony_ci return -EACCES; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = winbond_sio_enter(*base); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci winbond_sio_select_logical(*base, info->dev); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci winbond_sio_reg_bclear(*base, info->ioreg, offset); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (winbond_sio_reg_btest(*base, info->invreg, offset)) 4498c2ecf20Sopenharmony_ci val = !val; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (val) 4528c2ecf20Sopenharmony_ci winbond_sio_reg_bset(*base, info->datareg, offset); 4538c2ecf20Sopenharmony_ci else 4548c2ecf20Sopenharmony_ci winbond_sio_reg_bclear(*base, info->datareg, offset); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci winbond_sio_leave(*base); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, 4628c2ecf20Sopenharmony_ci int val) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci unsigned long *base = gpiochip_get_data(gc); 4658c2ecf20Sopenharmony_ci const struct winbond_gpio_info *info; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!winbond_gpio_get_info(&offset, &info)) 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (winbond_sio_enter(*base) != 0) 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci winbond_sio_select_logical(*base, info->dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (winbond_sio_reg_btest(*base, info->invreg, offset)) 4768c2ecf20Sopenharmony_ci val = !val; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (val) 4798c2ecf20Sopenharmony_ci winbond_sio_reg_bset(*base, info->datareg, offset); 4808c2ecf20Sopenharmony_ci else 4818c2ecf20Sopenharmony_ci winbond_sio_reg_bclear(*base, info->datareg, offset); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci winbond_sio_leave(*base); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic struct gpio_chip winbond_gpio_chip = { 4878c2ecf20Sopenharmony_ci .base = -1, 4888c2ecf20Sopenharmony_ci .label = WB_GPIO_DRIVER_NAME, 4898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4908c2ecf20Sopenharmony_ci .can_sleep = true, 4918c2ecf20Sopenharmony_ci .get = winbond_gpio_get, 4928c2ecf20Sopenharmony_ci .direction_input = winbond_gpio_direction_in, 4938c2ecf20Sopenharmony_ci .set = winbond_gpio_set, 4948c2ecf20Sopenharmony_ci .direction_output = winbond_gpio_direction_out, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void winbond_gpio_configure_port0_pins(unsigned long base) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci unsigned int val; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci val = winbond_sio_reg_read(base, WB_SIO_REG_GPIO1_MF); 5028c2ecf20Sopenharmony_ci if ((val & WB_SIO_REG_G1MF_FS_MASK) == WB_SIO_REG_G1MF_FS_GPIO1) 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci pr_warn("GPIO1 pins were connected to something else (%.2x), fixing\n", 5068c2ecf20Sopenharmony_ci val); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci val &= ~WB_SIO_REG_G1MF_FS_MASK; 5098c2ecf20Sopenharmony_ci val |= WB_SIO_REG_G1MF_FS_GPIO1; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci winbond_sio_reg_write(base, WB_SIO_REG_GPIO1_MF, val); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void winbond_gpio_configure_port1_check_i2c(unsigned long base) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci params.i2cgpio = !winbond_sio_reg_btest(base, WB_SIO_REG_I2C_PS, 5178c2ecf20Sopenharmony_ci WB_SIO_REG_I2CPS_I2CFS); 5188c2ecf20Sopenharmony_ci if (!params.i2cgpio) 5198c2ecf20Sopenharmony_ci pr_warn("disabling GPIO2.5 and GPIO2.6 as I2C is enabled\n"); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic bool winbond_gpio_configure_port(unsigned long base, unsigned int idx) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci const struct winbond_gpio_info *info = &winbond_gpio_infos[idx]; 5258c2ecf20Sopenharmony_ci const struct winbond_gpio_port_conflict *conflict = &info->conflict; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* is there a possible conflicting device defined? */ 5288c2ecf20Sopenharmony_ci if (conflict->name != NULL) { 5298c2ecf20Sopenharmony_ci if (conflict->dev != WB_SIO_DEV_NONE) 5308c2ecf20Sopenharmony_ci winbond_sio_select_logical(base, conflict->dev); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (winbond_sio_reg_btest(base, conflict->testreg, 5338c2ecf20Sopenharmony_ci conflict->testbit)) { 5348c2ecf20Sopenharmony_ci if (conflict->warnonly) 5358c2ecf20Sopenharmony_ci pr_warn("enabled GPIO%u share pins with active %s\n", 5368c2ecf20Sopenharmony_ci idx + 1, conflict->name); 5378c2ecf20Sopenharmony_ci else { 5388c2ecf20Sopenharmony_ci pr_warn("disabling GPIO%u as %s is enabled\n", 5398c2ecf20Sopenharmony_ci idx + 1, conflict->name); 5408c2ecf20Sopenharmony_ci return false; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* GPIO1 and GPIO2 need some (additional) special handling */ 5468c2ecf20Sopenharmony_ci if (idx == 0) 5478c2ecf20Sopenharmony_ci winbond_gpio_configure_port0_pins(base); 5488c2ecf20Sopenharmony_ci else if (idx == 1) 5498c2ecf20Sopenharmony_ci winbond_gpio_configure_port1_check_i2c(base); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci winbond_sio_select_logical(base, info->dev); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci winbond_sio_reg_bset(base, info->enablereg, info->enablebit); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (params.ppgpios & BIT(idx)) 5568c2ecf20Sopenharmony_ci winbond_sio_reg_bset(base, info->outputreg, 5578c2ecf20Sopenharmony_ci info->outputppbit); 5588c2ecf20Sopenharmony_ci else if (params.odgpios & BIT(idx)) 5598c2ecf20Sopenharmony_ci winbond_sio_reg_bclear(base, info->outputreg, 5608c2ecf20Sopenharmony_ci info->outputppbit); 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci pr_notice("GPIO%u pins are %s\n", idx + 1, 5638c2ecf20Sopenharmony_ci winbond_sio_reg_btest(base, info->outputreg, 5648c2ecf20Sopenharmony_ci info->outputppbit) ? 5658c2ecf20Sopenharmony_ci "push-pull" : 5668c2ecf20Sopenharmony_ci "open drain"); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return true; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int winbond_gpio_configure(unsigned long base) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci unsigned long i; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) 5768c2ecf20Sopenharmony_ci if (!winbond_gpio_configure_port(base, i)) 5778c2ecf20Sopenharmony_ci __clear_bit(i, ¶ms.gpios); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!params.gpios) { 5808c2ecf20Sopenharmony_ci pr_err("please use 'gpios' module parameter to select some active GPIO ports to enable\n"); 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int winbond_gpio_check_chip(unsigned long base) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci int ret; 5908c2ecf20Sopenharmony_ci unsigned int chip; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci ret = winbond_sio_enter(base); 5938c2ecf20Sopenharmony_ci if (ret) 5948c2ecf20Sopenharmony_ci return ret; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci chip = winbond_sio_reg_read(base, WB_SIO_REG_CHIP_MSB) << 8; 5978c2ecf20Sopenharmony_ci chip |= winbond_sio_reg_read(base, WB_SIO_REG_CHIP_LSB); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pr_notice("chip ID at %lx is %.4x\n", base, chip); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if ((chip & WB_SIO_CHIP_ID_W83627UHG_MASK) != 6028c2ecf20Sopenharmony_ci WB_SIO_CHIP_ID_W83627UHG) { 6038c2ecf20Sopenharmony_ci pr_err("not an our chip\n"); 6048c2ecf20Sopenharmony_ci ret = -ENODEV; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci winbond_sio_leave(base); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int winbond_gpio_imatch(struct device *dev, unsigned int id) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci unsigned long gpios_rem; 6158c2ecf20Sopenharmony_ci int ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci gpios_rem = params.gpios & ~GENMASK(ARRAY_SIZE(winbond_gpio_infos) - 1, 6188c2ecf20Sopenharmony_ci 0); 6198c2ecf20Sopenharmony_ci if (gpios_rem) { 6208c2ecf20Sopenharmony_ci pr_warn("unknown ports (%lx) enabled in GPIO ports bitmask\n", 6218c2ecf20Sopenharmony_ci gpios_rem); 6228c2ecf20Sopenharmony_ci params.gpios &= ~gpios_rem; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (params.ppgpios & params.odgpios) { 6268c2ecf20Sopenharmony_ci pr_err("some GPIO ports are set both to push-pull and open drain mode at the same time\n"); 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (params.base != 0) 6318c2ecf20Sopenharmony_ci return winbond_gpio_check_chip(params.base) == 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* 6348c2ecf20Sopenharmony_ci * if the 'base' module parameter is unset probe two chip default 6358c2ecf20Sopenharmony_ci * I/O port bases 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci params.base = WB_SIO_BASE; 6388c2ecf20Sopenharmony_ci ret = winbond_gpio_check_chip(params.base); 6398c2ecf20Sopenharmony_ci if (ret == 0) 6408c2ecf20Sopenharmony_ci return 1; 6418c2ecf20Sopenharmony_ci if (ret != -ENODEV && ret != -EBUSY) 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci params.base = WB_SIO_BASE_HIGH; 6458c2ecf20Sopenharmony_ci return winbond_gpio_check_chip(params.base) == 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int winbond_gpio_iprobe(struct device *dev, unsigned int id) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci int ret; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (params.base == 0) 6538c2ecf20Sopenharmony_ci return -EINVAL; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = winbond_sio_enter(params.base); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ret = winbond_gpio_configure(params.base); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci winbond_sio_leave(params.base); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (ret) 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* 6678c2ecf20Sopenharmony_ci * Add 8 gpios for every GPIO port that was enabled in gpios 6688c2ecf20Sopenharmony_ci * module parameter (that wasn't disabled earlier in 6698c2ecf20Sopenharmony_ci * winbond_gpio_configure() & co. due to, for example, a pin conflict). 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci winbond_gpio_chip.ngpio = hweight_long(params.gpios) * 8; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* 6748c2ecf20Sopenharmony_ci * GPIO6 port has only 5 pins, so if it is enabled we have to adjust 6758c2ecf20Sopenharmony_ci * the total count appropriately 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci if (params.gpios & BIT(5)) 6788c2ecf20Sopenharmony_ci winbond_gpio_chip.ngpio -= (8 - 5); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci winbond_gpio_chip.parent = dev; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return devm_gpiochip_add_data(dev, &winbond_gpio_chip, ¶ms.base); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic struct isa_driver winbond_gpio_idriver = { 6868c2ecf20Sopenharmony_ci .driver = { 6878c2ecf20Sopenharmony_ci .name = WB_GPIO_DRIVER_NAME, 6888c2ecf20Sopenharmony_ci }, 6898c2ecf20Sopenharmony_ci .match = winbond_gpio_imatch, 6908c2ecf20Sopenharmony_ci .probe = winbond_gpio_iprobe, 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cimodule_isa_driver(winbond_gpio_idriver, 1); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cimodule_param_named(base, params.base, ulong, 0444); 6968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(base, 6978c2ecf20Sopenharmony_ci "I/O port base (when unset - probe chip default ones)"); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci/* This parameter sets which GPIO devices (ports) we enable */ 7008c2ecf20Sopenharmony_cimodule_param_named(gpios, params.gpios, ulong, 0444); 7018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gpios, 7028c2ecf20Sopenharmony_ci "bitmask of GPIO ports to enable (bit 0 - GPIO1, bit 1 - GPIO2, etc."); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* 7058c2ecf20Sopenharmony_ci * These two parameters below set how we configure GPIO ports output drivers. 7068c2ecf20Sopenharmony_ci * It can't be a one bitmask since we need three values per port: push-pull, 7078c2ecf20Sopenharmony_ci * open-drain and keep as-is (this is the default). 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_cimodule_param_named(ppgpios, params.ppgpios, ulong, 0444); 7108c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ppgpios, 7118c2ecf20Sopenharmony_ci "bitmask of GPIO ports to set to push-pull mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cimodule_param_named(odgpios, params.odgpios, ulong, 0444); 7148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(odgpios, 7158c2ecf20Sopenharmony_ci "bitmask of GPIO ports to set to open drain mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/* 7188c2ecf20Sopenharmony_ci * GPIO2.0 and GPIO2.1 control a basic PC functionality that we 7198c2ecf20Sopenharmony_ci * don't allow tinkering with by default (it is very likely that the 7208c2ecf20Sopenharmony_ci * firmware owns these pins). 7218c2ecf20Sopenharmony_ci * These two parameters below allow overriding these prohibitions. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_cimodule_param_named(pledgpio, params.pledgpio, bool, 0644); 7248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pledgpio, 7258c2ecf20Sopenharmony_ci "enable changing value of GPIO2.0 bit (Power LED), default no."); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cimodule_param_named(beepgpio, params.beepgpio, bool, 0644); 7288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(beepgpio, 7298c2ecf20Sopenharmony_ci "enable changing value of GPIO2.1 bit (BEEP), default no."); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); 7328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for Winbond Super I/O chips"); 7338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 734