18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* MCP23S08 SPI/I2C GPIO driver */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/bitops.h> 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci#include <linux/device.h> 78c2ecf20Sopenharmony_ci#include <linux/mutex.h> 88c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 178c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 188c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "pinctrl-mcp23s08.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Registers are all 8 bits wide. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * The mcp23s17 has twice as many bits, and can be configured to work 258c2ecf20Sopenharmony_ci * with either 16 bit registers or with two adjacent 8 bit banks. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci#define MCP_IODIR 0x00 /* init/reset: all ones */ 288c2ecf20Sopenharmony_ci#define MCP_IPOL 0x01 298c2ecf20Sopenharmony_ci#define MCP_GPINTEN 0x02 308c2ecf20Sopenharmony_ci#define MCP_DEFVAL 0x03 318c2ecf20Sopenharmony_ci#define MCP_INTCON 0x04 328c2ecf20Sopenharmony_ci#define MCP_IOCON 0x05 338c2ecf20Sopenharmony_ci# define IOCON_MIRROR (1 << 6) 348c2ecf20Sopenharmony_ci# define IOCON_SEQOP (1 << 5) 358c2ecf20Sopenharmony_ci# define IOCON_HAEN (1 << 3) 368c2ecf20Sopenharmony_ci# define IOCON_ODR (1 << 2) 378c2ecf20Sopenharmony_ci# define IOCON_INTPOL (1 << 1) 388c2ecf20Sopenharmony_ci# define IOCON_INTCC (1) 398c2ecf20Sopenharmony_ci#define MCP_GPPU 0x06 408c2ecf20Sopenharmony_ci#define MCP_INTF 0x07 418c2ecf20Sopenharmony_ci#define MCP_INTCAP 0x08 428c2ecf20Sopenharmony_ci#define MCP_GPIO 0x09 438c2ecf20Sopenharmony_ci#define MCP_OLAT 0x0a 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const struct reg_default mcp23x08_defaults[] = { 468c2ecf20Sopenharmony_ci {.reg = MCP_IODIR, .def = 0xff}, 478c2ecf20Sopenharmony_ci {.reg = MCP_IPOL, .def = 0x00}, 488c2ecf20Sopenharmony_ci {.reg = MCP_GPINTEN, .def = 0x00}, 498c2ecf20Sopenharmony_ci {.reg = MCP_DEFVAL, .def = 0x00}, 508c2ecf20Sopenharmony_ci {.reg = MCP_INTCON, .def = 0x00}, 518c2ecf20Sopenharmony_ci {.reg = MCP_IOCON, .def = 0x00}, 528c2ecf20Sopenharmony_ci {.reg = MCP_GPPU, .def = 0x00}, 538c2ecf20Sopenharmony_ci {.reg = MCP_OLAT, .def = 0x00}, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct regmap_range mcp23x08_volatile_range = { 578c2ecf20Sopenharmony_ci .range_min = MCP_INTF, 588c2ecf20Sopenharmony_ci .range_max = MCP_GPIO, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const struct regmap_access_table mcp23x08_volatile_table = { 628c2ecf20Sopenharmony_ci .yes_ranges = &mcp23x08_volatile_range, 638c2ecf20Sopenharmony_ci .n_yes_ranges = 1, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const struct regmap_range mcp23x08_precious_range = { 678c2ecf20Sopenharmony_ci .range_min = MCP_GPIO, 688c2ecf20Sopenharmony_ci .range_max = MCP_GPIO, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const struct regmap_access_table mcp23x08_precious_table = { 728c2ecf20Sopenharmony_ci .yes_ranges = &mcp23x08_precious_range, 738c2ecf20Sopenharmony_ci .n_yes_ranges = 1, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciconst struct regmap_config mcp23x08_regmap = { 778c2ecf20Sopenharmony_ci .reg_bits = 8, 788c2ecf20Sopenharmony_ci .val_bits = 8, 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci .reg_stride = 1, 818c2ecf20Sopenharmony_ci .volatile_table = &mcp23x08_volatile_table, 828c2ecf20Sopenharmony_ci .precious_table = &mcp23x08_precious_table, 838c2ecf20Sopenharmony_ci .reg_defaults = mcp23x08_defaults, 848c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(mcp23x08_defaults), 858c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 868c2ecf20Sopenharmony_ci .max_register = MCP_OLAT, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mcp23x08_regmap); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct reg_default mcp23x17_defaults[] = { 918c2ecf20Sopenharmony_ci {.reg = MCP_IODIR << 1, .def = 0xffff}, 928c2ecf20Sopenharmony_ci {.reg = MCP_IPOL << 1, .def = 0x0000}, 938c2ecf20Sopenharmony_ci {.reg = MCP_GPINTEN << 1, .def = 0x0000}, 948c2ecf20Sopenharmony_ci {.reg = MCP_DEFVAL << 1, .def = 0x0000}, 958c2ecf20Sopenharmony_ci {.reg = MCP_INTCON << 1, .def = 0x0000}, 968c2ecf20Sopenharmony_ci {.reg = MCP_IOCON << 1, .def = 0x0000}, 978c2ecf20Sopenharmony_ci {.reg = MCP_GPPU << 1, .def = 0x0000}, 988c2ecf20Sopenharmony_ci {.reg = MCP_OLAT << 1, .def = 0x0000}, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const struct regmap_range mcp23x17_volatile_range = { 1028c2ecf20Sopenharmony_ci .range_min = MCP_INTF << 1, 1038c2ecf20Sopenharmony_ci .range_max = MCP_GPIO << 1, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct regmap_access_table mcp23x17_volatile_table = { 1078c2ecf20Sopenharmony_ci .yes_ranges = &mcp23x17_volatile_range, 1088c2ecf20Sopenharmony_ci .n_yes_ranges = 1, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct regmap_range mcp23x17_precious_range = { 1128c2ecf20Sopenharmony_ci .range_min = MCP_INTCAP << 1, 1138c2ecf20Sopenharmony_ci .range_max = MCP_GPIO << 1, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const struct regmap_access_table mcp23x17_precious_table = { 1178c2ecf20Sopenharmony_ci .yes_ranges = &mcp23x17_precious_range, 1188c2ecf20Sopenharmony_ci .n_yes_ranges = 1, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciconst struct regmap_config mcp23x17_regmap = { 1228c2ecf20Sopenharmony_ci .reg_bits = 8, 1238c2ecf20Sopenharmony_ci .val_bits = 16, 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci .reg_stride = 2, 1268c2ecf20Sopenharmony_ci .max_register = MCP_OLAT << 1, 1278c2ecf20Sopenharmony_ci .volatile_table = &mcp23x17_volatile_table, 1288c2ecf20Sopenharmony_ci .precious_table = &mcp23x17_precious_table, 1298c2ecf20Sopenharmony_ci .reg_defaults = mcp23x17_defaults, 1308c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(mcp23x17_defaults), 1318c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 1328c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mcp23x17_regmap); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int mcp_set_mask(struct mcp23s08 *mcp, unsigned int reg, 1478c2ecf20Sopenharmony_ci unsigned int mask, bool enabled) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u16 val = enabled ? 0xffff : 0x0000; 1508c2ecf20Sopenharmony_ci return regmap_update_bits(mcp->regmap, reg << mcp->reg_shift, 1518c2ecf20Sopenharmony_ci mask, val); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int mcp_set_bit(struct mcp23s08 *mcp, unsigned int reg, 1558c2ecf20Sopenharmony_ci unsigned int pin, bool enabled) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u16 mask = BIT(pin); 1588c2ecf20Sopenharmony_ci return mcp_set_mask(mcp, reg, mask, enabled); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct pinctrl_pin_desc mcp23x08_pins[] = { 1628c2ecf20Sopenharmony_ci PINCTRL_PIN(0, "gpio0"), 1638c2ecf20Sopenharmony_ci PINCTRL_PIN(1, "gpio1"), 1648c2ecf20Sopenharmony_ci PINCTRL_PIN(2, "gpio2"), 1658c2ecf20Sopenharmony_ci PINCTRL_PIN(3, "gpio3"), 1668c2ecf20Sopenharmony_ci PINCTRL_PIN(4, "gpio4"), 1678c2ecf20Sopenharmony_ci PINCTRL_PIN(5, "gpio5"), 1688c2ecf20Sopenharmony_ci PINCTRL_PIN(6, "gpio6"), 1698c2ecf20Sopenharmony_ci PINCTRL_PIN(7, "gpio7"), 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct pinctrl_pin_desc mcp23x17_pins[] = { 1738c2ecf20Sopenharmony_ci PINCTRL_PIN(0, "gpio0"), 1748c2ecf20Sopenharmony_ci PINCTRL_PIN(1, "gpio1"), 1758c2ecf20Sopenharmony_ci PINCTRL_PIN(2, "gpio2"), 1768c2ecf20Sopenharmony_ci PINCTRL_PIN(3, "gpio3"), 1778c2ecf20Sopenharmony_ci PINCTRL_PIN(4, "gpio4"), 1788c2ecf20Sopenharmony_ci PINCTRL_PIN(5, "gpio5"), 1798c2ecf20Sopenharmony_ci PINCTRL_PIN(6, "gpio6"), 1808c2ecf20Sopenharmony_ci PINCTRL_PIN(7, "gpio7"), 1818c2ecf20Sopenharmony_ci PINCTRL_PIN(8, "gpio8"), 1828c2ecf20Sopenharmony_ci PINCTRL_PIN(9, "gpio9"), 1838c2ecf20Sopenharmony_ci PINCTRL_PIN(10, "gpio10"), 1848c2ecf20Sopenharmony_ci PINCTRL_PIN(11, "gpio11"), 1858c2ecf20Sopenharmony_ci PINCTRL_PIN(12, "gpio12"), 1868c2ecf20Sopenharmony_ci PINCTRL_PIN(13, "gpio13"), 1878c2ecf20Sopenharmony_ci PINCTRL_PIN(14, "gpio14"), 1888c2ecf20Sopenharmony_ci PINCTRL_PIN(15, "gpio15"), 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int mcp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const char *mcp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 1978c2ecf20Sopenharmony_ci unsigned int group) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return NULL; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int mcp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 2038c2ecf20Sopenharmony_ci unsigned int group, 2048c2ecf20Sopenharmony_ci const unsigned int **pins, 2058c2ecf20Sopenharmony_ci unsigned int *num_pins) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci return -ENOTSUPP; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct pinctrl_ops mcp_pinctrl_ops = { 2118c2ecf20Sopenharmony_ci .get_groups_count = mcp_pinctrl_get_groups_count, 2128c2ecf20Sopenharmony_ci .get_group_name = mcp_pinctrl_get_group_name, 2138c2ecf20Sopenharmony_ci .get_group_pins = mcp_pinctrl_get_group_pins, 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2158c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 2168c2ecf20Sopenharmony_ci .dt_free_map = pinconf_generic_dt_free_map, 2178c2ecf20Sopenharmony_ci#endif 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int mcp_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, 2218c2ecf20Sopenharmony_ci unsigned long *config) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); 2248c2ecf20Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*config); 2258c2ecf20Sopenharmony_ci unsigned int data, status; 2268c2ecf20Sopenharmony_ci int ret; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci switch (param) { 2298c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 2308c2ecf20Sopenharmony_ci ret = mcp_read(mcp, MCP_GPPU, &data); 2318c2ecf20Sopenharmony_ci if (ret < 0) 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci status = (data & BIT(pin)) ? 1 : 0; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci return -ENOTSUPP; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci *config = 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return status ? 0 : -EINVAL; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int mcp_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, 2458c2ecf20Sopenharmony_ci unsigned long *configs, unsigned int num_configs) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = pinctrl_dev_get_drvdata(pctldev); 2488c2ecf20Sopenharmony_ci enum pin_config_param param; 2498c2ecf20Sopenharmony_ci u32 arg; 2508c2ecf20Sopenharmony_ci int ret = 0; 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 2548c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 2558c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci switch (param) { 2588c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 2598c2ecf20Sopenharmony_ci ret = mcp_set_bit(mcp, MCP_GPPU, pin, arg); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci dev_dbg(mcp->dev, "Invalid config param %04x\n", param); 2638c2ecf20Sopenharmony_ci return -ENOTSUPP; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct pinconf_ops mcp_pinconf_ops = { 2718c2ecf20Sopenharmony_ci .pin_config_get = mcp_pinconf_get, 2728c2ecf20Sopenharmony_ci .pin_config_set = mcp_pinconf_set, 2738c2ecf20Sopenharmony_ci .is_generic = true, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------*/ 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(chip); 2818c2ecf20Sopenharmony_ci int status; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 2848c2ecf20Sopenharmony_ci status = mcp_set_bit(mcp, MCP_IODIR, offset, true); 2858c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return status; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int mcp23s08_get(struct gpio_chip *chip, unsigned offset) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(chip); 2938c2ecf20Sopenharmony_ci int status, ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* REVISIT reading this clears any IRQ ... */ 2988c2ecf20Sopenharmony_ci ret = mcp_read(mcp, MCP_GPIO, &status); 2998c2ecf20Sopenharmony_ci if (ret < 0) 3008c2ecf20Sopenharmony_ci status = 0; 3018c2ecf20Sopenharmony_ci else { 3028c2ecf20Sopenharmony_ci mcp->cached_gpio = status; 3038c2ecf20Sopenharmony_ci status = !!(status & (1 << offset)); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 3078c2ecf20Sopenharmony_ci return status; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, bool value) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci return mcp_set_mask(mcp, MCP_OLAT, mask, value); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(chip); 3188c2ecf20Sopenharmony_ci unsigned mask = BIT(offset); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 3218c2ecf20Sopenharmony_ci __mcp23s08_set(mcp, mask, !!value); 3228c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int 3268c2ecf20Sopenharmony_cimcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(chip); 3298c2ecf20Sopenharmony_ci unsigned mask = BIT(offset); 3308c2ecf20Sopenharmony_ci int status; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 3338c2ecf20Sopenharmony_ci status = __mcp23s08_set(mcp, mask, value); 3348c2ecf20Sopenharmony_ci if (status == 0) { 3358c2ecf20Sopenharmony_ci status = mcp_set_mask(mcp, MCP_IODIR, mask, false); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 3388c2ecf20Sopenharmony_ci return status; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------*/ 3428c2ecf20Sopenharmony_cistatic irqreturn_t mcp23s08_irq(int irq, void *data) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = data; 3458c2ecf20Sopenharmony_ci int intcap, intcon, intf, i, gpio, gpio_orig, intcap_mask, defval; 3468c2ecf20Sopenharmony_ci unsigned int child_irq; 3478c2ecf20Sopenharmony_ci bool intf_set, intcap_changed, gpio_bit_changed, 3488c2ecf20Sopenharmony_ci defval_changed, gpio_set; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 3518c2ecf20Sopenharmony_ci if (mcp_read(mcp, MCP_INTF, &intf)) 3528c2ecf20Sopenharmony_ci goto unlock; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (intf == 0) { 3558c2ecf20Sopenharmony_ci /* There is no interrupt pending */ 3568c2ecf20Sopenharmony_ci goto unlock; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (mcp_read(mcp, MCP_INTCAP, &intcap)) 3608c2ecf20Sopenharmony_ci goto unlock; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (mcp_read(mcp, MCP_INTCON, &intcon)) 3638c2ecf20Sopenharmony_ci goto unlock; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (mcp_read(mcp, MCP_DEFVAL, &defval)) 3668c2ecf20Sopenharmony_ci goto unlock; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* This clears the interrupt(configurable on S18) */ 3698c2ecf20Sopenharmony_ci if (mcp_read(mcp, MCP_GPIO, &gpio)) 3708c2ecf20Sopenharmony_ci goto unlock; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci gpio_orig = mcp->cached_gpio; 3738c2ecf20Sopenharmony_ci mcp->cached_gpio = gpio; 3748c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev_dbg(mcp->chip.parent, 3778c2ecf20Sopenharmony_ci "intcap 0x%04X intf 0x%04X gpio_orig 0x%04X gpio 0x%04X\n", 3788c2ecf20Sopenharmony_ci intcap, intf, gpio_orig, gpio); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (i = 0; i < mcp->chip.ngpio; i++) { 3818c2ecf20Sopenharmony_ci /* We must check all of the inputs on the chip, 3828c2ecf20Sopenharmony_ci * otherwise we may not notice a change on >=2 pins. 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * On at least the mcp23s17, INTCAP is only updated 3858c2ecf20Sopenharmony_ci * one byte at a time(INTCAPA and INTCAPB are 3868c2ecf20Sopenharmony_ci * not written to at the same time - only on a per-bank 3878c2ecf20Sopenharmony_ci * basis). 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * INTF only contains the single bit that caused the 3908c2ecf20Sopenharmony_ci * interrupt per-bank. On the mcp23s17, there is 3918c2ecf20Sopenharmony_ci * INTFA and INTFB. If two pins are changed on the A 3928c2ecf20Sopenharmony_ci * side at the same time, INTF will only have one bit 3938c2ecf20Sopenharmony_ci * set. If one pin on the A side and one pin on the B 3948c2ecf20Sopenharmony_ci * side are changed at the same time, INTF will have 3958c2ecf20Sopenharmony_ci * two bits set. Thus, INTF can't be the only check 3968c2ecf20Sopenharmony_ci * to see if the input has changed. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci intf_set = intf & BIT(i); 4008c2ecf20Sopenharmony_ci if (i < 8 && intf_set) 4018c2ecf20Sopenharmony_ci intcap_mask = 0x00FF; 4028c2ecf20Sopenharmony_ci else if (i >= 8 && intf_set) 4038c2ecf20Sopenharmony_ci intcap_mask = 0xFF00; 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci intcap_mask = 0x00; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci intcap_changed = (intcap_mask & 4088c2ecf20Sopenharmony_ci (intcap & BIT(i))) != 4098c2ecf20Sopenharmony_ci (intcap_mask & (BIT(i) & gpio_orig)); 4108c2ecf20Sopenharmony_ci gpio_set = BIT(i) & gpio; 4118c2ecf20Sopenharmony_ci gpio_bit_changed = (BIT(i) & gpio_orig) != 4128c2ecf20Sopenharmony_ci (BIT(i) & gpio); 4138c2ecf20Sopenharmony_ci defval_changed = (BIT(i) & intcon) && 4148c2ecf20Sopenharmony_ci ((BIT(i) & gpio) != 4158c2ecf20Sopenharmony_ci (BIT(i) & defval)); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (((gpio_bit_changed || intcap_changed) && 4188c2ecf20Sopenharmony_ci (BIT(i) & mcp->irq_rise) && gpio_set) || 4198c2ecf20Sopenharmony_ci ((gpio_bit_changed || intcap_changed) && 4208c2ecf20Sopenharmony_ci (BIT(i) & mcp->irq_fall) && !gpio_set) || 4218c2ecf20Sopenharmony_ci defval_changed) { 4228c2ecf20Sopenharmony_ci child_irq = irq_find_mapping(mcp->chip.irq.domain, i); 4238c2ecf20Sopenharmony_ci handle_nested_irq(child_irq); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciunlock: 4308c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 4318c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void mcp23s08_irq_mask(struct irq_data *data) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 4378c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(gc); 4388c2ecf20Sopenharmony_ci unsigned int pos = data->hwirq; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_GPINTEN, pos, false); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void mcp23s08_irq_unmask(struct irq_data *data) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 4468c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(gc); 4478c2ecf20Sopenharmony_ci unsigned int pos = data->hwirq; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_GPINTEN, pos, true); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 4558c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(gc); 4568c2ecf20Sopenharmony_ci unsigned int pos = data->hwirq; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { 4598c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_INTCON, pos, false); 4608c2ecf20Sopenharmony_ci mcp->irq_rise |= BIT(pos); 4618c2ecf20Sopenharmony_ci mcp->irq_fall |= BIT(pos); 4628c2ecf20Sopenharmony_ci } else if (type & IRQ_TYPE_EDGE_RISING) { 4638c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_INTCON, pos, false); 4648c2ecf20Sopenharmony_ci mcp->irq_rise |= BIT(pos); 4658c2ecf20Sopenharmony_ci mcp->irq_fall &= ~BIT(pos); 4668c2ecf20Sopenharmony_ci } else if (type & IRQ_TYPE_EDGE_FALLING) { 4678c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_INTCON, pos, false); 4688c2ecf20Sopenharmony_ci mcp->irq_rise &= ~BIT(pos); 4698c2ecf20Sopenharmony_ci mcp->irq_fall |= BIT(pos); 4708c2ecf20Sopenharmony_ci } else if (type & IRQ_TYPE_LEVEL_HIGH) { 4718c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_INTCON, pos, true); 4728c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_DEFVAL, pos, false); 4738c2ecf20Sopenharmony_ci } else if (type & IRQ_TYPE_LEVEL_LOW) { 4748c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_INTCON, pos, true); 4758c2ecf20Sopenharmony_ci mcp_set_bit(mcp, MCP_DEFVAL, pos, true); 4768c2ecf20Sopenharmony_ci } else 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void mcp23s08_irq_bus_lock(struct irq_data *data) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 4858c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(gc); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mutex_lock(&mcp->lock); 4888c2ecf20Sopenharmony_ci regcache_cache_only(mcp->regmap, true); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void mcp23s08_irq_bus_unlock(struct irq_data *data) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 4948c2ecf20Sopenharmony_ci struct mcp23s08 *mcp = gpiochip_get_data(gc); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci regcache_cache_only(mcp->regmap, false); 4978c2ecf20Sopenharmony_ci regcache_sync(mcp->regmap); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci mutex_unlock(&mcp->lock); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int mcp23s08_irq_setup(struct mcp23s08 *mcp) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct gpio_chip *chip = &mcp->chip; 5058c2ecf20Sopenharmony_ci int err; 5068c2ecf20Sopenharmony_ci unsigned long irqflags = IRQF_ONESHOT | IRQF_SHARED; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (mcp->irq_active_high) 5098c2ecf20Sopenharmony_ci irqflags |= IRQF_TRIGGER_HIGH; 5108c2ecf20Sopenharmony_ci else 5118c2ecf20Sopenharmony_ci irqflags |= IRQF_TRIGGER_LOW; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(chip->parent, mcp->irq, NULL, 5148c2ecf20Sopenharmony_ci mcp23s08_irq, 5158c2ecf20Sopenharmony_ci irqflags, dev_name(chip->parent), mcp); 5168c2ecf20Sopenharmony_ci if (err != 0) { 5178c2ecf20Sopenharmony_ci dev_err(chip->parent, "unable to request IRQ#%d: %d\n", 5188c2ecf20Sopenharmony_ci mcp->irq, err); 5198c2ecf20Sopenharmony_ci return err; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------*/ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciint mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, 5288c2ecf20Sopenharmony_ci unsigned int addr, unsigned int type, unsigned int base) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci int status, ret; 5318c2ecf20Sopenharmony_ci bool mirror = false; 5328c2ecf20Sopenharmony_ci bool open_drain = false; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci mutex_init(&mcp->lock); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mcp->dev = dev; 5378c2ecf20Sopenharmony_ci mcp->addr = addr; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci mcp->irq_active_high = false; 5408c2ecf20Sopenharmony_ci mcp->irq_chip.name = dev_name(dev); 5418c2ecf20Sopenharmony_ci mcp->irq_chip.irq_mask = mcp23s08_irq_mask; 5428c2ecf20Sopenharmony_ci mcp->irq_chip.irq_unmask = mcp23s08_irq_unmask; 5438c2ecf20Sopenharmony_ci mcp->irq_chip.irq_set_type = mcp23s08_irq_set_type; 5448c2ecf20Sopenharmony_ci mcp->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock; 5458c2ecf20Sopenharmony_ci mcp->irq_chip.irq_bus_sync_unlock = mcp23s08_irq_bus_unlock; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci mcp->chip.direction_input = mcp23s08_direction_input; 5488c2ecf20Sopenharmony_ci mcp->chip.get = mcp23s08_get; 5498c2ecf20Sopenharmony_ci mcp->chip.direction_output = mcp23s08_direction_output; 5508c2ecf20Sopenharmony_ci mcp->chip.set = mcp23s08_set; 5518c2ecf20Sopenharmony_ci#ifdef CONFIG_OF_GPIO 5528c2ecf20Sopenharmony_ci mcp->chip.of_gpio_n_cells = 2; 5538c2ecf20Sopenharmony_ci mcp->chip.of_node = dev->of_node; 5548c2ecf20Sopenharmony_ci#endif 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci mcp->chip.base = base; 5578c2ecf20Sopenharmony_ci mcp->chip.can_sleep = true; 5588c2ecf20Sopenharmony_ci mcp->chip.parent = dev; 5598c2ecf20Sopenharmony_ci mcp->chip.owner = THIS_MODULE; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, 5628c2ecf20Sopenharmony_ci * and MCP_IOCON.HAEN = 1, so we work with all chips. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci ret = mcp_read(mcp, MCP_IOCON, &status); 5668c2ecf20Sopenharmony_ci if (ret < 0) 5678c2ecf20Sopenharmony_ci return dev_err_probe(dev, ret, "can't identify chip %d\n", addr); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci mcp->irq_controller = 5708c2ecf20Sopenharmony_ci device_property_read_bool(dev, "interrupt-controller"); 5718c2ecf20Sopenharmony_ci if (mcp->irq && mcp->irq_controller) { 5728c2ecf20Sopenharmony_ci mcp->irq_active_high = 5738c2ecf20Sopenharmony_ci device_property_read_bool(dev, 5748c2ecf20Sopenharmony_ci "microchip,irq-active-high"); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci mirror = device_property_read_bool(dev, "microchip,irq-mirror"); 5778c2ecf20Sopenharmony_ci open_drain = device_property_read_bool(dev, "drive-open-drain"); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror || 5818c2ecf20Sopenharmony_ci mcp->irq_active_high || open_drain) { 5828c2ecf20Sopenharmony_ci /* mcp23s17 has IOCON twice, make sure they are in sync */ 5838c2ecf20Sopenharmony_ci status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); 5848c2ecf20Sopenharmony_ci status |= IOCON_HAEN | (IOCON_HAEN << 8); 5858c2ecf20Sopenharmony_ci if (mcp->irq_active_high) 5868c2ecf20Sopenharmony_ci status |= IOCON_INTPOL | (IOCON_INTPOL << 8); 5878c2ecf20Sopenharmony_ci else 5888c2ecf20Sopenharmony_ci status &= ~(IOCON_INTPOL | (IOCON_INTPOL << 8)); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (mirror) 5918c2ecf20Sopenharmony_ci status |= IOCON_MIRROR | (IOCON_MIRROR << 8); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (open_drain) 5948c2ecf20Sopenharmony_ci status |= IOCON_ODR | (IOCON_ODR << 8); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (type == MCP_TYPE_S18 || type == MCP_TYPE_018) 5978c2ecf20Sopenharmony_ci status |= IOCON_INTCC | (IOCON_INTCC << 8); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci ret = mcp_write(mcp, MCP_IOCON, status); 6008c2ecf20Sopenharmony_ci if (ret < 0) 6018c2ecf20Sopenharmony_ci return dev_err_probe(dev, ret, "can't write IOCON %d\n", addr); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (mcp->irq && mcp->irq_controller) { 6058c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq = &mcp->chip.irq; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci girq->chip = &mcp->irq_chip; 6088c2ecf20Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 6098c2ecf20Sopenharmony_ci girq->parent_handler = NULL; 6108c2ecf20Sopenharmony_ci girq->num_parents = 0; 6118c2ecf20Sopenharmony_ci girq->parents = NULL; 6128c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 6138c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 6148c2ecf20Sopenharmony_ci girq->threaded = true; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(dev, &mcp->chip, mcp); 6188c2ecf20Sopenharmony_ci if (ret < 0) 6198c2ecf20Sopenharmony_ci return dev_err_probe(dev, ret, "can't add GPIO chip\n"); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci mcp->pinctrl_desc.pctlops = &mcp_pinctrl_ops; 6228c2ecf20Sopenharmony_ci mcp->pinctrl_desc.confops = &mcp_pinconf_ops; 6238c2ecf20Sopenharmony_ci mcp->pinctrl_desc.npins = mcp->chip.ngpio; 6248c2ecf20Sopenharmony_ci if (mcp->pinctrl_desc.npins == 8) 6258c2ecf20Sopenharmony_ci mcp->pinctrl_desc.pins = mcp23x08_pins; 6268c2ecf20Sopenharmony_ci else if (mcp->pinctrl_desc.npins == 16) 6278c2ecf20Sopenharmony_ci mcp->pinctrl_desc.pins = mcp23x17_pins; 6288c2ecf20Sopenharmony_ci mcp->pinctrl_desc.owner = THIS_MODULE; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci mcp->pctldev = devm_pinctrl_register(dev, &mcp->pinctrl_desc, mcp); 6318c2ecf20Sopenharmony_ci if (IS_ERR(mcp->pctldev)) 6328c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mcp->pctldev), "can't register controller\n"); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (mcp->irq) { 6358c2ecf20Sopenharmony_ci ret = mcp23s08_irq_setup(mcp); 6368c2ecf20Sopenharmony_ci if (ret) 6378c2ecf20Sopenharmony_ci return dev_err_probe(dev, ret, "can't setup IRQ\n"); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mcp23s08_probe_one); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 645