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