Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.17-rc7 174 lines 4.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * I2C multi-instantiate driver, pseudo driver to instantiate multiple 4 * i2c-clients 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/types.h> 18 19#define IRQ_RESOURCE_TYPE GENMASK(1, 0) 20#define IRQ_RESOURCE_NONE 0 21#define IRQ_RESOURCE_GPIO 1 22#define IRQ_RESOURCE_APIC 2 23 24struct i2c_inst_data { 25 const char *type; 26 unsigned int flags; 27 int irq_idx; 28}; 29 30struct i2c_multi_inst_data { 31 int num_clients; 32 struct i2c_client *clients[]; 33}; 34 35static int i2c_multi_inst_probe(struct platform_device *pdev) 36{ 37 struct i2c_multi_inst_data *multi; 38 const struct i2c_inst_data *inst_data; 39 struct i2c_board_info board_info = {}; 40 struct device *dev = &pdev->dev; 41 struct acpi_device *adev; 42 char name[32]; 43 int i, ret; 44 45 inst_data = device_get_match_data(dev); 46 if (!inst_data) { 47 dev_err(dev, "Error ACPI match data is missing\n"); 48 return -ENODEV; 49 } 50 51 adev = ACPI_COMPANION(dev); 52 53 /* Count number of clients to instantiate */ 54 ret = i2c_acpi_client_count(adev); 55 if (ret < 0) 56 return ret; 57 58 multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL); 59 if (!multi) 60 return -ENOMEM; 61 62 multi->num_clients = ret; 63 64 for (i = 0; i < multi->num_clients && inst_data[i].type; i++) { 65 memset(&board_info, 0, sizeof(board_info)); 66 strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE); 67 snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), 68 inst_data[i].type, i); 69 board_info.dev_name = name; 70 switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) { 71 case IRQ_RESOURCE_GPIO: 72 ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx); 73 if (ret < 0) { 74 dev_err(dev, "Error requesting irq at index %d: %d\n", 75 inst_data[i].irq_idx, ret); 76 goto error; 77 } 78 board_info.irq = ret; 79 break; 80 case IRQ_RESOURCE_APIC: 81 ret = platform_get_irq(pdev, inst_data[i].irq_idx); 82 if (ret < 0) { 83 dev_dbg(dev, "Error requesting irq at index %d: %d\n", 84 inst_data[i].irq_idx, ret); 85 goto error; 86 } 87 board_info.irq = ret; 88 break; 89 default: 90 board_info.irq = 0; 91 break; 92 } 93 multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info); 94 if (IS_ERR(multi->clients[i])) { 95 ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]), 96 "Error creating i2c-client, idx %d\n", i); 97 goto error; 98 } 99 } 100 if (i < multi->num_clients) { 101 dev_err(dev, "Error finding driver, idx %d\n", i); 102 ret = -ENODEV; 103 goto error; 104 } 105 106 platform_set_drvdata(pdev, multi); 107 return 0; 108 109error: 110 while (--i >= 0) 111 i2c_unregister_device(multi->clients[i]); 112 113 return ret; 114} 115 116static int i2c_multi_inst_remove(struct platform_device *pdev) 117{ 118 struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev); 119 int i; 120 121 for (i = 0; i < multi->num_clients; i++) 122 i2c_unregister_device(multi->clients[i]); 123 124 return 0; 125} 126 127static const struct i2c_inst_data bsg1160_data[] = { 128 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, 129 { "bmc150_magn" }, 130 { "bmg160" }, 131 {} 132}; 133 134static const struct i2c_inst_data bsg2150_data[] = { 135 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, 136 { "bmc150_magn" }, 137 /* The resources describe a 3th client, but it is not really there. */ 138 { "bsg2150_dummy_dev" }, 139 {} 140}; 141 142static const struct i2c_inst_data int3515_data[] = { 143 { "tps6598x", IRQ_RESOURCE_APIC, 0 }, 144 { "tps6598x", IRQ_RESOURCE_APIC, 1 }, 145 { "tps6598x", IRQ_RESOURCE_APIC, 2 }, 146 { "tps6598x", IRQ_RESOURCE_APIC, 3 }, 147 {} 148}; 149 150/* 151 * Note new device-ids must also be added to i2c_multi_instantiate_ids in 152 * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). 153 */ 154static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { 155 { "BSG1160", (unsigned long)bsg1160_data }, 156 { "BSG2150", (unsigned long)bsg2150_data }, 157 { "INT3515", (unsigned long)int3515_data }, 158 { } 159}; 160MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); 161 162static struct platform_driver i2c_multi_inst_driver = { 163 .driver = { 164 .name = "I2C multi instantiate pseudo device driver", 165 .acpi_match_table = i2c_multi_inst_acpi_ids, 166 }, 167 .probe = i2c_multi_inst_probe, 168 .remove = i2c_multi_inst_remove, 169}; 170module_platform_driver(i2c_multi_inst_driver); 171 172MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver"); 173MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 174MODULE_LICENSE("GPL");