/*
* MCP23S08 SPI/I2C GPIO gpio expander driver
*
* The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are
* supported.
* For the I2C versions of the chips (mcp23008 and mcp23017) generation of
* interrupts is also supported.
* The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is
* also capable of generating interrupts, but the linux driver does not
* support that yet.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/gpio.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_irq.h>
#include <linux/of_device.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
/* 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 MCP_GPPU 0x06
#define MCP_INTF 0x07
#define MCP_INTCAP 0x08
#define MCP_GPIO 0x09
#define MCP_OLAT 0x0a
struct mcp23s08;
struct mcp23s08_ops {
int (*read)(struct mcp23s08 *mcp, unsigned reg);
int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val);
int (*read_regs)(struct mcp23s08 *mcp, unsigned reg,
u16 *vals, unsigned n);
};
struct mcp23s08 {
u8 addr;
u16 cache[11];
u16 irq_rise;
u16 irq_fall;
int irq;
bool irq_controller;
/* lock protects the cached values */
struct mutex lock;
struct mutex irq_lock;
struct irq_domain *irq_domain;
struct gpio_chip chip;
const struct mcp23s08_ops *ops;
void *data; /* ops specific data */
};
/* A given spi_device can represent up to eight mcp23sxx chips
* sharing the same chipselect but using different addresses
* (e.g. chips #0 and #3 might be populated, but not #1 or $2).
* Driver data holds all the per-chip data.
*/
struct mcp23s08_driver_data {
unsigned ngpio;
struct mcp23s08 *mcp[8];
struct mcp23s08 chip[];
};
/* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
static struct lock_class_key gpio_lock_class;
/*----------------------------------------------------------------------*/
#if IS_ENABLED(CONFIG_I2C)
static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg)
{
return i2c_smbus_read_byte_data(mcp->data, reg);
}
static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
return i2c_smbus_write_byte_data(mcp->data, reg, val);
}
static int
mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
while (n--) {
int ret = mcp23008_read(mcp, reg++);
if (ret < 0)
return ret;
*vals++ = ret;
}
return 0;
}
static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg)
{
return i2c_smbus_read_word_data(mcp->data, reg << 1);
}
static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
return i2c_smbus_write_word_data(mcp->data, reg << 1, val);
}
static int
mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
while (n--) {
int ret = mcp23017_read(mcp, reg++);
if (ret < 0)
return ret;
*vals++ = ret;
}
return 0;
}
static const struct mcp23s08_ops mcp23008_ops = {
.read = mcp23008_read,
.write = mcp23008_write,
.read_regs = mcp23008_read_regs,
};
static const struct mcp23s08_ops mcp23017_ops = {
.read = mcp23017_read,
.write = mcp23017_write,
.read_regs = mcp23017_read_regs,
};
#endif /* CONFIG_I2C */
/*----------------------------------------------------------------------*/
#ifdef CONFIG_SPI_MASTER
static int mcp23s08_read(struct mcp23s08 *