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