419 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Serial multi-instantiate driver, pseudo driver to instantiate multiple4 * client devices from a single fwnode.5 *6 * Copyright 2018 Hans de Goede <hdegoede@redhat.com>7 */8 9#include <linux/acpi.h>10#include <linux/bits.h>11#include <linux/i2c.h>12#include <linux/interrupt.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/property.h>17#include <linux/spi/spi.h>18#include <linux/types.h>19 20#define IRQ_RESOURCE_TYPE GENMASK(1, 0)21#define IRQ_RESOURCE_NONE 022#define IRQ_RESOURCE_GPIO 123#define IRQ_RESOURCE_APIC 224#define IRQ_RESOURCE_AUTO 325 26enum smi_bus_type {27 SMI_I2C,28 SMI_SPI,29 SMI_AUTO_DETECT,30};31 32struct smi_instance {33 const char *type;34 unsigned int flags;35 int irq_idx;36};37 38struct smi_node {39 enum smi_bus_type bus_type;40 struct smi_instance instances[];41};42 43struct smi {44 int i2c_num;45 int spi_num;46 struct i2c_client **i2c_devs;47 struct spi_device **spi_devs;48};49 50static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,51 const struct smi_instance *inst)52{53 int ret;54 55 switch (inst->flags & IRQ_RESOURCE_TYPE) {56 case IRQ_RESOURCE_AUTO:57 ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx);58 if (ret > 0) {59 dev_dbg(&pdev->dev, "Using gpio irq\n");60 break;61 }62 ret = platform_get_irq(pdev, inst->irq_idx);63 if (ret > 0) {64 dev_dbg(&pdev->dev, "Using platform irq\n");65 break;66 }67 break;68 case IRQ_RESOURCE_GPIO:69 ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx);70 break;71 case IRQ_RESOURCE_APIC:72 ret = platform_get_irq(pdev, inst->irq_idx);73 break;74 default:75 return 0;76 }77 if (ret < 0)78 return dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d\n",79 inst->irq_idx);80 81 return ret;82}83 84static void smi_devs_unregister(struct smi *smi)85{86#if IS_REACHABLE(CONFIG_I2C)87 while (smi->i2c_num--)88 i2c_unregister_device(smi->i2c_devs[smi->i2c_num]);89#endif90 91 if (IS_REACHABLE(CONFIG_SPI)) {92 while (smi->spi_num--)93 spi_unregister_device(smi->spi_devs[smi->spi_num]);94 }95}96 97/**98 * smi_spi_probe - Instantiate multiple SPI devices from inst array99 * @pdev: Platform device100 * @smi: Internal struct for Serial multi instantiate driver101 * @inst_array: Array of instances to probe102 *103 * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code.104 */105static int smi_spi_probe(struct platform_device *pdev, struct smi *smi,106 const struct smi_instance *inst_array)107{108 struct device *dev = &pdev->dev;109 struct acpi_device *adev = ACPI_COMPANION(dev);110 struct spi_controller *ctlr;111 struct spi_device *spi_dev;112 char name[50];113 int i, ret, count;114 115 ret = acpi_spi_count_resources(adev);116 if (ret < 0)117 return ret;118 if (!ret)119 return -ENOENT;120 121 count = ret;122 123 smi->spi_devs = devm_kcalloc(dev, count, sizeof(*smi->spi_devs), GFP_KERNEL);124 if (!smi->spi_devs)125 return -ENOMEM;126 127 for (i = 0; i < count && inst_array[i].type; i++) {128 129 spi_dev = acpi_spi_device_alloc(NULL, adev, i);130 if (IS_ERR(spi_dev)) {131 ret = dev_err_probe(dev, PTR_ERR(spi_dev), "failed to allocate SPI device %s from ACPI\n",132 dev_name(&adev->dev));133 goto error;134 }135 136 ctlr = spi_dev->controller;137 138 strscpy(spi_dev->modalias, inst_array[i].type);139 140 ret = smi_get_irq(pdev, adev, &inst_array[i]);141 if (ret < 0) {142 spi_dev_put(spi_dev);143 goto error;144 }145 spi_dev->irq = ret;146 147 snprintf(name, sizeof(name), "%s-%s-%s.%d", dev_name(&ctlr->dev), dev_name(dev),148 inst_array[i].type, i);149 spi_dev->dev.init_name = name;150 151 ret = spi_add_device(spi_dev);152 if (ret) {153 dev_err_probe(&ctlr->dev, ret, "failed to add SPI device %s from ACPI\n",154 dev_name(&adev->dev));155 spi_dev_put(spi_dev);156 goto error;157 }158 159 dev_dbg(dev, "SPI device %s using chip select %u", name,160 spi_get_chipselect(spi_dev, 0));161 162 smi->spi_devs[i] = spi_dev;163 smi->spi_num++;164 }165 166 if (smi->spi_num < count) {167 dev_dbg(dev, "Error finding driver, idx %d\n", i);168 ret = -ENODEV;169 goto error;170 }171 172 dev_info(dev, "Instantiated %d SPI devices.\n", smi->spi_num);173 174 return 0;175error:176 smi_devs_unregister(smi);177 178 return ret;179}180 181/**182 * smi_i2c_probe - Instantiate multiple I2C devices from inst array183 * @pdev: Platform device184 * @smi: Internal struct for Serial multi instantiate driver185 * @inst_array: Array of instances to probe186 *187 * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code.188 */189static int smi_i2c_probe(struct platform_device *pdev, struct smi *smi,190 const struct smi_instance *inst_array)191{192 struct i2c_board_info board_info = {};193 struct device *dev = &pdev->dev;194 struct acpi_device *adev = ACPI_COMPANION(dev);195 char name[32];196 int i, ret, count;197 198 ret = i2c_acpi_client_count(adev);199 if (ret < 0)200 return ret;201 if (!ret)202 return -ENOENT;203 204 count = ret;205 206 smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL);207 if (!smi->i2c_devs)208 return -ENOMEM;209 210 for (i = 0; i < count && inst_array[i].type; i++) {211 memset(&board_info, 0, sizeof(board_info));212 strscpy(board_info.type, inst_array[i].type);213 snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i);214 board_info.dev_name = name;215 216 ret = smi_get_irq(pdev, adev, &inst_array[i]);217 if (ret < 0)218 goto error;219 board_info.irq = ret;220 221 smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info);222 if (IS_ERR(smi->i2c_devs[i])) {223 ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]),224 "Error creating i2c-client, idx %d\n", i);225 goto error;226 }227 smi->i2c_num++;228 }229 if (smi->i2c_num < count) {230 dev_dbg(dev, "Error finding driver, idx %d\n", i);231 ret = -ENODEV;232 goto error;233 }234 235 dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num);236 237 return 0;238error:239 smi_devs_unregister(smi);240 241 return ret;242}243 244static int smi_probe(struct platform_device *pdev)245{246 struct device *dev = &pdev->dev;247 const struct smi_node *node;248 struct smi *smi;249 int ret;250 251 node = device_get_match_data(dev);252 if (!node) {253 dev_dbg(dev, "Error ACPI match data is missing\n");254 return -ENODEV;255 }256 257 smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL);258 if (!smi)259 return -ENOMEM;260 261 platform_set_drvdata(pdev, smi);262 263 switch (node->bus_type) {264 case SMI_I2C:265 if (IS_REACHABLE(CONFIG_I2C))266 return smi_i2c_probe(pdev, smi, node->instances);267 268 return -ENODEV;269 case SMI_SPI:270 if (IS_REACHABLE(CONFIG_SPI))271 return smi_spi_probe(pdev, smi, node->instances);272 273 return -ENODEV;274 case SMI_AUTO_DETECT:275 /*276 * For backwards-compatibility with the existing nodes I2C277 * is checked first and if such entries are found ONLY I2C278 * devices are created. Since some existing nodes that were279 * already handled by this driver could also contain unrelated280 * SpiSerialBus nodes that were previously ignored, and this281 * preserves that behavior.282 */283 if (IS_REACHABLE(CONFIG_I2C)) {284 ret = smi_i2c_probe(pdev, smi, node->instances);285 if (ret != -ENOENT)286 return ret;287 }288 289 if (IS_REACHABLE(CONFIG_SPI))290 return smi_spi_probe(pdev, smi, node->instances);291 292 return -ENODEV;293 default:294 return -EINVAL;295 }296}297 298static void smi_remove(struct platform_device *pdev)299{300 struct smi *smi = platform_get_drvdata(pdev);301 302 smi_devs_unregister(smi);303}304 305static const struct smi_node bsg1160_data = {306 .instances = {307 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },308 { "bmc150_magn" },309 { "bmg160" },310 {}311 },312 .bus_type = SMI_I2C,313};314 315static const struct smi_node bsg2150_data = {316 .instances = {317 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },318 { "bmc150_magn" },319 /* The resources describe a 3th client, but it is not really there. */320 { "bsg2150_dummy_dev" },321 {}322 },323 .bus_type = SMI_I2C,324};325 326static const struct smi_node int3515_data = {327 .instances = {328 { "tps6598x", IRQ_RESOURCE_APIC, 0 },329 { "tps6598x", IRQ_RESOURCE_APIC, 1 },330 { "tps6598x", IRQ_RESOURCE_APIC, 2 },331 { "tps6598x", IRQ_RESOURCE_APIC, 3 },332 {}333 },334 .bus_type = SMI_I2C,335};336 337static const struct smi_node cs35l41_hda = {338 .instances = {339 { "cs35l41-hda", IRQ_RESOURCE_AUTO, 0 },340 { "cs35l41-hda", IRQ_RESOURCE_AUTO, 0 },341 { "cs35l41-hda", IRQ_RESOURCE_AUTO, 0 },342 { "cs35l41-hda", IRQ_RESOURCE_AUTO, 0 },343 {}344 },345 .bus_type = SMI_AUTO_DETECT,346};347 348static const struct smi_node cs35l54_hda = {349 .instances = {350 { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },351 { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },352 { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },353 { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },354 /* a 5th entry is an alias address, not a real device */355 { "cs35l54-hda_dummy_dev" },356 {}357 },358 .bus_type = SMI_AUTO_DETECT,359};360 361static const struct smi_node cs35l56_hda = {362 .instances = {363 { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },364 { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },365 { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },366 { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },367 /* a 5th entry is an alias address, not a real device */368 { "cs35l56-hda_dummy_dev" },369 {}370 },371 .bus_type = SMI_AUTO_DETECT,372};373 374static const struct smi_node cs35l57_hda = {375 .instances = {376 { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },377 { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },378 { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },379 { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },380 /* a 5th entry is an alias address, not a real device */381 { "cs35l57-hda_dummy_dev" },382 {}383 },384 .bus_type = SMI_AUTO_DETECT,385};386 387/*388 * Note new device-ids must also be added to ignore_serial_bus_ids in389 * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().390 */391static const struct acpi_device_id smi_acpi_ids[] = {392 { "BSG1160", (unsigned long)&bsg1160_data },393 { "BSG2150", (unsigned long)&bsg2150_data },394 { "CSC3551", (unsigned long)&cs35l41_hda },395 { "CSC3554", (unsigned long)&cs35l54_hda },396 { "CSC3556", (unsigned long)&cs35l56_hda },397 { "CSC3557", (unsigned long)&cs35l57_hda },398 { "INT3515", (unsigned long)&int3515_data },399 /* Non-conforming _HID for Cirrus Logic already released */400 { "CLSA0100", (unsigned long)&cs35l41_hda },401 { "CLSA0101", (unsigned long)&cs35l41_hda },402 { }403};404MODULE_DEVICE_TABLE(acpi, smi_acpi_ids);405 406static struct platform_driver smi_driver = {407 .driver = {408 .name = "Serial bus multi instantiate pseudo device driver",409 .acpi_match_table = smi_acpi_ids,410 },411 .probe = smi_probe,412 .remove_new = smi_remove,413};414module_platform_driver(smi_driver);415 416MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver");417MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");418MODULE_LICENSE("GPL");419