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