brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 31d9ae0 Raw
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