brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 7920a16 Raw
424 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hardware monitoring driver for ZL6100 and compatibles4 *5 * Copyright (c) 2011 Ericsson AB.6 * Copyright (c) 2012 Guenter Roeck7 */8 9#include <linux/bitops.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/init.h>13#include <linux/err.h>14#include <linux/slab.h>15#include <linux/i2c.h>16#include <linux/ktime.h>17#include <linux/delay.h>18#include "pmbus.h"19 20enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,21	     zl8802, zl9101, zl9117, zls1003, zls4009 };22 23struct zl6100_data {24	int id;25	struct pmbus_driver_info info;26};27 28#define to_zl6100_data(x)  container_of(x, struct zl6100_data, info)29 30#define ZL6100_MFR_CONFIG		0xd031#define ZL6100_DEVICE_ID		0xe432 33#define ZL6100_MFR_XTEMP_ENABLE		BIT(7)34 35#define ZL8802_MFR_USER_GLOBAL_CONFIG	0xe936#define ZL8802_MFR_TMON_ENABLE		BIT(12)37#define ZL8802_MFR_USER_CONFIG		0xd138#define ZL8802_MFR_XTEMP_ENABLE_2	BIT(1)39#define ZL8802_MFR_DDC_CONFIG		0xd340#define ZL8802_MFR_PHASES_MASK		0x000741 42#define MFR_VMON_OV_FAULT_LIMIT		0xf543#define MFR_VMON_UV_FAULT_LIMIT		0xf644#define MFR_READ_VMON			0xf745 46#define VMON_UV_WARNING			BIT(5)47#define VMON_OV_WARNING			BIT(4)48#define VMON_UV_FAULT			BIT(1)49#define VMON_OV_FAULT			BIT(0)50 51#define ZL6100_WAIT_TIME		1000	/* uS	*/52 53static ushort delay = ZL6100_WAIT_TIME;54module_param(delay, ushort, 0644);55MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");56 57/* Convert linear sensor value to milli-units */58static long zl6100_l2d(s16 l)59{60	s16 exponent;61	s32 mantissa;62	long val;63 64	exponent = l >> 11;65	mantissa = ((s16)((l & 0x7ff) << 5)) >> 5;66 67	val = mantissa;68 69	/* scale result to milli-units */70	val = val * 1000L;71 72	if (exponent >= 0)73		val <<= exponent;74	else75		val >>= -exponent;76 77	return val;78}79 80#define MAX_MANTISSA	(1023 * 1000)81#define MIN_MANTISSA	(511 * 1000)82 83static u16 zl6100_d2l(long val)84{85	s16 exponent = 0, mantissa;86	bool negative = false;87 88	/* simple case */89	if (val == 0)90		return 0;91 92	if (val < 0) {93		negative = true;94		val = -val;95	}96 97	/* Reduce large mantissa until it fits into 10 bit */98	while (val >= MAX_MANTISSA && exponent < 15) {99		exponent++;100		val >>= 1;101	}102	/* Increase small mantissa to improve precision */103	while (val < MIN_MANTISSA && exponent > -15) {104		exponent--;105		val <<= 1;106	}107 108	/* Convert mantissa from milli-units to units */109	mantissa = DIV_ROUND_CLOSEST(val, 1000);110 111	/* Ensure that resulting number is within range */112	if (mantissa > 0x3ff)113		mantissa = 0x3ff;114 115	/* restore sign */116	if (negative)117		mantissa = -mantissa;118 119	/* Convert to 5 bit exponent, 11 bit mantissa */120	return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);121}122 123static int zl6100_read_word_data(struct i2c_client *client, int page,124				 int phase, int reg)125{126	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);127	struct zl6100_data *data = to_zl6100_data(info);128	int ret, vreg;129 130	if (page >= info->pages)131		return -ENXIO;132 133	if (data->id == zl2005) {134		/*135		 * Limit register detection is not reliable on ZL2005.136		 * Make sure registers are not erroneously detected.137		 */138		switch (reg) {139		case PMBUS_VOUT_OV_WARN_LIMIT:140		case PMBUS_VOUT_UV_WARN_LIMIT:141		case PMBUS_IOUT_OC_WARN_LIMIT:142			return -ENXIO;143		}144	}145 146	switch (reg) {147	case PMBUS_VIRT_READ_VMON:148		vreg = MFR_READ_VMON;149		break;150	case PMBUS_VIRT_VMON_OV_WARN_LIMIT:151	case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:152		vreg = MFR_VMON_OV_FAULT_LIMIT;153		break;154	case PMBUS_VIRT_VMON_UV_WARN_LIMIT:155	case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:156		vreg = MFR_VMON_UV_FAULT_LIMIT;157		break;158	default:159		if (reg >= PMBUS_VIRT_BASE)160			return -ENXIO;161		vreg = reg;162		break;163	}164 165	ret = pmbus_read_word_data(client, page, phase, vreg);166	if (ret < 0)167		return ret;168 169	switch (reg) {170	case PMBUS_VIRT_VMON_OV_WARN_LIMIT:171		ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10));172		break;173	case PMBUS_VIRT_VMON_UV_WARN_LIMIT:174		ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10));175		break;176	}177 178	return ret;179}180 181static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)182{183	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);184	int ret, status;185 186	if (page >= info->pages)187		return -ENXIO;188 189	switch (reg) {190	case PMBUS_VIRT_STATUS_VMON:191		ret = pmbus_read_byte_data(client, 0,192					   PMBUS_STATUS_MFR_SPECIFIC);193		if (ret < 0)194			break;195 196		status = 0;197		if (ret & VMON_UV_WARNING)198			status |= PB_VOLTAGE_UV_WARNING;199		if (ret & VMON_OV_WARNING)200			status |= PB_VOLTAGE_OV_WARNING;201		if (ret & VMON_UV_FAULT)202			status |= PB_VOLTAGE_UV_FAULT;203		if (ret & VMON_OV_FAULT)204			status |= PB_VOLTAGE_OV_FAULT;205		ret = status;206		break;207	default:208		ret = pmbus_read_byte_data(client, page, reg);209		break;210	}211 212	return ret;213}214 215static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,216				  u16 word)217{218	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);219	int vreg;220 221	if (page >= info->pages)222		return -ENXIO;223 224	switch (reg) {225	case PMBUS_VIRT_VMON_OV_WARN_LIMIT:226		word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9));227		vreg = MFR_VMON_OV_FAULT_LIMIT;228		pmbus_clear_cache(client);229		break;230	case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:231		vreg = MFR_VMON_OV_FAULT_LIMIT;232		pmbus_clear_cache(client);233		break;234	case PMBUS_VIRT_VMON_UV_WARN_LIMIT:235		word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11));236		vreg = MFR_VMON_UV_FAULT_LIMIT;237		pmbus_clear_cache(client);238		break;239	case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:240		vreg = MFR_VMON_UV_FAULT_LIMIT;241		pmbus_clear_cache(client);242		break;243	default:244		if (reg >= PMBUS_VIRT_BASE)245			return -ENXIO;246		vreg = reg;247	}248 249	return pmbus_write_word_data(client, page, vreg, word);250}251 252static const struct i2c_device_id zl6100_id[] = {253	{"bmr450", zl2005},254	{"bmr451", zl2005},255	{"bmr462", zl2008},256	{"bmr463", zl2008},257	{"bmr464", zl2008},258	{"bmr465", zls4009},259	{"bmr466", zls1003},260	{"bmr467", zls4009},261	{"bmr469", zl8802},262	{"zl2004", zl2004},263	{"zl2005", zl2005},264	{"zl2006", zl2006},265	{"zl2008", zl2008},266	{"zl2105", zl2105},267	{"zl2106", zl2106},268	{"zl6100", zl6100},269	{"zl6105", zl6105},270	{"zl8802", zl8802},271	{"zl9101", zl9101},272	{"zl9117", zl9117},273	{"zls1003", zls1003},274	{"zls4009", zls4009},275	{ }276};277MODULE_DEVICE_TABLE(i2c, zl6100_id);278 279static int zl6100_probe(struct i2c_client *client)280{281	int ret, i;282	struct zl6100_data *data;283	struct pmbus_driver_info *info;284	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];285	const struct i2c_device_id *mid;286 287	if (!i2c_check_functionality(client->adapter,288				     I2C_FUNC_SMBUS_READ_WORD_DATA289				     | I2C_FUNC_SMBUS_READ_BLOCK_DATA))290		return -ENODEV;291 292	ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,293					device_id);294	if (ret < 0) {295		dev_err(&client->dev, "Failed to read device ID\n");296		return ret;297	}298	device_id[ret] = '\0';299	dev_info(&client->dev, "Device ID %s\n", device_id);300 301	mid = NULL;302	for (mid = zl6100_id; mid->name[0]; mid++) {303		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))304			break;305	}306	if (!mid->name[0]) {307		dev_err(&client->dev, "Unsupported device\n");308		return -ENODEV;309	}310	if (strcmp(client->name, mid->name) != 0)311		dev_notice(&client->dev,312			   "Device mismatch: Configured %s, detected %s\n",313			   client->name, mid->name);314 315	data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),316			    GFP_KERNEL);317	if (!data)318		return -ENOMEM;319 320	data->id = mid->driver_data;321 322	/*323	 * According to information from the chip vendor, all currently324	 * supported chips are known to require a wait time between I2C325	 * accesses.326	 */327	udelay(delay);328 329	info = &data->info;330 331	info->pages = 1;332	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT333	  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT334	  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT335	  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;336 337	/*338	 * ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring339	 * an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009,340	 * VDRV for ZL9101M and ZL9117M). Report it as vmon.341	 */342	if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 ||343	    data->id == zl9117 || data->id == zls4009)344		info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;345 346	/*347	 * ZL8802 has two outputs that can be used either independently or in348	 * a current sharing configuration. The driver uses the DDC_CONFIG349	 * register to check if the module is running with independent or350	 * shared outputs. If the module is in shared output mode, only one351	 * output voltage will be reported.352	 */353	if (data->id == zl8802) {354		info->pages = 2;355		info->func[0] |= PMBUS_HAVE_IIN;356 357		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG);358		if (ret < 0)359			return ret;360 361		udelay(delay);362 363		if (ret & ZL8802_MFR_PHASES_MASK)364			info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;365		else366			info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT367				| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;368 369		for (i = 0; i < 2; i++) {370			ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);371			if (ret < 0)372				return ret;373 374			udelay(delay);375 376			ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);377			if (ret < 0)378				return ret;379 380			if (ret & ZL8802_MFR_XTEMP_ENABLE_2)381				info->func[i] |= PMBUS_HAVE_TEMP2;382 383			udelay(delay);384		}385		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);386		if (ret < 0)387			return ret;388 389		if (ret & ZL8802_MFR_TMON_ENABLE)390			info->func[0] |= PMBUS_HAVE_TEMP3;391	} else {392		ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);393		if (ret < 0)394			return ret;395 396		if (ret & ZL6100_MFR_XTEMP_ENABLE)397			info->func[0] |= PMBUS_HAVE_TEMP2;398	}399 400	udelay(delay);401 402	info->access_delay = delay;403	info->read_word_data = zl6100_read_word_data;404	info->read_byte_data = zl6100_read_byte_data;405	info->write_word_data = zl6100_write_word_data;406 407	return pmbus_do_probe(client, info);408}409 410static struct i2c_driver zl6100_driver = {411	.driver = {412		   .name = "zl6100",413		   },414	.probe = zl6100_probe,415	.id_table = zl6100_id,416};417 418module_i2c_driver(zl6100_driver);419 420MODULE_AUTHOR("Guenter Roeck");421MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");422MODULE_LICENSE("GPL");423MODULE_IMPORT_NS(PMBUS);424