GNU Linux-libre 5.15.54-gnu
[releases.git] / drivers / hwmon / pmbus / pim4328.c
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
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
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
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);