// SPDX-License-Identifier: GPL-2.0-only
/* MCP23S08 SPI/I2C GPIO driver */
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
/*
* MCP types supported by driver
*/
#define MCP_TYPE_S08 0
#define MCP_TYPE_S17 1
#define MCP_TYPE_008 2
#define MCP_TYPE_017 3
#define MCP_TYPE_S18 4
#define MCP_TYPE_018 5
#define MCP_MAX_DEV_PER_CS 8
/* Registers are all 8 bits wide.
*
* The mcp23s17 has twice as many bits, and can be configured to work
* with either 16 bit registers or with two adjacent 8 bit banks.
*/
#define MCP_IODIR 0x00 /* init/reset: all ones */
#define MCP_IPOL 0x01
#define MCP_GPINTEN 0x02
#define MCP_DEFVAL 0x03
#define MCP_INTCON 0x04
#define MCP_IOCON 0x05
# define IOCON_MIRROR (1 << 6)
# define IOCON_SEQOP (1 << 5)
# define IOCON_HAEN (1 << 3)
# define IOCON_ODR (1 << 2)
# define IOCON_INTPOL (1 << 1)
# define IOCON_INTCC (1)
#define MCP_GPPU 0x06
#define MCP_INTF 0x07
#define MCP_INTCAP 0x08
#define MCP_GPIO 0x09
#define MCP_OLAT 0x0a
struct mcp23s08;
struct mcp23s08 {
u8 addr;
bool irq_active_high;
bool reg_shift;
u16 irq_rise;
u16 irq_fall;
int irq;
bool irq_controller;
int cached_gpio;
/* lock protects regmap access with bypass/cache flags */
struct mutex lock;
struct gpio_chip chip;
struct irq_chip irq_chip;
struct regmap *regmap;
struct device *dev;
struct pinctrl_dev *pctldev;
struct pinctrl_desc pinctrl_desc;
};
static const struct reg_default mcp23x08_defaults[] = {
{.reg = MCP_IODIR, .def = 0xff},
{.reg = MCP_IPOL, .def = 0x00},
{.reg = MCP_GPINTEN, .def = 0x00},
{.reg = MCP_DEFVAL, .def = 0x00},
{.reg = MCP_INTCON, .def = 0x00},
{.reg = MCP_IOCON, .def = 0x00},
{.reg = MCP_GPPU, .def = 0x00},
{.reg = MCP_OLAT, .def = 0x00},
};
static const struct regmap_range mcp23x08_volatile_range = {
.range_min = MCP_INTF,
.range_max = MCP_GPIO,
};
static const struct regmap_access_table mcp23x08_volatile_table = {
.yes_ranges = &mcp23x08_volatile_range,
.n_yes_ranges = 1,
};
static const struct regmap_range mcp23x08_precious_range = {
.range_min = MCP_GPIO,
.range_max = MCP_GPIO,
};
static const struct regmap_access_table mcp23x08_precious_table = {
.yes_ranges = &mcp23x08_precious_range,
.n_yes_ranges = 1,
};
static const struct regmap_config mcp23x08_regmap = {
.reg_bits = 8,
.val_bits = 8,
.reg_stride = 1,
.volatile_table = &mcp23x08_volatile_table,
.precious_table = &mcp23x08_precious_table,
.reg_defaults = mcp23x08_defaults,
.num_reg_defaults = ARRAY_SIZE(mcp23x08_defaults),
.cache_type = REGCACHE_FLAT,
.max_register = MCP_OLAT,
};
static const struct reg_default mcp23x16_defaults[] = {
{.reg = MCP_IODIR << 1, .def = 0xffff},
{.reg = MCP_IPOL << 1, .def = 0x0000},
{.reg = MCP_GPINTEN << 1, .def = 0x0000},
{.reg = MCP_DEFVAL << 1, .def = 0x0000},
{.reg = MCP_INTCON << 1, .def = 0x0000},
{.reg = MCP_IOCON << 1, .def = 0x0000},
{.reg = MCP_GPPU << 1, .def = 0x0000},
{.reg = MCP_OLAT << 1, .def = 0x0000},
};
static const struct regmap_range mcp23x16_volatile_range = {
.range_min = MCP_INTF << 1,
.range_max = MCP_GPIO << 1,
};
static const struct regmap_access_table mcp23x16_volatile_table = {
.yes_ranges = &mcp23x16_volatile_range,
.n_yes_ranges = 1,
};
static const struct regmap_range mcp23x16_precious_range = {
.range_min = MCP_GPIO << 1,
.range_max = MCP_GPIO << 1,
};
static const struct regmap_access_table mcp23x16_precious_table = {
.yes_ranges = &mcp23x16_precious_range,
.n_yes_ranges = 1,
};
static const struct regmap_config mcp23x17_regmap = {
.reg_bits = 8,
.val_bits =