162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* MCP23S08 I2C GPIO driver */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/i2c.h>
562306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/regmap.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "pinctrl-mcp23s08.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic int mcp230xx_probe(struct i2c_client *client)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
1462306a36Sopenharmony_ci	struct device *dev = &client->dev;
1562306a36Sopenharmony_ci	unsigned int type = id->driver_data;
1662306a36Sopenharmony_ci	struct mcp23s08 *mcp;
1762306a36Sopenharmony_ci	int ret;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	mcp = devm_kzalloc(dev, sizeof(*mcp), GFP_KERNEL);
2062306a36Sopenharmony_ci	if (!mcp)
2162306a36Sopenharmony_ci		return -ENOMEM;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	switch (type) {
2462306a36Sopenharmony_ci	case MCP_TYPE_008:
2562306a36Sopenharmony_ci		mcp->regmap = devm_regmap_init_i2c(client, &mcp23x08_regmap);
2662306a36Sopenharmony_ci		mcp->reg_shift = 0;
2762306a36Sopenharmony_ci		mcp->chip.ngpio = 8;
2862306a36Sopenharmony_ci		mcp->chip.label = "mcp23008";
2962306a36Sopenharmony_ci		break;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	case MCP_TYPE_017:
3262306a36Sopenharmony_ci		mcp->regmap = devm_regmap_init_i2c(client, &mcp23x17_regmap);
3362306a36Sopenharmony_ci		mcp->reg_shift = 1;
3462306a36Sopenharmony_ci		mcp->chip.ngpio = 16;
3562306a36Sopenharmony_ci		mcp->chip.label = "mcp23017";
3662306a36Sopenharmony_ci		break;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	case MCP_TYPE_018:
3962306a36Sopenharmony_ci		mcp->regmap = devm_regmap_init_i2c(client, &mcp23x17_regmap);
4062306a36Sopenharmony_ci		mcp->reg_shift = 1;
4162306a36Sopenharmony_ci		mcp->chip.ngpio = 16;
4262306a36Sopenharmony_ci		mcp->chip.label = "mcp23018";
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	default:
4662306a36Sopenharmony_ci		dev_err(dev, "invalid device type (%d)\n", type);
4762306a36Sopenharmony_ci		return -EINVAL;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (IS_ERR(mcp->regmap))
5162306a36Sopenharmony_ci		return PTR_ERR(mcp->regmap);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	mcp->irq = client->irq;
5462306a36Sopenharmony_ci	mcp->pinctrl_desc.name = "mcp23xxx-pinctrl";
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ret = mcp23s08_probe_one(mcp, dev, client->addr, type, -1);
5762306a36Sopenharmony_ci	if (ret)
5862306a36Sopenharmony_ci		return ret;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	i2c_set_clientdata(client, mcp);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const struct i2c_device_id mcp230xx_id[] = {
6662306a36Sopenharmony_ci	{ "mcp23008", MCP_TYPE_008 },
6762306a36Sopenharmony_ci	{ "mcp23017", MCP_TYPE_017 },
6862306a36Sopenharmony_ci	{ "mcp23018", MCP_TYPE_018 },
6962306a36Sopenharmony_ci	{ }
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcp230xx_id);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic const struct of_device_id mcp23s08_i2c_of_match[] = {
7462306a36Sopenharmony_ci	{
7562306a36Sopenharmony_ci		.compatible = "microchip,mcp23008",
7662306a36Sopenharmony_ci		.data = (void *) MCP_TYPE_008,
7762306a36Sopenharmony_ci	},
7862306a36Sopenharmony_ci	{
7962306a36Sopenharmony_ci		.compatible = "microchip,mcp23017",
8062306a36Sopenharmony_ci		.data = (void *) MCP_TYPE_017,
8162306a36Sopenharmony_ci	},
8262306a36Sopenharmony_ci	{
8362306a36Sopenharmony_ci		.compatible = "microchip,mcp23018",
8462306a36Sopenharmony_ci		.data = (void *) MCP_TYPE_018,
8562306a36Sopenharmony_ci	},
8662306a36Sopenharmony_ci/* NOTE: The use of the mcp prefix is deprecated and will be removed. */
8762306a36Sopenharmony_ci	{
8862306a36Sopenharmony_ci		.compatible = "mcp,mcp23008",
8962306a36Sopenharmony_ci		.data = (void *) MCP_TYPE_008,
9062306a36Sopenharmony_ci	},
9162306a36Sopenharmony_ci	{
9262306a36Sopenharmony_ci		.compatible = "mcp,mcp23017",
9362306a36Sopenharmony_ci		.data = (void *) MCP_TYPE_017,
9462306a36Sopenharmony_ci	},
9562306a36Sopenharmony_ci	{ }
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic struct i2c_driver mcp230xx_driver = {
10062306a36Sopenharmony_ci	.driver = {
10162306a36Sopenharmony_ci		.name	= "mcp230xx",
10262306a36Sopenharmony_ci		.of_match_table = mcp23s08_i2c_of_match,
10362306a36Sopenharmony_ci	},
10462306a36Sopenharmony_ci	.probe		= mcp230xx_probe,
10562306a36Sopenharmony_ci	.id_table	= mcp230xx_id,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int __init mcp23s08_i2c_init(void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	return i2c_add_driver(&mcp230xx_driver);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/*
11462306a36Sopenharmony_ci * Register after I²C postcore initcall and before
11562306a36Sopenharmony_ci * subsys initcalls that may rely on these GPIOs.
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_cisubsys_initcall(mcp23s08_i2c_init);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void mcp23s08_i2c_exit(void)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	i2c_del_driver(&mcp230xx_driver);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_cimodule_exit(mcp23s08_i2c_exit);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
126