1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820
5 * Copyright (c) 2021 Flextronics International Sweden AB
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>
17 enum chips
{ pim4006
, pim4328
, pim4820
};
21 struct pmbus_driver_info info
;
24 #define to_pim4328_data(x) container_of(x, struct pim4328_data, info)
26 /* PIM4006 and PIM4328 */
27 #define PIM4328_MFR_READ_VINA 0xd3
28 #define PIM4328_MFR_READ_VINB 0xd4
31 #define PIM4328_MFR_READ_IINA 0xd6
32 #define PIM4328_MFR_READ_IINB 0xd7
33 #define PIM4328_MFR_FET_CHECKSTATUS 0xd9
36 #define PIM4328_MFR_STATUS_BITS 0xd5
39 #define PIM4328_MFR_READ_STATUS 0xd0
41 static const struct i2c_device_id pim4328_id
[] = {
52 MODULE_DEVICE_TABLE(i2c
, pim4328_id
);
54 static int pim4328_read_word_data(struct i2c_client
*client
, int page
,
67 ret
= pmbus_read_word_data(client
, page
, phase
,
68 phase
== 0 ? PIM4328_MFR_READ_VINA
69 : PIM4328_MFR_READ_VINB
);
72 ret
= pmbus_read_word_data(client
, page
, phase
,
73 phase
== 0 ? PIM4328_MFR_READ_IINA
74 : PIM4328_MFR_READ_IINB
);
83 static int pim4328_read_byte_data(struct i2c_client
*client
, int page
, int reg
)
85 const struct pmbus_driver_info
*info
= pmbus_get_driver_info(client
);
86 struct pim4328_data
*data
= to_pim4328_data(info
);
93 case PMBUS_STATUS_BYTE
:
94 ret
= pmbus_read_byte_data(client
, page
, PMBUS_STATUS_BYTE
);
97 if (data
->id
== pim4006
) {
98 status
= pmbus_read_word_data(client
, page
, 0xff,
99 PIM4328_MFR_FET_CHECKSTATUS
);
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
);
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
);
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
;
133 static int pim4328_probe(struct i2c_client
*client
)
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
;
143 if (!i2c_check_functionality(client
->adapter
,
144 I2C_FUNC_SMBUS_READ_BYTE_DATA
145 | I2C_FUNC_SMBUS_BLOCK_DATA
))
148 data
= devm_kzalloc(&client
->dev
, sizeof(struct pim4328_data
),
153 status
= i2c_smbus_read_block_data(client
, PMBUS_MFR_MODEL
, device_id
);
155 dev_err(&client
->dev
, "Failed to read Manufacturer Model\n");
158 for (mid
= pim4328_id
; mid
->name
[0]; mid
++) {
159 if (!strncasecmp(mid
->name
, device_id
, strlen(mid
->name
)))
163 dev_err(&client
->dev
, "Unsupported device\n");
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
);
172 data
->id
= mid
->driver_data
;
175 info
->read_byte_data
= pim4328_read_byte_data
;
176 info
->read_word_data
= pim4328_read_word_data
;
178 pdata
= devm_kzalloc(dev
, sizeof(struct pmbus_platform_data
),
182 dev
->platform_data
= pdata
;
183 pdata
->flags
= PMBUS_NO_CAPABILITY
| PMBUS_NO_WRITE_PROTECT
;
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
;
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
;
206 info
->func
[0] = PMBUS_HAVE_VIN
| PMBUS_HAVE_TEMP
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
;
217 return pmbus_do_probe(client
, info
);
220 static struct i2c_driver pim4328_driver
= {
224 .probe
= pim4328_probe
,
225 .id_table
= pim4328_id
,
228 module_i2c_driver(pim4328_driver
);
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");