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