Loading...
Note: File does not exist in v4.6.
1// SPDX-License-Identifier: GPL-2.0
2/* Author: Dan Scally <djrscally@gmail.com> */
3
4#include <linux/i2c.h>
5#include <linux/mfd/core.h>
6#include <linux/mfd/tps68470.h>
7#include <linux/platform_device.h>
8#include <linux/regmap.h>
9
10#include "intel_skl_int3472_common.h"
11
12#define DESIGNED_FOR_CHROMEOS 1
13#define DESIGNED_FOR_WINDOWS 2
14
15static const struct mfd_cell tps68470_cros[] = {
16 { .name = "tps68470-gpio" },
17 { .name = "tps68470_pmic_opregion" },
18};
19
20static const struct mfd_cell tps68470_win[] = {
21 { .name = "tps68470-gpio" },
22 { .name = "tps68470-clk" },
23 { .name = "tps68470-regulator" },
24};
25
26static const struct regmap_config tps68470_regmap_config = {
27 .reg_bits = 8,
28 .val_bits = 8,
29 .max_register = TPS68470_REG_MAX,
30};
31
32static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
33{
34 unsigned int version;
35 int ret;
36
37 /* Force software reset */
38 ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
39 if (ret)
40 return ret;
41
42 ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
43 if (ret) {
44 dev_err(dev, "Failed to read revision register: %d\n", ret);
45 return ret;
46 }
47
48 dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
49
50 return 0;
51}
52
53/** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
54 * @adev: A pointer to a &struct acpi_device
55 *
56 * Check CLDB buffer against the PMIC's adev. If present, then we check
57 * the value of control_logic_type field and follow one of the
58 * following scenarios:
59 *
60 * 1. No CLDB - likely ACPI tables designed for ChromeOS. We
61 * create platform devices for the GPIOs and OpRegion drivers.
62 *
63 * 2. CLDB, with control_logic_type = 2 - probably ACPI tables
64 * made for Windows 2-in-1 platforms. Register pdevs for GPIO,
65 * Clock and Regulator drivers to bind to.
66 *
67 * 3. Any other value in control_logic_type, we should never have
68 * gotten to this point; fail probe and return.
69 *
70 * Return:
71 * * 1 Device intended for ChromeOS
72 * * 2 Device intended for Windows
73 * * -EINVAL Where @adev has an object named CLDB but it does not conform to
74 * our expectations
75 */
76static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
77{
78 struct int3472_cldb cldb = { 0 };
79 int ret;
80
81 /*
82 * A CLDB buffer that exists, but which does not match our expectations
83 * should trigger an error so we don't blindly continue.
84 */
85 ret = skl_int3472_fill_cldb(adev, &cldb);
86 if (ret && ret != -ENODEV)
87 return ret;
88
89 if (ret)
90 return DESIGNED_FOR_CHROMEOS;
91
92 if (cldb.control_logic_type != 2)
93 return -EINVAL;
94
95 return DESIGNED_FOR_WINDOWS;
96}
97
98int skl_int3472_tps68470_probe(struct i2c_client *client)
99{
100 struct acpi_device *adev = ACPI_COMPANION(&client->dev);
101 struct regmap *regmap;
102 int device_type;
103 int ret;
104
105 regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
106 if (IS_ERR(regmap)) {
107 dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
108 return PTR_ERR(regmap);
109 }
110
111 i2c_set_clientdata(client, regmap);
112
113 ret = tps68470_chip_init(&client->dev, regmap);
114 if (ret < 0) {
115 dev_err(&client->dev, "TPS68470 init error %d\n", ret);
116 return ret;
117 }
118
119 device_type = skl_int3472_tps68470_calc_type(adev);
120 switch (device_type) {
121 case DESIGNED_FOR_WINDOWS:
122 ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
123 tps68470_win, ARRAY_SIZE(tps68470_win),
124 NULL, 0, NULL);
125 break;
126 case DESIGNED_FOR_CHROMEOS:
127 ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
128 tps68470_cros, ARRAY_SIZE(tps68470_cros),
129 NULL, 0, NULL);
130 break;
131 default:
132 dev_err(&client->dev, "Failed to add MFD devices\n");
133 return device_type;
134 }
135
136 return ret;
137}