1 // SPDX-License-Identifier: GPL-2.0+
3 * I2C multi-instantiate driver, pseudo driver to instantiate multiple
4 * i2c-clients from a single fwnode.
6 * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
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>
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
23 struct i2c_inst_data
{
29 struct i2c_multi_inst_data
{
31 struct i2c_client
*clients
[0];
34 static int i2c_multi_inst_count(struct acpi_resource
*ares
, void *data
)
36 struct acpi_resource_i2c_serialbus
*sb
;
39 if (i2c_acpi_get_i2c_resource(ares
, &sb
))
45 static int i2c_multi_inst_count_resources(struct acpi_device
*adev
)
51 ret
= acpi_dev_get_resources(adev
, &r
, i2c_multi_inst_count
, &count
);
55 acpi_dev_free_resource_list(&r
);
59 static int i2c_multi_inst_probe(struct platform_device
*pdev
)
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
;
70 match
= acpi_match_device(dev
->driver
->acpi_match_table
, dev
);
72 dev_err(dev
, "Error ACPI match data is missing\n");
75 inst_data
= (const struct i2c_inst_data
*)match
->driver_data
;
77 adev
= ACPI_COMPANION(dev
);
79 /* Count number of clients to instantiate */
80 ret
= i2c_multi_inst_count_resources(adev
);
84 multi
= devm_kmalloc(dev
, struct_size(multi
, clients
, ret
), GFP_KERNEL
);
88 multi
->num_clients
= ret
;
90 for (i
= 0; i
< multi
->num_clients
&& inst_data
[i
].type
; i
++) {
91 memset(&board_info
, 0, sizeof(board_info
));
92 strlcpy(board_info
.type
, inst_data
[i
].type
, I2C_NAME_SIZE
);
93 snprintf(name
, sizeof(name
), "%s-%s.%d", dev_name(dev
),
94 inst_data
[i
].type
, i
);
95 board_info
.dev_name
= name
;
96 switch (inst_data
[i
].flags
& IRQ_RESOURCE_TYPE
) {
97 case IRQ_RESOURCE_GPIO
:
98 ret
= acpi_dev_gpio_irq_get(adev
, inst_data
[i
].irq_idx
);
100 dev_err(dev
, "Error requesting irq at index %d: %d\n",
101 inst_data
[i
].irq_idx
, ret
);
104 board_info
.irq
= ret
;
106 case IRQ_RESOURCE_APIC
:
107 ret
= platform_get_irq(pdev
, inst_data
[i
].irq_idx
);
109 dev_dbg(dev
, "Error requesting irq at index %d: %d\n",
110 inst_data
[i
].irq_idx
, ret
);
113 board_info
.irq
= ret
;
119 multi
->clients
[i
] = i2c_acpi_new_device(dev
, i
, &board_info
);
120 if (IS_ERR(multi
->clients
[i
])) {
121 ret
= PTR_ERR(multi
->clients
[i
]);
122 if (ret
!= -EPROBE_DEFER
)
123 dev_err(dev
, "Error creating i2c-client, idx %d\n", i
);
127 if (i
< multi
->num_clients
) {
128 dev_err(dev
, "Error finding driver, idx %d\n", i
);
133 platform_set_drvdata(pdev
, multi
);
138 i2c_unregister_device(multi
->clients
[i
]);
143 static int i2c_multi_inst_remove(struct platform_device
*pdev
)
145 struct i2c_multi_inst_data
*multi
= platform_get_drvdata(pdev
);
148 for (i
= 0; i
< multi
->num_clients
; i
++)
149 i2c_unregister_device(multi
->clients
[i
]);
154 static const struct i2c_inst_data bsg1160_data
[] = {
155 { "bmc150_accel", IRQ_RESOURCE_GPIO
, 0 },
161 static const struct i2c_inst_data bsg2150_data
[] = {
162 { "bmc150_accel", IRQ_RESOURCE_GPIO
, 0 },
164 /* The resources describe a 3th client, but it is not really there. */
165 { "bsg2150_dummy_dev" },
169 static const struct i2c_inst_data int3515_data
[] = {
170 { "tps6598x", IRQ_RESOURCE_APIC
, 0 },
171 { "tps6598x", IRQ_RESOURCE_APIC
, 1 },
172 { "tps6598x", IRQ_RESOURCE_APIC
, 2 },
173 { "tps6598x", IRQ_RESOURCE_APIC
, 3 },
178 * Note new device-ids must also be added to i2c_multi_instantiate_ids in
179 * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
181 static const struct acpi_device_id i2c_multi_inst_acpi_ids
[] = {
182 { "BSG1160", (unsigned long)bsg1160_data
},
183 { "BSG2150", (unsigned long)bsg2150_data
},
184 { "INT3515", (unsigned long)int3515_data
},
187 MODULE_DEVICE_TABLE(acpi
, i2c_multi_inst_acpi_ids
);
189 static struct platform_driver i2c_multi_inst_driver
= {
191 .name
= "I2C multi instantiate pseudo device driver",
192 .acpi_match_table
= ACPI_PTR(i2c_multi_inst_acpi_ids
),
194 .probe
= i2c_multi_inst_probe
,
195 .remove
= i2c_multi_inst_remove
,
197 module_platform_driver(i2c_multi_inst_driver
);
199 MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
200 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
201 MODULE_LICENSE("GPL");