xref: /linux/drivers/platform/x86/serial-multi-instantiate.c (revision 68f201f9061c000d7a4a9f359f021b1cd535d62b)
15e63b2eaSLucas Tanure // SPDX-License-Identifier: GPL-2.0+
25e63b2eaSLucas Tanure /*
35e63b2eaSLucas Tanure  * Serial multi-instantiate driver, pseudo driver to instantiate multiple
45e63b2eaSLucas Tanure  * client devices from a single fwnode.
55e63b2eaSLucas Tanure  *
65e63b2eaSLucas Tanure  * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
75e63b2eaSLucas Tanure  */
85e63b2eaSLucas Tanure 
95e63b2eaSLucas Tanure #include <linux/acpi.h>
105e63b2eaSLucas Tanure #include <linux/bits.h>
115e63b2eaSLucas Tanure #include <linux/i2c.h>
125e63b2eaSLucas Tanure #include <linux/interrupt.h>
135e63b2eaSLucas Tanure #include <linux/kernel.h>
145e63b2eaSLucas Tanure #include <linux/module.h>
155e63b2eaSLucas Tanure #include <linux/platform_device.h>
165e63b2eaSLucas Tanure #include <linux/property.h>
17*68f201f9SStefan Binding #include <linux/spi/spi.h>
185e63b2eaSLucas Tanure #include <linux/types.h>
195e63b2eaSLucas Tanure 
205e63b2eaSLucas Tanure #define IRQ_RESOURCE_TYPE	GENMASK(1, 0)
215e63b2eaSLucas Tanure #define IRQ_RESOURCE_NONE	0
225e63b2eaSLucas Tanure #define IRQ_RESOURCE_GPIO	1
235e63b2eaSLucas Tanure #define IRQ_RESOURCE_APIC	2
245e63b2eaSLucas Tanure 
25*68f201f9SStefan Binding enum smi_bus_type {
26*68f201f9SStefan Binding 	SMI_I2C,
27*68f201f9SStefan Binding 	SMI_SPI,
28*68f201f9SStefan Binding 	SMI_AUTO_DETECT,
29*68f201f9SStefan Binding };
30*68f201f9SStefan Binding 
315e63b2eaSLucas Tanure struct smi_instance {
325e63b2eaSLucas Tanure 	const char *type;
335e63b2eaSLucas Tanure 	unsigned int flags;
345e63b2eaSLucas Tanure 	int irq_idx;
355e63b2eaSLucas Tanure };
365e63b2eaSLucas Tanure 
37*68f201f9SStefan Binding struct smi_node {
38*68f201f9SStefan Binding 	enum smi_bus_type bus_type;
39*68f201f9SStefan Binding 	struct smi_instance instances[];
40*68f201f9SStefan Binding };
41*68f201f9SStefan Binding 
425e63b2eaSLucas Tanure struct smi {
435e63b2eaSLucas Tanure 	int i2c_num;
44*68f201f9SStefan Binding 	int spi_num;
4535a36cbbSLucas Tanure 	struct i2c_client **i2c_devs;
46*68f201f9SStefan Binding 	struct spi_device **spi_devs;
475e63b2eaSLucas Tanure };
485e63b2eaSLucas Tanure 
4935a36cbbSLucas Tanure static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,
5035a36cbbSLucas Tanure 		       const struct smi_instance *inst)
515e63b2eaSLucas Tanure {
5235a36cbbSLucas Tanure 	int ret;
535e63b2eaSLucas Tanure 
5435a36cbbSLucas Tanure 	switch (inst->flags & IRQ_RESOURCE_TYPE) {
5535a36cbbSLucas Tanure 	case IRQ_RESOURCE_GPIO:
5635a36cbbSLucas Tanure 		ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx);
5735a36cbbSLucas Tanure 		break;
5835a36cbbSLucas Tanure 	case IRQ_RESOURCE_APIC:
5935a36cbbSLucas Tanure 		ret = platform_get_irq(pdev, inst->irq_idx);
6035a36cbbSLucas Tanure 		break;
6135a36cbbSLucas Tanure 	default:
6235a36cbbSLucas Tanure 		return 0;
635e63b2eaSLucas Tanure 	}
645e63b2eaSLucas Tanure 
6535a36cbbSLucas Tanure 	if (ret < 0)
6635a36cbbSLucas Tanure 		dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n",
6735a36cbbSLucas Tanure 			      inst->irq_idx, ret);
685e63b2eaSLucas Tanure 
6935a36cbbSLucas Tanure 	return ret;
7035a36cbbSLucas Tanure }
7135a36cbbSLucas Tanure 
7235a36cbbSLucas Tanure static void smi_devs_unregister(struct smi *smi)
7335a36cbbSLucas Tanure {
7435a36cbbSLucas Tanure 	while (smi->i2c_num > 0)
7535a36cbbSLucas Tanure 		i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]);
76*68f201f9SStefan Binding 
77*68f201f9SStefan Binding 	while (smi->spi_num > 0)
78*68f201f9SStefan Binding 		spi_unregister_device(smi->spi_devs[--smi->spi_num]);
79*68f201f9SStefan Binding }
80*68f201f9SStefan Binding 
81*68f201f9SStefan Binding /**
82*68f201f9SStefan Binding  * smi_spi_probe - Instantiate multiple SPI devices from inst array
83*68f201f9SStefan Binding  * @pdev:	Platform device
84*68f201f9SStefan Binding  * @adev:	ACPI device
85*68f201f9SStefan Binding  * @smi:	Internal struct for Serial multi instantiate driver
86*68f201f9SStefan Binding  * @inst_array:	Array of instances to probe
87*68f201f9SStefan Binding  *
88*68f201f9SStefan Binding  * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code.
89*68f201f9SStefan Binding  */
90*68f201f9SStefan Binding static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
91*68f201f9SStefan Binding 			 const struct smi_instance *inst_array)
92*68f201f9SStefan Binding {
93*68f201f9SStefan Binding 	struct device *dev = &pdev->dev;
94*68f201f9SStefan Binding 	struct spi_controller *ctlr;
95*68f201f9SStefan Binding 	struct spi_device *spi_dev;
96*68f201f9SStefan Binding 	char name[50];
97*68f201f9SStefan Binding 	int i, ret, count;
98*68f201f9SStefan Binding 
99*68f201f9SStefan Binding 	ret = acpi_spi_count_resources(adev);
100*68f201f9SStefan Binding 	if (ret < 0)
101*68f201f9SStefan Binding 		return ret;
102*68f201f9SStefan Binding 	else if (!ret)
103*68f201f9SStefan Binding 		return -ENODEV;
104*68f201f9SStefan Binding 
105*68f201f9SStefan Binding 	count = ret;
106*68f201f9SStefan Binding 
107*68f201f9SStefan Binding 	smi->spi_devs = devm_kcalloc(dev, count, sizeof(*smi->spi_devs), GFP_KERNEL);
108*68f201f9SStefan Binding 	if (!smi->spi_devs)
109*68f201f9SStefan Binding 		return -ENOMEM;
110*68f201f9SStefan Binding 
111*68f201f9SStefan Binding 	for (i = 0; i < count && inst_array[i].type; i++) {
112*68f201f9SStefan Binding 
113*68f201f9SStefan Binding 		spi_dev = acpi_spi_device_alloc(NULL, adev, i);
114*68f201f9SStefan Binding 		if (IS_ERR(spi_dev)) {
115*68f201f9SStefan Binding 			ret = PTR_ERR(spi_dev);
116*68f201f9SStefan Binding 			dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n",
117*68f201f9SStefan Binding 				      dev_name(&adev->dev), ret);
118*68f201f9SStefan Binding 			goto error;
119*68f201f9SStefan Binding 		}
120*68f201f9SStefan Binding 
121*68f201f9SStefan Binding 		ctlr = spi_dev->controller;
122*68f201f9SStefan Binding 
123*68f201f9SStefan Binding 		strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias));
124*68f201f9SStefan Binding 
125*68f201f9SStefan Binding 		ret = smi_get_irq(pdev, adev, &inst_array[i]);
126*68f201f9SStefan Binding 		if (ret < 0) {
127*68f201f9SStefan Binding 			spi_dev_put(spi_dev);
128*68f201f9SStefan Binding 			goto error;
129*68f201f9SStefan Binding 		}
130*68f201f9SStefan Binding 		spi_dev->irq = ret;
131*68f201f9SStefan Binding 
132*68f201f9SStefan Binding 		snprintf(name, sizeof(name), "%s-%s-%s.%d", dev_name(&ctlr->dev), dev_name(dev),
133*68f201f9SStefan Binding 			 inst_array[i].type, i);
134*68f201f9SStefan Binding 		spi_dev->dev.init_name = name;
135*68f201f9SStefan Binding 
136*68f201f9SStefan Binding 		ret = spi_add_device(spi_dev);
137*68f201f9SStefan Binding 		if (ret) {
138*68f201f9SStefan Binding 			dev_err_probe(&ctlr->dev, ret,
139*68f201f9SStefan Binding 				      "failed to add SPI device %s from ACPI: %d\n",
140*68f201f9SStefan Binding 				      dev_name(&adev->dev), ret);
141*68f201f9SStefan Binding 			spi_dev_put(spi_dev);
142*68f201f9SStefan Binding 			goto error;
143*68f201f9SStefan Binding 		}
144*68f201f9SStefan Binding 
145*68f201f9SStefan Binding 		dev_dbg(dev, "SPI device %s using chip select %u", name, spi_dev->chip_select);
146*68f201f9SStefan Binding 
147*68f201f9SStefan Binding 		smi->spi_devs[i] = spi_dev;
148*68f201f9SStefan Binding 		smi->spi_num++;
149*68f201f9SStefan Binding 	}
150*68f201f9SStefan Binding 
151*68f201f9SStefan Binding 	if (smi->spi_num < count) {
152*68f201f9SStefan Binding 		dev_dbg(dev, "Error finding driver, idx %d\n", i);
153*68f201f9SStefan Binding 		ret = -ENODEV;
154*68f201f9SStefan Binding 		goto error;
155*68f201f9SStefan Binding 	}
156*68f201f9SStefan Binding 
157*68f201f9SStefan Binding 	dev_info(dev, "Instantiated %d SPI devices.\n", smi->spi_num);
158*68f201f9SStefan Binding 
159*68f201f9SStefan Binding 	return 0;
160*68f201f9SStefan Binding error:
161*68f201f9SStefan Binding 	smi_devs_unregister(smi);
162*68f201f9SStefan Binding 
163*68f201f9SStefan Binding 	return ret;
16435a36cbbSLucas Tanure }
16535a36cbbSLucas Tanure 
16635a36cbbSLucas Tanure /**
16735a36cbbSLucas Tanure  * smi_i2c_probe - Instantiate multiple I2C devices from inst array
16835a36cbbSLucas Tanure  * @pdev:	Platform device
16935a36cbbSLucas Tanure  * @adev:	ACPI device
17035a36cbbSLucas Tanure  * @smi:	Internal struct for Serial multi instantiate driver
17135a36cbbSLucas Tanure  * @inst_array:	Array of instances to probe
17235a36cbbSLucas Tanure  *
17335a36cbbSLucas Tanure  * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code.
17435a36cbbSLucas Tanure  */
17535a36cbbSLucas Tanure static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi,
17635a36cbbSLucas Tanure 			 const struct smi_instance *inst_array)
17735a36cbbSLucas Tanure {
17835a36cbbSLucas Tanure 	struct i2c_board_info board_info = {};
17935a36cbbSLucas Tanure 	struct device *dev = &pdev->dev;
18035a36cbbSLucas Tanure 	char name[32];
18135a36cbbSLucas Tanure 	int i, ret, count;
18235a36cbbSLucas Tanure 
1835e63b2eaSLucas Tanure 	ret = i2c_acpi_client_count(adev);
1845e63b2eaSLucas Tanure 	if (ret < 0)
1855e63b2eaSLucas Tanure 		return ret;
18635a36cbbSLucas Tanure 	else if (!ret)
18735a36cbbSLucas Tanure 		return -ENODEV;
1885e63b2eaSLucas Tanure 
18935a36cbbSLucas Tanure 	count = ret;
19035a36cbbSLucas Tanure 
19135a36cbbSLucas Tanure 	smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL);
19235a36cbbSLucas Tanure 	if (!smi->i2c_devs)
1935e63b2eaSLucas Tanure 		return -ENOMEM;
1945e63b2eaSLucas Tanure 
19535a36cbbSLucas Tanure 	for (i = 0; i < count && inst_array[i].type; i++) {
1965e63b2eaSLucas Tanure 		memset(&board_info, 0, sizeof(board_info));
19735a36cbbSLucas Tanure 		strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE);
19835a36cbbSLucas Tanure 		snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i);
1995e63b2eaSLucas Tanure 		board_info.dev_name = name;
20035a36cbbSLucas Tanure 
20135a36cbbSLucas Tanure 		ret = smi_get_irq(pdev, adev, &inst_array[i]);
20235a36cbbSLucas Tanure 		if (ret < 0)
2035e63b2eaSLucas Tanure 			goto error;
2045e63b2eaSLucas Tanure 		board_info.irq = ret;
20535a36cbbSLucas Tanure 
2065e63b2eaSLucas Tanure 		smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info);
2075e63b2eaSLucas Tanure 		if (IS_ERR(smi->i2c_devs[i])) {
2085e63b2eaSLucas Tanure 			ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]),
2095e63b2eaSLucas Tanure 					    "Error creating i2c-client, idx %d\n", i);
2105e63b2eaSLucas Tanure 			goto error;
2115e63b2eaSLucas Tanure 		}
21235a36cbbSLucas Tanure 		smi->i2c_num++;
2135e63b2eaSLucas Tanure 	}
21435a36cbbSLucas Tanure 	if (smi->i2c_num < count) {
21535a36cbbSLucas Tanure 		dev_dbg(dev, "Error finding driver, idx %d\n", i);
2165e63b2eaSLucas Tanure 		ret = -ENODEV;
2175e63b2eaSLucas Tanure 		goto error;
2185e63b2eaSLucas Tanure 	}
2195e63b2eaSLucas Tanure 
22035a36cbbSLucas Tanure 	dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num);
2215e63b2eaSLucas Tanure 
22235a36cbbSLucas Tanure 	return 0;
2235e63b2eaSLucas Tanure error:
22435a36cbbSLucas Tanure 	smi_devs_unregister(smi);
2255e63b2eaSLucas Tanure 
2265e63b2eaSLucas Tanure 	return ret;
2275e63b2eaSLucas Tanure }
2285e63b2eaSLucas Tanure 
22935a36cbbSLucas Tanure static int smi_probe(struct platform_device *pdev)
23035a36cbbSLucas Tanure {
23135a36cbbSLucas Tanure 	struct device *dev = &pdev->dev;
232*68f201f9SStefan Binding 	const struct smi_node *node;
23335a36cbbSLucas Tanure 	struct acpi_device *adev;
23435a36cbbSLucas Tanure 	struct smi *smi;
23535a36cbbSLucas Tanure 
23635a36cbbSLucas Tanure 	adev = ACPI_COMPANION(dev);
23735a36cbbSLucas Tanure 	if (!adev)
23835a36cbbSLucas Tanure 		return -ENODEV;
23935a36cbbSLucas Tanure 
240*68f201f9SStefan Binding 	node = device_get_match_data(dev);
241*68f201f9SStefan Binding 	if (!node) {
24235a36cbbSLucas Tanure 		dev_dbg(dev, "Error ACPI match data is missing\n");
24335a36cbbSLucas Tanure 		return -ENODEV;
24435a36cbbSLucas Tanure 	}
24535a36cbbSLucas Tanure 
24635a36cbbSLucas Tanure 	smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL);
24735a36cbbSLucas Tanure 	if (!smi)
24835a36cbbSLucas Tanure 		return -ENOMEM;
24935a36cbbSLucas Tanure 
25035a36cbbSLucas Tanure 	platform_set_drvdata(pdev, smi);
25135a36cbbSLucas Tanure 
252*68f201f9SStefan Binding 	switch (node->bus_type) {
253*68f201f9SStefan Binding 	case SMI_I2C:
254*68f201f9SStefan Binding 		return smi_i2c_probe(pdev, adev, smi, node->instances);
255*68f201f9SStefan Binding 	case SMI_SPI:
256*68f201f9SStefan Binding 		return smi_spi_probe(pdev, adev, smi, node->instances);
257*68f201f9SStefan Binding 	case SMI_AUTO_DETECT:
258*68f201f9SStefan Binding 		if (i2c_acpi_client_count(adev) > 0)
259*68f201f9SStefan Binding 			return smi_i2c_probe(pdev, adev, smi, node->instances);
260*68f201f9SStefan Binding 		else
261*68f201f9SStefan Binding 			return smi_spi_probe(pdev, adev, smi, node->instances);
262*68f201f9SStefan Binding 	default:
263*68f201f9SStefan Binding 		return -EINVAL;
264*68f201f9SStefan Binding 	}
265*68f201f9SStefan Binding 
266*68f201f9SStefan Binding 	return 0; /* never reached */
26735a36cbbSLucas Tanure }
26835a36cbbSLucas Tanure 
2695e63b2eaSLucas Tanure static int smi_remove(struct platform_device *pdev)
2705e63b2eaSLucas Tanure {
2715e63b2eaSLucas Tanure 	struct smi *smi = platform_get_drvdata(pdev);
2725e63b2eaSLucas Tanure 
27335a36cbbSLucas Tanure 	smi_devs_unregister(smi);
2745e63b2eaSLucas Tanure 
2755e63b2eaSLucas Tanure 	return 0;
2765e63b2eaSLucas Tanure }
2775e63b2eaSLucas Tanure 
278*68f201f9SStefan Binding static const struct smi_node bsg1160_data = {
279*68f201f9SStefan Binding 	.instances = {
2805e63b2eaSLucas Tanure 		{ "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
2815e63b2eaSLucas Tanure 		{ "bmc150_magn" },
2825e63b2eaSLucas Tanure 		{ "bmg160" },
2835e63b2eaSLucas Tanure 		{}
284*68f201f9SStefan Binding 	},
285*68f201f9SStefan Binding 	.bus_type = SMI_I2C,
2865e63b2eaSLucas Tanure };
2875e63b2eaSLucas Tanure 
288*68f201f9SStefan Binding static const struct smi_node bsg2150_data = {
289*68f201f9SStefan Binding 	.instances = {
2905e63b2eaSLucas Tanure 		{ "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
2915e63b2eaSLucas Tanure 		{ "bmc150_magn" },
2925e63b2eaSLucas Tanure 		/* The resources describe a 3th client, but it is not really there. */
2935e63b2eaSLucas Tanure 		{ "bsg2150_dummy_dev" },
2945e63b2eaSLucas Tanure 		{}
295*68f201f9SStefan Binding 	},
296*68f201f9SStefan Binding 	.bus_type = SMI_I2C,
2975e63b2eaSLucas Tanure };
2985e63b2eaSLucas Tanure 
299*68f201f9SStefan Binding static const struct smi_node int3515_data = {
300*68f201f9SStefan Binding 	.instances = {
3015e63b2eaSLucas Tanure 		{ "tps6598x", IRQ_RESOURCE_APIC, 0 },
3025e63b2eaSLucas Tanure 		{ "tps6598x", IRQ_RESOURCE_APIC, 1 },
3035e63b2eaSLucas Tanure 		{ "tps6598x", IRQ_RESOURCE_APIC, 2 },
3045e63b2eaSLucas Tanure 		{ "tps6598x", IRQ_RESOURCE_APIC, 3 },
3055e63b2eaSLucas Tanure 		{}
306*68f201f9SStefan Binding 	},
307*68f201f9SStefan Binding 	.bus_type = SMI_I2C,
3085e63b2eaSLucas Tanure };
3095e63b2eaSLucas Tanure 
3105e63b2eaSLucas Tanure /*
3115e63b2eaSLucas Tanure  * Note new device-ids must also be added to ignore_serial_bus_ids in
3125e63b2eaSLucas Tanure  * drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
3135e63b2eaSLucas Tanure  */
3145e63b2eaSLucas Tanure static const struct acpi_device_id smi_acpi_ids[] = {
315*68f201f9SStefan Binding 	{ "BSG1160", (unsigned long)&bsg1160_data },
316*68f201f9SStefan Binding 	{ "BSG2150", (unsigned long)&bsg2150_data },
317*68f201f9SStefan Binding 	{ "INT3515", (unsigned long)&int3515_data },
3185e63b2eaSLucas Tanure 	{ }
3195e63b2eaSLucas Tanure };
3205e63b2eaSLucas Tanure MODULE_DEVICE_TABLE(acpi, smi_acpi_ids);
3215e63b2eaSLucas Tanure 
3225e63b2eaSLucas Tanure static struct platform_driver smi_driver = {
3235e63b2eaSLucas Tanure 	.driver	= {
3245e63b2eaSLucas Tanure 		.name = "Serial bus multi instantiate pseudo device driver",
3255e63b2eaSLucas Tanure 		.acpi_match_table = smi_acpi_ids,
3265e63b2eaSLucas Tanure 	},
3275e63b2eaSLucas Tanure 	.probe = smi_probe,
3285e63b2eaSLucas Tanure 	.remove = smi_remove,
3295e63b2eaSLucas Tanure };
3305e63b2eaSLucas Tanure module_platform_driver(smi_driver);
3315e63b2eaSLucas Tanure 
3325e63b2eaSLucas Tanure MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver");
3335e63b2eaSLucas Tanure MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
3345e63b2eaSLucas Tanure MODULE_LICENSE("GPL");
335