1
2
3
4
5
6
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
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
138 { "bsg2150_dummy_dev" },
139 {}
140};
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
171 { "BSG1160", (unsigned long)bsg1160_data },
172 { "BSG2150", (unsigned long)bsg2150_data },
173 { }
174};
175MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids);
176
177static struct platform_driver i2c_multi_inst_driver = {
178 .driver = {
179 .name = "I2C multi instantiate pseudo device driver",
180 .acpi_match_table = i2c_multi_inst_acpi_ids,
181 },
182 .probe = i2c_multi_inst_probe,
183 .remove = i2c_multi_inst_remove,
184};
185module_platform_driver(i2c_multi_inst_driver);
186
187MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
188MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
189MODULE_LICENSE("GPL");
190