1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820
4  *
5  * Copyright (c) 2021 Flextronics International Sweden AB
6  */
7 
8 #include <linux/err.h>
9 #include <linux/i2c.h>
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/pmbus.h>
14 #include <linux/slab.h>
15 #include "pmbus.h"
16 
17 enum chips { pim4006, pim4328, pim4820 };
18 
19 struct pim4328_data {
20 	enum chips id;
21 	struct pmbus_driver_info info;
22 };
23 
24 #define to_pim4328_data(x)  container_of(x, struct pim4328_data, info)
25 
26 /* PIM4006 and PIM4328 */
27 #define PIM4328_MFR_READ_VINA		0xd3
28 #define PIM4328_MFR_READ_VINB		0xd4
29 
30 /* PIM4006 */
31 #define PIM4328_MFR_READ_IINA		0xd6
32 #define PIM4328_MFR_READ_IINB		0xd7
33 #define PIM4328_MFR_FET_CHECKSTATUS	0xd9
34 
35 /* PIM4328 */
36 #define PIM4328_MFR_STATUS_BITS		0xd5
37 
38 /* PIM4820 */
39 #define PIM4328_MFR_READ_STATUS		0xd0
40 
41 static const struct i2c_device_id pim4328_id[] = {
42 	{"bmr455", pim4328},
43 	{"pim4006", pim4006},
44 	{"pim4106", pim4006},
45 	{"pim4206", pim4006},
46 	{"pim4306", pim4006},
47 	{"pim4328", pim4328},
48 	{"pim4406", pim4006},
49 	{"pim4820", pim4820},
50 	{}
51 };
52 MODULE_DEVICE_TABLE(i2c, pim4328_id);
53 
pim4328_read_word_data(struct i2c_client * client,int page,int phase,int reg)54 static int pim4328_read_word_data(struct i2c_client *client, int page,
55 				  int phase, int reg)
56 {
57 	int ret;
58 
59 	if (page > 0)
60 		return -ENXIO;
61 
62 	if (phase == 0xff)
63 		return -ENODATA;
64 
65 	switch (reg) {
66 	case PMBUS_READ_VIN:
67 		ret = pmbus_read_word_data(client, page, phase,
68 					   phase == 0 ? PIM4328_MFR_READ_VINA
69 						      : PIM4328_MFR_READ_VINB);
70 		break;
71 	case PMBUS_READ_IIN:
72 		ret = pmbus_read_word_data(client, page, phase,
73 					   phase == 0 ? PIM4328_MFR_READ_IINA
74 						      : PIM4328_MFR_READ_IINB);
75 		break;
76 	default:
77 		ret = -ENODATA;
78 	}
79 
80 	return ret;
81 }
82 
pim4328_read_byte_data(struct i2c_client * client,int page,int reg)83 static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg)
84 {
85 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
86 	struct pim4328_data *data = to_pim4328_data(info);
87 	int ret, status;
88 
89 	if (page > 0)
90 		return -ENXIO;
91 
92 	switch (reg) {
93 	case PMBUS_STATUS_BYTE:
94 		ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
95 		if (ret < 0)
96 			return ret;
97 		if (data->id == pim4006) {
98 			status = pmbus_read_word_data(client, page, 0xff,
99 						      PIM4328_MFR_FET_CHECKSTATUS);
100 			if (status < 0)
101 				return status;
102 			if (status & 0x0630) /* Input UV */
103 				ret |= PB_STATUS_VIN_UV;
104 		} else if (data->id == pim4328) {
105 			status = pmbus_read_byte_data(client, page,
106 						      PIM4328_MFR_STATUS_BITS);
107 			if (status < 0)
108 				return status;
109 			if (status & 0x04) /* Input UV */
110 				ret |= PB_STATUS_VIN_UV;
111 			if (status & 0x40) /* Output UV */
112 				ret |= PB_STATUS_NONE_ABOVE;
113 		} else if (data->id == pim4820) {
114 			status = pmbus_read_byte_data(client, page,
115 						      PIM4328_MFR_READ_STATUS);
116 			if (status < 0)
117 				return status;
118 			if (status & 0x05) /* Input OV or OC */
119 				ret |= PB_STATUS_NONE_ABOVE;
120 			if (status & 0x1a) /* Input UV */
121 				ret |= PB_STATUS_VIN_UV;
122 			if (status & 0x40) /* OT */
123 				ret |= PB_STATUS_TEMPERATURE;
124 		}
125 		break;
126 	default:
127 		ret = -ENODATA;
128 	}
129 
130 	return ret;
131 }
132 
pim4328_probe(struct i2c_client * client)133 static int pim4328_probe(struct i2c_client *client)
134 {
135 	int status;
136 	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
137 	const struct i2c_device_id *mid;
138 	struct pim4328_data *data;
139 	struct pmbus_driver_info *info;
140 	struct pmbus_platform_data *pdata;
141 	struct device *dev = &client->dev;
142 
143 	if (!i2c_check_functionality(client->adapter,
144 				     I2C_FUNC_SMBUS_READ_BYTE_DATA
145 				     | I2C_FUNC_SMBUS_BLOCK_DATA))
146 		return -ENODEV;
147 
148 	data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data),
149 			    GFP_KERNEL);
150 	if (!data)
151 		return -ENOMEM;
152 
153 	status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
154 	if (status < 0) {
155 		dev_err(&client->dev, "Failed to read Manufacturer Model\n");
156 		return status;
157 	}
158 	for (mid = pim4328_id; mid->name[0]; mid++) {
159 		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
160 			break;
161 	}
162 	if (!mid->name[0]) {
163 		dev_err(&client->dev, "Unsupported device\n");
164 		return -ENODEV;
165 	}
166 
167 	if (strcmp(client->name, mid->name))
168 		dev_notice(&client->dev,
169 			   "Device mismatch: Configured %s, detected %s\n",
170 			   client->name, mid->name);
171 
172 	data->id = mid->driver_data;
173 	info = &data->info;
174 	info->pages = 1;
175 	info->read_byte_data = pim4328_read_byte_data;
176 	info->read_word_data = pim4328_read_word_data;
177 
178 	pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
179 			     GFP_KERNEL);
180 	if (!pdata)
181 		return -ENOMEM;
182 	dev->platform_data = pdata;
183 	pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT;
184 
185 	switch (data->id) {
186 	case pim4006:
187 		info->phases[0] = 2;
188 		info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN
189 			| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
190 		info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
191 		info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
192 		break;
193 	case pim4328:
194 		info->phases[0] = 2;
195 		info->func[0] = PMBUS_PHASE_VIRTUAL
196 			| PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN
197 			| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
198 		info->pfunc[0] = PMBUS_HAVE_VIN;
199 		info->pfunc[1] = PMBUS_HAVE_VIN;
200 		info->format[PSC_VOLTAGE_IN] = direct;
201 		info->format[PSC_TEMPERATURE] = direct;
202 		info->format[PSC_CURRENT_OUT] = direct;
203 		pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
204 		break;
205 	case pim4820:
206 		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP
207 			| PMBUS_HAVE_IIN;
208 		info->format[PSC_VOLTAGE_IN] = direct;
209 		info->format[PSC_TEMPERATURE] = direct;
210 		info->format[PSC_CURRENT_IN] = direct;
211 		pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
212 		break;
213 	default:
214 		return -ENODEV;
215 	}
216 
217 	return pmbus_do_probe(client, info);
218 }
219 
220 static struct i2c_driver pim4328_driver = {
221 	.driver = {
222 		   .name = "pim4328",
223 		   },
224 	.probe_new = pim4328_probe,
225 	.id_table = pim4328_id,
226 };
227 
228 module_i2c_driver(pim4328_driver);
229 
230 MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
231 MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");
232 MODULE_LICENSE("GPL");
233 MODULE_IMPORT_NS(PMBUS);
234