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/i2c.h>
11 #include <linux/interrupt.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
16 struct i2c_inst_data
{
21 struct i2c_multi_inst_data
{
23 struct i2c_client
*clients
[0];
26 static int i2c_multi_inst_probe(struct platform_device
*pdev
)
28 struct i2c_multi_inst_data
*multi
;
29 const struct acpi_device_id
*match
;
30 const struct i2c_inst_data
*inst_data
;
31 struct i2c_board_info board_info
= {};
32 struct device
*dev
= &pdev
->dev
;
33 struct acpi_device
*adev
;
37 match
= acpi_match_device(dev
->driver
->acpi_match_table
, dev
);
39 dev_err(dev
, "Error ACPI match data is missing\n");
42 inst_data
= (const struct i2c_inst_data
*)match
->driver_data
;
44 adev
= ACPI_COMPANION(dev
);
46 /* Count number of clients to instantiate */
47 for (i
= 0; inst_data
[i
].type
; i
++) {}
49 multi
= devm_kmalloc(dev
,
50 offsetof(struct i2c_multi_inst_data
, clients
[i
]),
55 multi
->num_clients
= i
;
57 for (i
= 0; i
< multi
->num_clients
; i
++) {
58 memset(&board_info
, 0, sizeof(board_info
));
59 strlcpy(board_info
.type
, inst_data
[i
].type
, I2C_NAME_SIZE
);
60 snprintf(name
, sizeof(name
), "%s-%s", match
->id
,
62 board_info
.dev_name
= name
;
64 if (inst_data
[i
].gpio_irq_idx
!= -1) {
65 ret
= acpi_dev_gpio_irq_get(adev
,
66 inst_data
[i
].gpio_irq_idx
);
68 dev_err(dev
, "Error requesting irq at index %d: %d\n",
69 inst_data
[i
].gpio_irq_idx
, ret
);
74 multi
->clients
[i
] = i2c_acpi_new_device(dev
, i
, &board_info
);
75 if (!multi
->clients
[i
]) {
76 dev_err(dev
, "Error creating i2c-client, idx %d\n", i
);
82 platform_set_drvdata(pdev
, multi
);
87 i2c_unregister_device(multi
->clients
[i
]);
92 static int i2c_multi_inst_remove(struct platform_device
*pdev
)
94 struct i2c_multi_inst_data
*multi
= platform_get_drvdata(pdev
);
97 for (i
= 0; i
< multi
->num_clients
; i
++)
98 i2c_unregister_device(multi
->clients
[i
]);
103 static const struct i2c_inst_data bsg1160_data
[] = {
104 { "bmc150_accel", 0 },
105 { "bmc150_magn", -1 },
111 * Note new device-ids must also be added to i2c_multi_instantiate_ids in
112 * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
114 static const struct acpi_device_id i2c_multi_inst_acpi_ids
[] = {
115 { "BSG1160", (unsigned long)bsg1160_data
},
118 MODULE_DEVICE_TABLE(acpi
, i2c_multi_inst_acpi_ids
);
120 static struct platform_driver i2c_multi_inst_driver
= {
122 .name
= "I2C multi instantiate pseudo device driver",
123 .acpi_match_table
= ACPI_PTR(i2c_multi_inst_acpi_ids
),
125 .probe
= i2c_multi_inst_probe
,
126 .remove
= i2c_multi_inst_remove
,
128 module_platform_driver(i2c_multi_inst_driver
);
130 MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
131 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
132 MODULE_LICENSE("GPL");