Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* MCP23S08 SPI GPIO driver */
0003 
0004 #include <linux/mod_devicetable.h>
0005 #include <linux/module.h>
0006 #include <linux/property.h>
0007 #include <linux/regmap.h>
0008 #include <linux/spi/spi.h>
0009 
0010 #include "pinctrl-mcp23s08.h"
0011 
0012 #define MCP_MAX_DEV_PER_CS  8
0013 
0014 /*
0015  * A given spi_device can represent up to eight mcp23sxx chips
0016  * sharing the same chipselect but using different addresses
0017  * (e.g. chips #0 and #3 might be populated, but not #1 or #2).
0018  * Driver data holds all the per-chip data.
0019  */
0020 struct mcp23s08_driver_data {
0021     unsigned        ngpio;
0022     struct mcp23s08     *mcp[8];
0023     struct mcp23s08     chip[];
0024 };
0025 
0026 static int mcp23sxx_spi_write(void *context, const void *data, size_t count)
0027 {
0028     struct mcp23s08 *mcp = context;
0029     struct spi_device *spi = to_spi_device(mcp->dev);
0030     struct spi_message m;
0031     struct spi_transfer t[2] = { { .tx_buf = &mcp->addr, .len = 1, },
0032                      { .tx_buf = data, .len = count, }, };
0033 
0034     spi_message_init(&m);
0035     spi_message_add_tail(&t[0], &m);
0036     spi_message_add_tail(&t[1], &m);
0037 
0038     return spi_sync(spi, &m);
0039 }
0040 
0041 static int mcp23sxx_spi_gather_write(void *context,
0042                 const void *reg, size_t reg_size,
0043                 const void *val, size_t val_size)
0044 {
0045     struct mcp23s08 *mcp = context;
0046     struct spi_device *spi = to_spi_device(mcp->dev);
0047     struct spi_message m;
0048     struct spi_transfer t[3] = { { .tx_buf = &mcp->addr, .len = 1, },
0049                      { .tx_buf = reg, .len = reg_size, },
0050                      { .tx_buf = val, .len = val_size, }, };
0051 
0052     spi_message_init(&m);
0053     spi_message_add_tail(&t[0], &m);
0054     spi_message_add_tail(&t[1], &m);
0055     spi_message_add_tail(&t[2], &m);
0056 
0057     return spi_sync(spi, &m);
0058 }
0059 
0060 static int mcp23sxx_spi_read(void *context, const void *reg, size_t reg_size,
0061                 void *val, size_t val_size)
0062 {
0063     struct mcp23s08 *mcp = context;
0064     struct spi_device *spi = to_spi_device(mcp->dev);
0065     u8 tx[2];
0066 
0067     if (reg_size != 1)
0068         return -EINVAL;
0069 
0070     tx[0] = mcp->addr | 0x01;
0071     tx[1] = *((u8 *) reg);
0072 
0073     return spi_write_then_read(spi, tx, sizeof(tx), val, val_size);
0074 }
0075 
0076 static const struct regmap_bus mcp23sxx_spi_regmap = {
0077     .write = mcp23sxx_spi_write,
0078     .gather_write = mcp23sxx_spi_gather_write,
0079     .read = mcp23sxx_spi_read,
0080 };
0081 
0082 static int mcp23s08_spi_regmap_init(struct mcp23s08 *mcp, struct device *dev,
0083                     unsigned int addr, unsigned int type)
0084 {
0085     const struct regmap_config *config;
0086     struct regmap_config *copy;
0087     const char *name;
0088 
0089     switch (type) {
0090     case MCP_TYPE_S08:
0091         mcp->reg_shift = 0;
0092         mcp->chip.ngpio = 8;
0093         mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL, "mcp23s08.%d", addr);
0094 
0095         config = &mcp23x08_regmap;
0096         name = devm_kasprintf(dev, GFP_KERNEL, "%d", addr);
0097         break;
0098 
0099     case MCP_TYPE_S17:
0100         mcp->reg_shift = 1;
0101         mcp->chip.ngpio = 16;
0102         mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL, "mcp23s17.%d", addr);
0103 
0104         config = &mcp23x17_regmap;
0105         name = devm_kasprintf(dev, GFP_KERNEL, "%d", addr);
0106         break;
0107 
0108     case MCP_TYPE_S18:
0109         mcp->reg_shift = 1;
0110         mcp->chip.ngpio = 16;
0111         mcp->chip.label = "mcp23s18";
0112 
0113         config = &mcp23x17_regmap;
0114         name = config->name;
0115         break;
0116 
0117     default:
0118         dev_err(dev, "invalid device type (%d)\n", type);
0119         return -EINVAL;
0120     }
0121 
0122     copy = devm_kmemdup(dev, config, sizeof(*config), GFP_KERNEL);
0123     if (!copy)
0124         return -ENOMEM;
0125 
0126     copy->name = name;
0127 
0128     mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, copy);
0129     if (IS_ERR(mcp->regmap))
0130         dev_err(dev, "regmap init failed for %s\n", mcp->chip.label);
0131     return PTR_ERR_OR_ZERO(mcp->regmap);
0132 }
0133 
0134 static int mcp23s08_probe(struct spi_device *spi)
0135 {
0136     struct device *dev = &spi->dev;
0137     struct mcp23s08_driver_data *data;
0138     unsigned long spi_present_mask;
0139     const void *match;
0140     unsigned int addr;
0141     unsigned int ngpio = 0;
0142     int chips;
0143     int type;
0144     int ret;
0145     u32 v;
0146 
0147     match = device_get_match_data(dev);
0148     if (match)
0149         type = (int)(uintptr_t)match;
0150     else
0151         type = spi_get_device_id(spi)->driver_data;
0152 
0153     ret = device_property_read_u32(dev, "microchip,spi-present-mask", &v);
0154     if (ret) {
0155         ret = device_property_read_u32(dev, "mcp,spi-present-mask", &v);
0156         if (ret) {
0157             dev_err(dev, "missing spi-present-mask");
0158             return ret;
0159         }
0160     }
0161     spi_present_mask = v;
0162 
0163     if (!spi_present_mask || spi_present_mask >= BIT(MCP_MAX_DEV_PER_CS)) {
0164         dev_err(dev, "invalid spi-present-mask");
0165         return -ENODEV;
0166     }
0167 
0168     chips = hweight_long(spi_present_mask);
0169 
0170     data = devm_kzalloc(dev, struct_size(data, chip, chips), GFP_KERNEL);
0171     if (!data)
0172         return -ENOMEM;
0173 
0174     spi_set_drvdata(spi, data);
0175 
0176     for_each_set_bit(addr, &spi_present_mask, MCP_MAX_DEV_PER_CS) {
0177         data->mcp[addr] = &data->chip[--chips];
0178         data->mcp[addr]->irq = spi->irq;
0179 
0180         ret = mcp23s08_spi_regmap_init(data->mcp[addr], dev, addr, type);
0181         if (ret)
0182             return ret;
0183 
0184         data->mcp[addr]->pinctrl_desc.name = devm_kasprintf(dev, GFP_KERNEL,
0185                                     "mcp23xxx-pinctrl.%d",
0186                                     addr);
0187         if (!data->mcp[addr]->pinctrl_desc.name)
0188             return -ENOMEM;
0189 
0190         ret = mcp23s08_probe_one(data->mcp[addr], dev, 0x40 | (addr << 1), type, -1);
0191         if (ret < 0)
0192             return ret;
0193 
0194         ngpio += data->mcp[addr]->chip.ngpio;
0195     }
0196     data->ngpio = ngpio;
0197 
0198     return 0;
0199 }
0200 
0201 static const struct spi_device_id mcp23s08_ids[] = {
0202     { "mcp23s08", MCP_TYPE_S08 },
0203     { "mcp23s17", MCP_TYPE_S17 },
0204     { "mcp23s18", MCP_TYPE_S18 },
0205     { }
0206 };
0207 MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
0208 
0209 static const struct of_device_id mcp23s08_spi_of_match[] = {
0210     {
0211         .compatible = "microchip,mcp23s08",
0212         .data = (void *) MCP_TYPE_S08,
0213     },
0214     {
0215         .compatible = "microchip,mcp23s17",
0216         .data = (void *) MCP_TYPE_S17,
0217     },
0218     {
0219         .compatible = "microchip,mcp23s18",
0220         .data = (void *) MCP_TYPE_S18,
0221     },
0222 /* NOTE: The use of the mcp prefix is deprecated and will be removed. */
0223     {
0224         .compatible = "mcp,mcp23s08",
0225         .data = (void *) MCP_TYPE_S08,
0226     },
0227     {
0228         .compatible = "mcp,mcp23s17",
0229         .data = (void *) MCP_TYPE_S17,
0230     },
0231     { }
0232 };
0233 MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
0234 
0235 static struct spi_driver mcp23s08_driver = {
0236     .probe      = mcp23s08_probe,
0237     .id_table   = mcp23s08_ids,
0238     .driver = {
0239         .name   = "mcp23s08",
0240         .of_match_table = mcp23s08_spi_of_match,
0241     },
0242 };
0243 
0244 static int __init mcp23s08_spi_init(void)
0245 {
0246     return spi_register_driver(&mcp23s08_driver);
0247 }
0248 
0249 /*
0250  * Register after SPI postcore initcall and before
0251  * subsys initcalls that may rely on these GPIOs.
0252  */
0253 subsys_initcall(mcp23s08_spi_init);
0254 
0255 static void mcp23s08_spi_exit(void)
0256 {
0257     spi_unregister_driver(&mcp23s08_driver);
0258 }
0259 module_exit(mcp23s08_spi_exit);
0260 
0261 MODULE_LICENSE("GPL");