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_count(struct acpi_resource *ares, void *data)
36{
37 struct acpi_resource_i2c_serialbus *sb;
38 int *count = data;
39
40 if (i2c_acpi_get_i2c_resource(ares, &sb))
41 *count = *count + 1;
42
43 return 1;
44}
45
46static int i2c_multi_inst_count_resources(struct acpi_device *adev)
47{
48 LIST_HEAD(r);
49 int count = 0;
50 int ret;
51
52 ret = acpi_dev_get_resources(adev, &r, i2c_multi_inst_count, &count);
53 if (ret < 0)
54 return ret;
55
56 acpi_dev_free_resource_list(&r);
57 return count;
58}
59
60static int i2c_multi_inst_probe(struct platform_device *pdev)
61{
62 struct i2c_multi_inst_data *multi;
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 inst_data = device_get_match_data(dev);
71 if (!inst_data) {
72 dev_err(dev, "Error ACPI match data is missing\n");
73 return -ENODEV;
74 }
75
76 adev = ACPI_COMPANION(dev);
77
78
79 ret = i2c_multi_inst_count_resources(adev);
80 if (ret < 0)
81 return ret;
82
83 multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL);
84 if (!multi)
85 return -ENOMEM;
86
87 multi->num_clients = ret;
88
89 for (i = 0; i < multi->num_clients && inst_data[i].type; i++) {
90 memset(&board_info, 0, sizeof(board_info));
91 strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE);
92 snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev),
93 inst_data[i].type, i);
94 board_info.dev_name = name;
95 switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) {
96 case IRQ_RESOURCE_GPIO:
97 ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx);
98 if (ret < 0) {
99 dev_err(dev, "Error requesting irq at index %d: %d\n",
100 inst_data[i].irq_idx, ret);
101 goto error;
102 }
103 board_info.irq = ret;
104 break;
105 case IRQ_RESOURCE_APIC:
106 ret = platform_get_irq(pdev, inst_data[i].irq_idx);
107 if (ret < 0) {
108 dev_dbg(dev, "Error requesting irq at index %d: %d\n",
109 inst_data[i].irq_idx, ret);
110 goto error;
111 }
112 board_info.irq = ret;
113 break;
114 default:
115 board_info.irq = 0;
116 break;
117 }
118 multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info);
119 if (IS_ERR(multi->clients[i])) {
120 ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]),
121 "Error creating i2c-client, idx %d\n", i);
122 goto error;
123 }
124 }
125 if (i < multi->num_clients) {
126 dev_err(dev, "Error finding driver, idx %d\n", i);
127 ret = -ENODEV;
128 goto error;
129 }
130
131 platform_set_drvdata(pdev, multi);
132 return 0;
133
134error:
135 while (--i >= 0)
136 i2c_unregister_device(multi->clients[i]);
137
138 return ret;
139}
140
141static int i2c_multi_inst_remove(struct platform_device *pdev)
142{
143 struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev);
144 int i;
145
146 for (i = 0; i < multi->num_clients; i++)
147 i2c_unregister_device(multi->clients[i]);
148
149 return 0;
150}
151
152static const struct i2c_inst_data bsg1160_data[] = {
153 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
154 { "bmc150_magn" },
155 { "bmg160" },
156 {}
157};
158
159static const struct i2c_inst_data bsg2150_data[] = {
160 { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
161 { "bmc150_magn" },
162
163 { "bsg2150_dummy_dev" },
164 {}
165};
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
196 { "BSG1160", (unsigned long)bsg1160_data },
197 { "BSG2150", (unsigned long)bsg2150_data },
198 { }
199};
200MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids);
201
202static struct platform_driver i2c_multi_inst_driver = {
203 .driver = {
204 .name = "I2C multi instantiate pseudo device driver",
205 .acpi_match_table = i2c_multi_inst_acpi_ids,
206 },
207 .probe = i2c_multi_inst_probe,
208 .remove = i2c_multi_inst_remove,
209};
210module_platform_driver(i2c_multi_inst_driver);
211
212MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
213MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
214MODULE_LICENSE("GPL");
215