xref: /openbmc/linux/drivers/platform/x86/intel/int3472/tps68470.c (revision 360823a09426347ea8f232b0b0b5156d0aed0302)
1a2f9fbc2SHans de Goede // SPDX-License-Identifier: GPL-2.0
2a2f9fbc2SHans de Goede /* Author: Dan Scally <djrscally@gmail.com> */
3a2f9fbc2SHans de Goede 
443cf3697SDaniel Scally #include <linux/acpi.h>
5a2f9fbc2SHans de Goede #include <linux/i2c.h>
619d8d6e3SHans de Goede #include <linux/kernel.h>
7a2f9fbc2SHans de Goede #include <linux/mfd/core.h>
8a2f9fbc2SHans de Goede #include <linux/mfd/tps68470.h>
9a2f9fbc2SHans de Goede #include <linux/platform_device.h>
10d3d76ae1SHans de Goede #include <linux/platform_data/tps68470.h>
11a2f9fbc2SHans de Goede #include <linux/regmap.h>
1219d8d6e3SHans de Goede #include <linux/string.h>
13a2f9fbc2SHans de Goede 
14a2f9fbc2SHans de Goede #include "common.h"
1519d8d6e3SHans de Goede #include "tps68470.h"
16a2f9fbc2SHans de Goede 
17a2f9fbc2SHans de Goede #define DESIGNED_FOR_CHROMEOS		1
18a2f9fbc2SHans de Goede #define DESIGNED_FOR_WINDOWS		2
19a2f9fbc2SHans de Goede 
20d3d76ae1SHans de Goede #define TPS68470_WIN_MFD_CELL_COUNT	3
21d3d76ae1SHans de Goede 
22a2f9fbc2SHans de Goede static const struct mfd_cell tps68470_cros[] = {
23a2f9fbc2SHans de Goede 	{ .name = "tps68470-gpio" },
24a2f9fbc2SHans de Goede 	{ .name = "tps68470_pmic_opregion" },
25a2f9fbc2SHans de Goede };
26a2f9fbc2SHans de Goede 
27a2f9fbc2SHans de Goede static const struct regmap_config tps68470_regmap_config = {
28a2f9fbc2SHans de Goede 	.reg_bits = 8,
29a2f9fbc2SHans de Goede 	.val_bits = 8,
30a2f9fbc2SHans de Goede 	.max_register = TPS68470_REG_MAX,
31a2f9fbc2SHans de Goede };
32a2f9fbc2SHans de Goede 
tps68470_chip_init(struct device * dev,struct regmap * regmap)33a2f9fbc2SHans de Goede static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
34a2f9fbc2SHans de Goede {
35a2f9fbc2SHans de Goede 	unsigned int version;
36a2f9fbc2SHans de Goede 	int ret;
37a2f9fbc2SHans de Goede 
38a2f9fbc2SHans de Goede 	/* Force software reset */
39a2f9fbc2SHans de Goede 	ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
40a2f9fbc2SHans de Goede 	if (ret)
41a2f9fbc2SHans de Goede 		return ret;
42a2f9fbc2SHans de Goede 
43a2f9fbc2SHans de Goede 	ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
44a2f9fbc2SHans de Goede 	if (ret) {
45a2f9fbc2SHans de Goede 		dev_err(dev, "Failed to read revision register: %d\n", ret);
46a2f9fbc2SHans de Goede 		return ret;
47a2f9fbc2SHans de Goede 	}
48a2f9fbc2SHans de Goede 
49a2f9fbc2SHans de Goede 	dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
50a2f9fbc2SHans de Goede 
51a2f9fbc2SHans de Goede 	return 0;
52a2f9fbc2SHans de Goede }
53a2f9fbc2SHans de Goede 
54a2f9fbc2SHans de Goede /** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
55a2f9fbc2SHans de Goede  * @adev: A pointer to a &struct acpi_device
56a2f9fbc2SHans de Goede  *
57a2f9fbc2SHans de Goede  * Check CLDB buffer against the PMIC's adev. If present, then we check
58a2f9fbc2SHans de Goede  * the value of control_logic_type field and follow one of the
59a2f9fbc2SHans de Goede  * following scenarios:
60a2f9fbc2SHans de Goede  *
61a2f9fbc2SHans de Goede  *	1. No CLDB - likely ACPI tables designed for ChromeOS. We
62a2f9fbc2SHans de Goede  *	create platform devices for the GPIOs and OpRegion drivers.
63a2f9fbc2SHans de Goede  *
64a2f9fbc2SHans de Goede  *	2. CLDB, with control_logic_type = 2 - probably ACPI tables
65a2f9fbc2SHans de Goede  *	made for Windows 2-in-1 platforms. Register pdevs for GPIO,
66a2f9fbc2SHans de Goede  *	Clock and Regulator drivers to bind to.
67a2f9fbc2SHans de Goede  *
68a2f9fbc2SHans de Goede  *	3. Any other value in control_logic_type, we should never have
69a2f9fbc2SHans de Goede  *	gotten to this point; fail probe and return.
70a2f9fbc2SHans de Goede  *
71a2f9fbc2SHans de Goede  * Return:
72a2f9fbc2SHans de Goede  * * 1		Device intended for ChromeOS
73a2f9fbc2SHans de Goede  * * 2		Device intended for Windows
74a2f9fbc2SHans de Goede  * * -EINVAL	Where @adev has an object named CLDB but it does not conform to
75a2f9fbc2SHans de Goede  *		our expectations
76a2f9fbc2SHans de Goede  */
skl_int3472_tps68470_calc_type(struct acpi_device * adev)77a2f9fbc2SHans de Goede static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
78a2f9fbc2SHans de Goede {
79a2f9fbc2SHans de Goede 	struct int3472_cldb cldb = { 0 };
80a2f9fbc2SHans de Goede 	int ret;
81a2f9fbc2SHans de Goede 
82a2f9fbc2SHans de Goede 	/*
83a2f9fbc2SHans de Goede 	 * A CLDB buffer that exists, but which does not match our expectations
84a2f9fbc2SHans de Goede 	 * should trigger an error so we don't blindly continue.
85a2f9fbc2SHans de Goede 	 */
86a2f9fbc2SHans de Goede 	ret = skl_int3472_fill_cldb(adev, &cldb);
87a2f9fbc2SHans de Goede 	if (ret && ret != -ENODEV)
88a2f9fbc2SHans de Goede 		return ret;
89a2f9fbc2SHans de Goede 
90a2f9fbc2SHans de Goede 	if (ret)
91a2f9fbc2SHans de Goede 		return DESIGNED_FOR_CHROMEOS;
92a2f9fbc2SHans de Goede 
93a2f9fbc2SHans de Goede 	if (cldb.control_logic_type != 2)
94a2f9fbc2SHans de Goede 		return -EINVAL;
95a2f9fbc2SHans de Goede 
96a2f9fbc2SHans de Goede 	return DESIGNED_FOR_WINDOWS;
97a2f9fbc2SHans de Goede }
98a2f9fbc2SHans de Goede 
9943cf3697SDaniel Scally /*
10043cf3697SDaniel Scally  * Return the size of the flexible array member, because we'll need that later
10143cf3697SDaniel Scally  * on to pass .pdata_size to cells.
10243cf3697SDaniel Scally  */
10343cf3697SDaniel Scally static int
skl_int3472_fill_clk_pdata(struct device * dev,struct tps68470_clk_platform_data ** clk_pdata)10443cf3697SDaniel Scally skl_int3472_fill_clk_pdata(struct device *dev, struct tps68470_clk_platform_data **clk_pdata)
10543cf3697SDaniel Scally {
10643cf3697SDaniel Scally 	struct acpi_device *adev = ACPI_COMPANION(dev);
10743cf3697SDaniel Scally 	struct acpi_device *consumer;
10843cf3697SDaniel Scally 	unsigned int n_consumers = 0;
10943cf3697SDaniel Scally 	const char *sensor_name;
11043cf3697SDaniel Scally 	unsigned int i = 0;
11143cf3697SDaniel Scally 
11243cf3697SDaniel Scally 	for_each_acpi_consumer_dev(adev, consumer)
11343cf3697SDaniel Scally 		n_consumers++;
11443cf3697SDaniel Scally 
11543cf3697SDaniel Scally 	if (!n_consumers) {
11643cf3697SDaniel Scally 		dev_err(dev, "INT3472 seems to have no dependents\n");
11743cf3697SDaniel Scally 		return -ENODEV;
11843cf3697SDaniel Scally 	}
11943cf3697SDaniel Scally 
12043cf3697SDaniel Scally 	*clk_pdata = devm_kzalloc(dev, struct_size(*clk_pdata, consumers, n_consumers),
12143cf3697SDaniel Scally 				  GFP_KERNEL);
12243cf3697SDaniel Scally 	if (!*clk_pdata)
12343cf3697SDaniel Scally 		return -ENOMEM;
12443cf3697SDaniel Scally 
12543cf3697SDaniel Scally 	(*clk_pdata)->n_consumers = n_consumers;
12643cf3697SDaniel Scally 	i = 0;
12743cf3697SDaniel Scally 
12843cf3697SDaniel Scally 	for_each_acpi_consumer_dev(adev, consumer) {
12943cf3697SDaniel Scally 		sensor_name = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT,
13043cf3697SDaniel Scally 					     acpi_dev_name(consumer));
131800b8eecSAndy Shevchenko 		if (!sensor_name) {
132800b8eecSAndy Shevchenko 			acpi_dev_put(consumer);
13343cf3697SDaniel Scally 			return -ENOMEM;
134800b8eecSAndy Shevchenko 		}
13543cf3697SDaniel Scally 
13643cf3697SDaniel Scally 		(*clk_pdata)->consumers[i].consumer_dev_name = sensor_name;
13743cf3697SDaniel Scally 		i++;
13843cf3697SDaniel Scally 	}
13943cf3697SDaniel Scally 
14043cf3697SDaniel Scally 	return n_consumers;
14143cf3697SDaniel Scally }
14243cf3697SDaniel Scally 
skl_int3472_tps68470_probe(struct i2c_client * client)143a2f9fbc2SHans de Goede static int skl_int3472_tps68470_probe(struct i2c_client *client)
144a2f9fbc2SHans de Goede {
145a2f9fbc2SHans de Goede 	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
14619d8d6e3SHans de Goede 	const struct int3472_tps68470_board_data *board_data;
14743cf3697SDaniel Scally 	struct tps68470_clk_platform_data *clk_pdata;
148d3d76ae1SHans de Goede 	struct mfd_cell *cells;
149a2f9fbc2SHans de Goede 	struct regmap *regmap;
15043cf3697SDaniel Scally 	int n_consumers;
151a2f9fbc2SHans de Goede 	int device_type;
152a2f9fbc2SHans de Goede 	int ret;
15306a659d1SDaniel Scally 	int i;
154a2f9fbc2SHans de Goede 
155*f9c7cc44SHans de Goede 	if (!adev)
156*f9c7cc44SHans de Goede 		return -ENODEV;
157*f9c7cc44SHans de Goede 
15843cf3697SDaniel Scally 	n_consumers = skl_int3472_fill_clk_pdata(&client->dev, &clk_pdata);
15943cf3697SDaniel Scally 	if (n_consumers < 0)
16043cf3697SDaniel Scally 		return n_consumers;
161d3d76ae1SHans de Goede 
162a2f9fbc2SHans de Goede 	regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
163a2f9fbc2SHans de Goede 	if (IS_ERR(regmap)) {
164a2f9fbc2SHans de Goede 		dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
165a2f9fbc2SHans de Goede 		return PTR_ERR(regmap);
166a2f9fbc2SHans de Goede 	}
167a2f9fbc2SHans de Goede 
168a2f9fbc2SHans de Goede 	i2c_set_clientdata(client, regmap);
169a2f9fbc2SHans de Goede 
170a2f9fbc2SHans de Goede 	ret = tps68470_chip_init(&client->dev, regmap);
171a2f9fbc2SHans de Goede 	if (ret < 0) {
172a2f9fbc2SHans de Goede 		dev_err(&client->dev, "TPS68470 init error %d\n", ret);
173a2f9fbc2SHans de Goede 		return ret;
174a2f9fbc2SHans de Goede 	}
175a2f9fbc2SHans de Goede 
176a2f9fbc2SHans de Goede 	device_type = skl_int3472_tps68470_calc_type(adev);
177a2f9fbc2SHans de Goede 	switch (device_type) {
178a2f9fbc2SHans de Goede 	case DESIGNED_FOR_WINDOWS:
17919d8d6e3SHans de Goede 		board_data = int3472_tps68470_get_board_data(dev_name(&client->dev));
18019d8d6e3SHans de Goede 		if (!board_data)
18119d8d6e3SHans de Goede 			return dev_err_probe(&client->dev, -ENODEV, "No board-data found for this model\n");
18219d8d6e3SHans de Goede 
183d3d76ae1SHans de Goede 		cells = kcalloc(TPS68470_WIN_MFD_CELL_COUNT, sizeof(*cells), GFP_KERNEL);
184d3d76ae1SHans de Goede 		if (!cells)
185d3d76ae1SHans de Goede 			return -ENOMEM;
186d3d76ae1SHans de Goede 
187d3d76ae1SHans de Goede 		/*
188d3d76ae1SHans de Goede 		 * The order of the cells matters here! The clk must be first
189d3d76ae1SHans de Goede 		 * because the regulator depends on it. The gpios must be last,
190d3d76ae1SHans de Goede 		 * acpi_gpiochip_add() calls acpi_dev_clear_dependencies() and
191d3d76ae1SHans de Goede 		 * the clk + regulators must be ready when this happens.
192d3d76ae1SHans de Goede 		 */
193d3d76ae1SHans de Goede 		cells[0].name = "tps68470-clk";
19443cf3697SDaniel Scally 		cells[0].platform_data = clk_pdata;
19543cf3697SDaniel Scally 		cells[0].pdata_size = struct_size(clk_pdata, consumers, n_consumers);
196d3d76ae1SHans de Goede 		cells[1].name = "tps68470-regulator";
19719d8d6e3SHans de Goede 		cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata;
19819d8d6e3SHans de Goede 		cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data);
199d3d76ae1SHans de Goede 		cells[2].name = "tps68470-gpio";
200d3d76ae1SHans de Goede 
20106a659d1SDaniel Scally 		for (i = 0; i < board_data->n_gpiod_lookups; i++)
20206a659d1SDaniel Scally 			gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
20319d8d6e3SHans de Goede 
204a2f9fbc2SHans de Goede 		ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
205d3d76ae1SHans de Goede 					   cells, TPS68470_WIN_MFD_CELL_COUNT,
206a2f9fbc2SHans de Goede 					   NULL, 0, NULL);
207d3d76ae1SHans de Goede 		kfree(cells);
20819d8d6e3SHans de Goede 
20906a659d1SDaniel Scally 		if (ret) {
21006a659d1SDaniel Scally 			for (i = 0; i < board_data->n_gpiod_lookups; i++)
21106a659d1SDaniel Scally 				gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
21206a659d1SDaniel Scally 		}
21319d8d6e3SHans de Goede 
214a2f9fbc2SHans de Goede 		break;
215a2f9fbc2SHans de Goede 	case DESIGNED_FOR_CHROMEOS:
216a2f9fbc2SHans de Goede 		ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
217a2f9fbc2SHans de Goede 					   tps68470_cros, ARRAY_SIZE(tps68470_cros),
218a2f9fbc2SHans de Goede 					   NULL, 0, NULL);
219a2f9fbc2SHans de Goede 		break;
220a2f9fbc2SHans de Goede 	default:
221a2f9fbc2SHans de Goede 		dev_err(&client->dev, "Failed to add MFD devices\n");
222a2f9fbc2SHans de Goede 		return device_type;
223a2f9fbc2SHans de Goede 	}
224a2f9fbc2SHans de Goede 
22597c2259eSHans de Goede 	/*
22697c2259eSHans de Goede 	 * No acpi_dev_clear_dependencies() here, since the acpi_gpiochip_add()
22797c2259eSHans de Goede 	 * for the GPIO cell already does this.
22897c2259eSHans de Goede 	 */
22997c2259eSHans de Goede 
230a2f9fbc2SHans de Goede 	return ret;
231a2f9fbc2SHans de Goede }
232a2f9fbc2SHans de Goede 
skl_int3472_tps68470_remove(struct i2c_client * client)233ed5c2f5fSUwe Kleine-König static void skl_int3472_tps68470_remove(struct i2c_client *client)
23419d8d6e3SHans de Goede {
23519d8d6e3SHans de Goede 	const struct int3472_tps68470_board_data *board_data;
23606a659d1SDaniel Scally 	int i;
23719d8d6e3SHans de Goede 
23819d8d6e3SHans de Goede 	board_data = int3472_tps68470_get_board_data(dev_name(&client->dev));
23906a659d1SDaniel Scally 	if (board_data) {
24006a659d1SDaniel Scally 		for (i = 0; i < board_data->n_gpiod_lookups; i++)
24106a659d1SDaniel Scally 			gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
24206a659d1SDaniel Scally 	}
24319d8d6e3SHans de Goede }
24419d8d6e3SHans de Goede 
245a2f9fbc2SHans de Goede static const struct acpi_device_id int3472_device_id[] = {
246a2f9fbc2SHans de Goede 	{ "INT3472", 0 },
247a2f9fbc2SHans de Goede 	{ }
248a2f9fbc2SHans de Goede };
249a2f9fbc2SHans de Goede MODULE_DEVICE_TABLE(acpi, int3472_device_id);
250a2f9fbc2SHans de Goede 
251a2f9fbc2SHans de Goede static struct i2c_driver int3472_tps68470 = {
252a2f9fbc2SHans de Goede 	.driver = {
253a2f9fbc2SHans de Goede 		.name = "int3472-tps68470",
254a2f9fbc2SHans de Goede 		.acpi_match_table = int3472_device_id,
255a2f9fbc2SHans de Goede 	},
256aeaee158SUwe Kleine-König 	.probe = skl_int3472_tps68470_probe,
25719d8d6e3SHans de Goede 	.remove = skl_int3472_tps68470_remove,
258a2f9fbc2SHans de Goede };
259a2f9fbc2SHans de Goede module_i2c_driver(int3472_tps68470);
260a2f9fbc2SHans de Goede 
261a2f9fbc2SHans de Goede MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver");
262a2f9fbc2SHans de Goede MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
263a2f9fbc2SHans de Goede MODULE_LICENSE("GPL v2");
26497c2259eSHans de Goede MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator");
265