Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 752ad5e8 authored by Peter Korsgaard's avatar Peter Korsgaard Committed by Grant Likely
Browse files

mcp23s08: add i2c support



Add i2c bindings for the mcp230xx devices. This is quite a lot simpler
than the spi one as there's no funky sub addressing done (one struct
i2c_client per struct gpio_chip).

The mcp23s08_platform_data structure is reused for i2c, even though
only a single mcp23s08_chip_info structure is needed.

To use, simply fill out a platform_data structure and pass it in
i2c_board_info, E.G.:

static const struct mcp23s08_platform_data mcp23017_data = {
	.chip[0] = {
		.pullups = 0x00ff,
	},
	.base = 240,
};

static struct i2c_board_info __initdata i2c_devs[] = {
	{ I2C_BOARD_INFO("mcp23017", 0x20),
	  .platform_data = &smartview_mcp23017_data, },
	...
};

Signed-off-by: default avatarPeter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent d62b98f3
Loading
Loading
Loading
Loading
+4 −3
Original line number Original line Diff line number Diff line
@@ -412,10 +412,11 @@ config GPIO_MAX7301
	  GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
	  GPIO driver for Maxim MAX7301 SPI-based GPIO expander.


config GPIO_MCP23S08
config GPIO_MCP23S08
	tristate "Microchip MCP23Sxx I/O expander"
	tristate "Microchip MCP23xxx I/O expander"
	depends on SPI_MASTER
	depends on SPI_MASTER || I2C
	help
	help
	  SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders.
	  SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
	  I/O expanders.
	  This provides a GPIO interface supporting inputs and outputs.
	  This provides a GPIO interface supporting inputs and outputs.


config GPIO_MC33880
config GPIO_MC33880
+187 −5
Original line number Original line Diff line number Diff line
/*
/*
 * MCP23S08 SPI gpio expander driver
 * MCP23S08 SPI/GPIO gpio expander driver
 */
 */


#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.h>
#include <linux/spi/mcp23s08.h>
#include <linux/slab.h>
#include <linux/slab.h>
@@ -16,13 +17,13 @@
 */
 */
#define MCP_TYPE_S08	0
#define MCP_TYPE_S08	0
#define MCP_TYPE_S17	1
#define MCP_TYPE_S17	1
#define MCP_TYPE_008	2
#define MCP_TYPE_017	3


/* Registers are all 8 bits wide.
/* Registers are all 8 bits wide.
 *
 *
 * The mcp23s17 has twice as many bits, and can be configured to work
 * 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.
 * with either 16 bit registers or with two adjacent 8 bit banks.
 *
 * Also, there are I2C versions of both chips.
 */
 */
#define MCP_IODIR	0x00		/* init/reset:  all ones */
#define MCP_IODIR	0x00		/* init/reset:  all ones */
#define MCP_IPOL	0x01
#define MCP_IPOL	0x01
@@ -73,6 +74,72 @@ struct mcp23s08_driver_data {
	struct mcp23s08		chip[];
	struct mcp23s08		chip[];
};
};


/*----------------------------------------------------------------------*/

#ifdef 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
#ifdef CONFIG_SPI_MASTER


static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
@@ -331,6 +398,20 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
		break;
		break;
#endif /* CONFIG_SPI_MASTER */
#endif /* CONFIG_SPI_MASTER */


#ifdef CONFIG_I2C
	case MCP_TYPE_008:
		mcp->ops = &mcp23008_ops;
		mcp->chip.ngpio = 8;
		mcp->chip.label = "mcp23008";
		break;

	case MCP_TYPE_017:
		mcp->ops = &mcp23017_ops;
		mcp->chip.ngpio = 16;
		mcp->chip.label = "mcp23017";
		break;
#endif /* CONFIG_I2C */

	default:
	default:
		dev_err(dev, "invalid device type (%d)\n", type);
		dev_err(dev, "invalid device type (%d)\n", type);
		return -EINVAL;
		return -EINVAL;
@@ -389,6 +470,91 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
	return status;
	return status;
}
}


/*----------------------------------------------------------------------*/

#ifdef CONFIG_I2C

static int __devinit mcp230xx_probe(struct i2c_client *client,
				    const struct i2c_device_id *id)
{
	struct mcp23s08_platform_data *pdata;
	struct mcp23s08 *mcp;
	int status;

	pdata = client->dev.platform_data;
	if (!pdata || !gpio_is_valid(pdata->base)) {
		dev_dbg(&client->dev, "invalid or missing platform data\n");
		return -EINVAL;
	}

	mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
	if (!mcp)
		return -ENOMEM;

	status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
				    id->driver_data, pdata->base,
				    pdata->chip[0].pullups);
	if (status)
		goto fail;

	i2c_set_clientdata(client, mcp);

	return 0;

fail:
	kfree(mcp);

	return status;
}

static int __devexit mcp230xx_remove(struct i2c_client *client)
{
	struct mcp23s08 *mcp = i2c_get_clientdata(client);
	int status;

	status = gpiochip_remove(&mcp->chip);
	if (status == 0)
		kfree(mcp);

	return status;
}

static const struct i2c_device_id mcp230xx_id[] = {
	{ "mcp23008", MCP_TYPE_008 },
	{ "mcp23017", MCP_TYPE_017 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, mcp230xx_id);

static struct i2c_driver mcp230xx_driver = {
	.driver = {
		.name	= "mcp230xx",
		.owner	= THIS_MODULE,
	},
	.probe		= mcp230xx_probe,
	.remove		= __devexit_p(mcp230xx_remove),
	.id_table	= mcp230xx_id,
};

static int __init mcp23s08_i2c_init(void)
{
	return i2c_add_driver(&mcp230xx_driver);
}

static void mcp23s08_i2c_exit(void)
{
	i2c_del_driver(&mcp230xx_driver);
}

#else

static int __init mcp23s08_i2c_init(void) { return 0; }
static void mcp23s08_i2c_exit(void) { }

#endif /* CONFIG_I2C */

/*----------------------------------------------------------------------*/

#ifdef CONFIG_SPI_MASTER
#ifdef CONFIG_SPI_MASTER


static int mcp23s08_probe(struct spi_device *spi)
static int mcp23s08_probe(struct spi_device *spi)
@@ -525,9 +691,24 @@ static void mcp23s08_spi_exit(void) { }


static int __init mcp23s08_init(void)
static int __init mcp23s08_init(void)
{
{
	return mcp23s08_spi_init();
	int ret;

	ret = mcp23s08_spi_init();
	if (ret)
		goto spi_fail;

	ret = mcp23s08_i2c_init();
	if (ret)
		goto i2c_fail;

	return 0;

 i2c_fail:
	mcp23s08_spi_exit();
 spi_fail:
	return ret;
}
}
/* register after spi postcore initcall and before
/* register after spi/i2c postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 * subsys initcalls that may rely on these GPIOs
 */
 */
subsys_initcall(mcp23s08_init);
subsys_initcall(mcp23s08_init);
@@ -535,6 +716,7 @@ subsys_initcall(mcp23s08_init);
static void __exit mcp23s08_exit(void)
static void __exit mcp23s08_exit(void)
{
{
	mcp23s08_spi_exit();
	mcp23s08_spi_exit();
	mcp23s08_i2c_exit();
}
}
module_exit(mcp23s08_exit);
module_exit(mcp23s08_exit);