234 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hardware monitoring driver for PIM4006, PIM4328 and PIM48204 *5 * Copyright (c) 2021 Flextronics International Sweden AB6 */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 17enum chips { pim4006, pim4328, pim4820 };18 19struct 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 0xd328#define PIM4328_MFR_READ_VINB 0xd429 30/* PIM4006 */31#define PIM4328_MFR_READ_IINA 0xd632#define PIM4328_MFR_READ_IINB 0xd733#define PIM4328_MFR_FET_CHECKSTATUS 0xd934 35/* PIM4328 */36#define PIM4328_MFR_STATUS_BITS 0xd537 38/* PIM4820 */39#define PIM4328_MFR_READ_STATUS 0xd040 41static 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};52MODULE_DEVICE_TABLE(i2c, pim4328_id);53 54static 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_VINA69 : 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_IINA74 : PIM4328_MFR_READ_IINB);75 break;76 default:77 ret = -ENODATA;78 }79 80 return ret;81}82 83static 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 133static 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_DATA145 | 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_VIN189 | 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_VIRTUAL196 | PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN197 | 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_TEMP207 | 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 220static struct i2c_driver pim4328_driver = {221 .driver = {222 .name = "pim4328",223 },224 .probe = pim4328_probe,225 .id_table = pim4328_id,226};227 228module_i2c_driver(pim4328_driver);229 230MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");231MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");232MODULE_LICENSE("GPL");233MODULE_IMPORT_NS(PMBUS);234