xref: /linux/drivers/hwmon/pmbus/tda38640.c (revision cdd30ebb1b9f36159d66f088b61aee264e649d7a)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Hardware monitoring driver for Infineon TDA38640
4  *
5  * Copyright (c) 2023 9elements GmbH
6  *
7  */
8 
9 #include <linux/err.h>
10 #include <linux/i2c.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/regulator/driver.h>
15 #include "pmbus.h"
16 
17 static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
18 	PMBUS_REGULATOR_ONE("vout"),
19 };
20 
21 struct tda38640_data {
22 	struct pmbus_driver_info info;
23 	u32 en_pin_lvl;
24 };
25 
26 #define to_tda38640_data(x)  container_of(x, struct tda38640_data, info)
27 
28 /*
29  * Map PB_ON_OFF_CONFIG_POLARITY_HIGH to PB_OPERATION_CONTROL_ON.
30  */
tda38640_read_byte_data(struct i2c_client * client,int page,int reg)31 static int tda38640_read_byte_data(struct i2c_client *client, int page, int reg)
32 {
33 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
34 	struct tda38640_data *data = to_tda38640_data(info);
35 	int ret, on_off_config, enabled;
36 
37 	if (reg != PMBUS_OPERATION)
38 		return -ENODATA;
39 
40 	ret = pmbus_read_byte_data(client, page, reg);
41 	if (ret < 0)
42 		return ret;
43 
44 	on_off_config = pmbus_read_byte_data(client, page,
45 					     PMBUS_ON_OFF_CONFIG);
46 	if (on_off_config < 0)
47 		return on_off_config;
48 
49 	enabled = !!(on_off_config & PB_ON_OFF_CONFIG_POLARITY_HIGH);
50 
51 	enabled ^= data->en_pin_lvl;
52 	if (enabled)
53 		ret &= ~PB_OPERATION_CONTROL_ON;
54 	else
55 		ret |= PB_OPERATION_CONTROL_ON;
56 
57 	return ret;
58 }
59 
60 /*
61  * Map PB_OPERATION_CONTROL_ON to PB_ON_OFF_CONFIG_POLARITY_HIGH.
62  */
tda38640_write_byte_data(struct i2c_client * client,int page,int reg,u8 byte)63 static int tda38640_write_byte_data(struct i2c_client *client, int page,
64 				    int reg, u8 byte)
65 {
66 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
67 	struct tda38640_data *data = to_tda38640_data(info);
68 	int enable, ret;
69 
70 	if (reg != PMBUS_OPERATION)
71 		return -ENODATA;
72 
73 	enable = !!(byte & PB_OPERATION_CONTROL_ON);
74 
75 	byte &= ~PB_OPERATION_CONTROL_ON;
76 	ret = pmbus_write_byte_data(client, page, reg, byte);
77 	if (ret < 0)
78 		return ret;
79 
80 	enable ^= data->en_pin_lvl;
81 
82 	return pmbus_update_byte_data(client, page, PMBUS_ON_OFF_CONFIG,
83 				      PB_ON_OFF_CONFIG_POLARITY_HIGH,
84 				      enable ? 0 : PB_ON_OFF_CONFIG_POLARITY_HIGH);
85 }
86 
svid_mode(struct i2c_client * client,struct tda38640_data * data)87 static int svid_mode(struct i2c_client *client, struct tda38640_data *data)
88 {
89 	/* PMBUS_MFR_READ(0xD0) + MTP Address offset */
90 	u8 write_buf[] = {0xd0, 0x44, 0x00};
91 	u8 read_buf[2];
92 	int ret, svid;
93 	bool off, reg_en_pin_pol;
94 
95 	struct i2c_msg msgs[2] = {
96 		{
97 			.addr = client->addr,
98 			.flags = 0,
99 			.buf = write_buf,
100 			.len = sizeof(write_buf),
101 		},
102 		{
103 			.addr = client->addr,
104 			.flags = I2C_M_RD,
105 			.buf = read_buf,
106 			.len = sizeof(read_buf),
107 		}
108 	};
109 
110 	ret = i2c_transfer(client->adapter, msgs, 2);
111 	if (ret < 0) {
112 		dev_err(&client->dev, "i2c_transfer failed. %d", ret);
113 		return ret;
114 	}
115 
116 	/*
117 	 * 0x44[15] determines PMBus Operating Mode
118 	 * If bit is set then it is SVID mode.
119 	 */
120 	svid = !!(read_buf[1] & BIT(7));
121 
122 	/*
123 	 * Determine EN pin level for use in SVID mode.
124 	 * This is done with help of STATUS_BYTE bit 6(OFF) & ON_OFF_CONFIG bit 2(EN pin polarity).
125 	 */
126 	if (svid) {
127 		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
128 		if (ret < 0)
129 			return ret;
130 		off = !!(ret & PB_STATUS_OFF);
131 
132 		ret = i2c_smbus_read_byte_data(client, PMBUS_ON_OFF_CONFIG);
133 		if (ret < 0)
134 			return ret;
135 		reg_en_pin_pol = !!(ret & PB_ON_OFF_CONFIG_POLARITY_HIGH);
136 		data->en_pin_lvl = off ^ reg_en_pin_pol;
137 	}
138 
139 	return svid;
140 }
141 
142 static struct pmbus_driver_info tda38640_info = {
143 	.pages = 1,
144 	.format[PSC_VOLTAGE_IN] = linear,
145 	.format[PSC_VOLTAGE_OUT] = linear,
146 	.format[PSC_CURRENT_OUT] = linear,
147 	.format[PSC_CURRENT_IN] = linear,
148 	.format[PSC_POWER] = linear,
149 	.format[PSC_TEMPERATURE] = linear,
150 	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
151 	    | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
152 	    | PMBUS_HAVE_IIN
153 	    | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
154 	    | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
155 	    | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN,
156 #if IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR)
157 	.num_regulators = 1,
158 	.reg_desc = tda38640_reg_desc,
159 #endif
160 };
161 
tda38640_probe(struct i2c_client * client)162 static int tda38640_probe(struct i2c_client *client)
163 {
164 	struct tda38640_data *data;
165 	int svid;
166 
167 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
168 	if (!data)
169 		return -ENOMEM;
170 	memcpy(&data->info, &tda38640_info, sizeof(tda38640_info));
171 
172 	if (IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) &&
173 	    of_property_read_bool(client->dev.of_node, "infineon,en-pin-fixed-level")) {
174 		svid = svid_mode(client, data);
175 		if (svid < 0) {
176 			dev_err_probe(&client->dev, svid, "Could not determine operating mode.");
177 			return svid;
178 		}
179 
180 		/*
181 		 * Apply ON_OFF_CONFIG workaround as enabling the regulator using the
182 		 * OPERATION register doesn't work in SVID mode.
183 		 *
184 		 * One should configure PMBUS_ON_OFF_CONFIG here, but
185 		 * PB_ON_OFF_CONFIG_POWERUP_CONTROL and PB_ON_OFF_CONFIG_EN_PIN_REQ
186 		 * are ignored by the device.
187 		 * Only PB_ON_OFF_CONFIG_POLARITY_HIGH has an effect.
188 		 */
189 		if (svid) {
190 			data->info.read_byte_data = tda38640_read_byte_data;
191 			data->info.write_byte_data = tda38640_write_byte_data;
192 		}
193 	}
194 	return pmbus_do_probe(client, &data->info);
195 }
196 
197 static const struct i2c_device_id tda38640_id[] = {
198 	{"tda38640"},
199 	{}
200 };
201 MODULE_DEVICE_TABLE(i2c, tda38640_id);
202 
203 static const struct of_device_id __maybe_unused tda38640_of_match[] = {
204 	{ .compatible = "infineon,tda38640"},
205 	{ },
206 };
207 MODULE_DEVICE_TABLE(of, tda38640_of_match);
208 
209 /* This is the driver that will be inserted */
210 static struct i2c_driver tda38640_driver = {
211 	.driver = {
212 		.name = "tda38640",
213 		.of_match_table = of_match_ptr(tda38640_of_match),
214 	},
215 	.probe = tda38640_probe,
216 	.id_table = tda38640_id,
217 };
218 
219 module_i2c_driver(tda38640_driver);
220 
221 MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>");
222 MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640");
223 MODULE_LICENSE("GPL");
224 MODULE_IMPORT_NS("PMBUS");
225