brintos

brintos / linux-shallow public Read only

0
0
Text · 17.1 KiB · fcbce5a Raw
704 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Jedec 5118 compliant temperature sensors4 *5 * Derived from https://github.com/Steve-Tech/SPD5118-DKMS6 * Originally from T/2 driver at https://t2sde.org/packages/linux7 *	Copyright (c) 2023 René Rebe, ExactCODE GmbH; Germany.8 *9 * Copyright (c) 2024 Guenter Roeck10 *11 * Inspired by ee1004.c and jc42.c.12 *13 * SPD5118 compliant temperature sensors are typically used on DDR514 * memory modules.15 */16 17#include <linux/bitops.h>18#include <linux/bits.h>19#include <linux/err.h>20#include <linux/i2c.h>21#include <linux/hwmon.h>22#include <linux/module.h>23#include <linux/mutex.h>24#include <linux/nvmem-provider.h>25#include <linux/pm.h>26#include <linux/regmap.h>27#include <linux/units.h>28 29/* Addresses to scan */30static const unsigned short normal_i2c[] = {31	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END };32 33/* SPD5118 registers. */34#define SPD5118_REG_TYPE		0x00	/* MR0:MR1 */35#define SPD5118_REG_REVISION		0x02	/* MR2 */36#define SPD5118_REG_VENDOR		0x03	/* MR3:MR4 */37#define SPD5118_REG_CAPABILITY		0x05	/* MR5 */38#define SPD5118_REG_I2C_LEGACY_MODE	0x0B	/* MR11 */39#define SPD5118_REG_TEMP_CLR		0x13	/* MR19 */40#define SPD5118_REG_ERROR_CLR		0x14	/* MR20 */41#define SPD5118_REG_TEMP_CONFIG		0x1A	/* MR26 */42#define SPD5118_REG_TEMP_MAX		0x1c	/* MR28:MR29 */43#define SPD5118_REG_TEMP_MIN		0x1e	/* MR30:MR31 */44#define SPD5118_REG_TEMP_CRIT		0x20	/* MR32:MR33 */45#define SPD5118_REG_TEMP_LCRIT		0x22	/* MR34:MR35 */46#define SPD5118_REG_TEMP		0x31	/* MR49:MR50 */47#define SPD5118_REG_TEMP_STATUS		0x33	/* MR51 */48 49#define SPD5118_TEMP_STATUS_HIGH	BIT(0)50#define SPD5118_TEMP_STATUS_LOW		BIT(1)51#define SPD5118_TEMP_STATUS_CRIT	BIT(2)52#define SPD5118_TEMP_STATUS_LCRIT	BIT(3)53 54#define SPD5118_CAP_TS_SUPPORT		BIT(1)	/* temperature sensor support */55 56#define SPD5118_TS_DISABLE		BIT(0)	/* temperature sensor disable */57 58#define SPD5118_LEGACY_MODE_ADDR	BIT(3)59#define SPD5118_LEGACY_PAGE_MASK	GENMASK(2, 0)60#define SPD5118_LEGACY_MODE_MASK	(SPD5118_LEGACY_MODE_ADDR | SPD5118_LEGACY_PAGE_MASK)61 62#define SPD5118_NUM_PAGES		863#define SPD5118_PAGE_SIZE		12864#define SPD5118_PAGE_SHIFT		765#define SPD5118_PAGE_MASK		GENMASK(6, 0)66#define SPD5118_EEPROM_BASE		0x8067#define SPD5118_EEPROM_SIZE		(SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)68 69/* Temperature unit in millicelsius */70#define SPD5118_TEMP_UNIT		(MILLIDEGREE_PER_DEGREE / 4)71/* Representable temperature range in millicelsius */72#define SPD5118_TEMP_RANGE_MIN		-25600073#define SPD5118_TEMP_RANGE_MAX		25575074 75struct spd5118_data {76	struct regmap *regmap;77	struct mutex nvmem_lock;78};79 80/* hwmon */81 82static int spd5118_temp_from_reg(u16 reg)83{84	int temp = sign_extend32(reg >> 2, 10);85 86	return temp * SPD5118_TEMP_UNIT;87}88 89static u16 spd5118_temp_to_reg(long temp)90{91	temp = clamp_val(temp, SPD5118_TEMP_RANGE_MIN, SPD5118_TEMP_RANGE_MAX);92	return (DIV_ROUND_CLOSEST(temp, SPD5118_TEMP_UNIT) & 0x7ff) << 2;93}94 95static int spd5118_read_temp(struct regmap *regmap, u32 attr, long *val)96{97	int reg, err;98	u8 regval[2];99	u16 temp;100 101	switch (attr) {102	case hwmon_temp_input:103		reg = SPD5118_REG_TEMP;104		break;105	case hwmon_temp_max:106		reg = SPD5118_REG_TEMP_MAX;107		break;108	case hwmon_temp_min:109		reg = SPD5118_REG_TEMP_MIN;110		break;111	case hwmon_temp_crit:112		reg = SPD5118_REG_TEMP_CRIT;113		break;114	case hwmon_temp_lcrit:115		reg = SPD5118_REG_TEMP_LCRIT;116		break;117	default:118		return -EOPNOTSUPP;119	}120 121	err = regmap_bulk_read(regmap, reg, regval, 2);122	if (err)123		return err;124 125	temp = (regval[1] << 8) | regval[0];126 127	*val = spd5118_temp_from_reg(temp);128	return 0;129}130 131static int spd5118_read_alarm(struct regmap *regmap, u32 attr, long *val)132{133	unsigned int mask, regval;134	int err;135 136	switch (attr) {137	case hwmon_temp_max_alarm:138		mask = SPD5118_TEMP_STATUS_HIGH;139		break;140	case hwmon_temp_min_alarm:141		mask = SPD5118_TEMP_STATUS_LOW;142		break;143	case hwmon_temp_crit_alarm:144		mask = SPD5118_TEMP_STATUS_CRIT;145		break;146	case hwmon_temp_lcrit_alarm:147		mask = SPD5118_TEMP_STATUS_LCRIT;148		break;149	default:150		return -EOPNOTSUPP;151	}152 153	err = regmap_read(regmap, SPD5118_REG_TEMP_STATUS, &regval);154	if (err < 0)155		return err;156	*val = !!(regval & mask);157	if (*val)158		return regmap_write(regmap, SPD5118_REG_TEMP_CLR, mask);159	return 0;160}161 162static int spd5118_read_enable(struct regmap *regmap, long *val)163{164	u32 regval;165	int err;166 167	err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);168	if (err < 0)169		return err;170	*val = !(regval & SPD5118_TS_DISABLE);171	return 0;172}173 174static int spd5118_read(struct device *dev, enum hwmon_sensor_types type,175			u32 attr, int channel, long *val)176{177	struct regmap *regmap = dev_get_drvdata(dev);178 179	if (type != hwmon_temp)180		return -EOPNOTSUPP;181 182	switch (attr) {183	case hwmon_temp_input:184	case hwmon_temp_max:185	case hwmon_temp_min:186	case hwmon_temp_crit:187	case hwmon_temp_lcrit:188		return spd5118_read_temp(regmap, attr, val);189	case hwmon_temp_max_alarm:190	case hwmon_temp_min_alarm:191	case hwmon_temp_crit_alarm:192	case hwmon_temp_lcrit_alarm:193		return spd5118_read_alarm(regmap, attr, val);194	case hwmon_temp_enable:195		return spd5118_read_enable(regmap, val);196	default:197		return -EOPNOTSUPP;198	}199}200 201static int spd5118_write_temp(struct regmap *regmap, u32 attr, long val)202{203	u8 regval[2];204	u16 temp;205	int reg;206 207	switch (attr) {208	case hwmon_temp_max:209		reg = SPD5118_REG_TEMP_MAX;210		break;211	case hwmon_temp_min:212		reg = SPD5118_REG_TEMP_MIN;213		break;214	case hwmon_temp_crit:215		reg = SPD5118_REG_TEMP_CRIT;216		break;217	case hwmon_temp_lcrit:218		reg = SPD5118_REG_TEMP_LCRIT;219		break;220	default:221		return -EOPNOTSUPP;222	}223 224	temp = spd5118_temp_to_reg(val);225	regval[0] = temp & 0xff;226	regval[1] = temp >> 8;227 228	return regmap_bulk_write(regmap, reg, regval, 2);229}230 231static int spd5118_write_enable(struct regmap *regmap, long val)232{233	if (val && val != 1)234		return -EINVAL;235 236	return regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG,237				  SPD5118_TS_DISABLE,238				  val ? 0 : SPD5118_TS_DISABLE);239}240 241static int spd5118_temp_write(struct regmap *regmap, u32 attr, long val)242{243	switch (attr) {244	case hwmon_temp_max:245	case hwmon_temp_min:246	case hwmon_temp_crit:247	case hwmon_temp_lcrit:248		return spd5118_write_temp(regmap, attr, val);249	case hwmon_temp_enable:250		return spd5118_write_enable(regmap, val);251	default:252		return -EOPNOTSUPP;253	}254}255 256static int spd5118_write(struct device *dev, enum hwmon_sensor_types type,257			 u32 attr, int channel, long val)258{259	struct regmap *regmap = dev_get_drvdata(dev);260 261	switch (type) {262	case hwmon_temp:263		return spd5118_temp_write(regmap, attr, val);264	default:265		return -EOPNOTSUPP;266	}267}268 269static umode_t spd5118_is_visible(const void *_data, enum hwmon_sensor_types type,270				  u32 attr, int channel)271{272	if (type != hwmon_temp)273		return 0;274 275	switch (attr) {276	case hwmon_temp_input:277		return 0444;278	case hwmon_temp_min:279	case hwmon_temp_max:280	case hwmon_temp_lcrit:281	case hwmon_temp_crit:282	case hwmon_temp_enable:283		return 0644;284	case hwmon_temp_min_alarm:285	case hwmon_temp_max_alarm:286	case hwmon_temp_crit_alarm:287	case hwmon_temp_lcrit_alarm:288		return 0444;289	default:290		return 0;291	}292}293 294static inline bool spd5118_parity8(u8 w)295{296	w ^= w >> 4;297	return (0x6996 >> (w & 0xf)) & 1;298}299 300/*301 * Bank and vendor id are 8-bit fields with seven data bits and odd parity.302 * Vendor IDs 0 and 0x7f are invalid.303 * See Jedec standard JEP106BJ for details and a list of assigned vendor IDs.304 */305static bool spd5118_vendor_valid(u8 bank, u8 id)306{307	if (!spd5118_parity8(bank) || !spd5118_parity8(id))308		return false;309 310	id &= 0x7f;311	return id && id != 0x7f;312}313 314/* Return 0 if detection is successful, -ENODEV otherwise */315static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)316{317	struct i2c_adapter *adapter = client->adapter;318	int regval;319 320	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |321				     I2C_FUNC_SMBUS_WORD_DATA))322		return -ENODEV;323 324	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);325	if (regval != 0x5118)326		return -ENODEV;327 328	regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);329	if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))330		return -ENODEV;331 332	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);333	if (regval < 0)334		return -ENODEV;335	if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))336		return -ENODEV;337 338	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);339	if (regval)340		return -ENODEV;341	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);342	if (regval)343		return -ENODEV;344 345	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);346	if (regval < 0 || (regval & 0xc1))347		return -ENODEV;348 349	regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);350	if (regval < 0)351		return -ENODEV;352	if (regval & ~SPD5118_TS_DISABLE)353		return -ENODEV;354 355	strscpy(info->type, "spd5118", I2C_NAME_SIZE);356	return 0;357}358 359static const struct hwmon_channel_info *spd5118_info[] = {360	HWMON_CHANNEL_INFO(chip,361			   HWMON_C_REGISTER_TZ),362	HWMON_CHANNEL_INFO(temp,363			   HWMON_T_INPUT |364			   HWMON_T_LCRIT | HWMON_T_LCRIT_ALARM |365			   HWMON_T_MIN | HWMON_T_MIN_ALARM |366			   HWMON_T_MAX | HWMON_T_MAX_ALARM |367			   HWMON_T_CRIT | HWMON_T_CRIT_ALARM |368			   HWMON_T_ENABLE),369	NULL370};371 372static const struct hwmon_ops spd5118_hwmon_ops = {373	.is_visible = spd5118_is_visible,374	.read = spd5118_read,375	.write = spd5118_write,376};377 378static const struct hwmon_chip_info spd5118_chip_info = {379	.ops = &spd5118_hwmon_ops,380	.info = spd5118_info,381};382 383/* nvmem */384 385static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,386				       unsigned int offset, size_t count)387{388	int addr = (offset >> SPD5118_PAGE_SHIFT) * 0x100 + SPD5118_EEPROM_BASE;389	int err;390 391	offset &= SPD5118_PAGE_MASK;392 393	/* Can't cross page boundaries */394	if (offset + count > SPD5118_PAGE_SIZE)395		count = SPD5118_PAGE_SIZE - offset;396 397	err = regmap_bulk_read(regmap, addr + offset, buf, count);398	if (err)399		return err;400 401	return count;402}403 404static int spd5118_nvmem_read(void *priv, unsigned int off, void *val, size_t count)405{406	struct spd5118_data *data = priv;407	char *buf = val;408	int ret;409 410	if (unlikely(!count))411		return count;412 413	if (off + count > SPD5118_EEPROM_SIZE)414		return -EINVAL;415 416	mutex_lock(&data->nvmem_lock);417 418	while (count) {419		ret = spd5118_nvmem_read_page(data->regmap, buf, off, count);420		if (ret < 0) {421			mutex_unlock(&data->nvmem_lock);422			return ret;423		}424		buf += ret;425		off += ret;426		count -= ret;427	}428	mutex_unlock(&data->nvmem_lock);429	return 0;430}431 432static int spd5118_nvmem_init(struct device *dev, struct spd5118_data *data)433{434	struct nvmem_config nvmem_config = {435		.type = NVMEM_TYPE_EEPROM,436		.name = dev_name(dev),437		.id = NVMEM_DEVID_NONE,438		.dev = dev,439		.base_dev = dev,440		.read_only = true,441		.root_only = false,442		.owner = THIS_MODULE,443		.compat = true,444		.reg_read = spd5118_nvmem_read,445		.priv = data,446		.stride = 1,447		.word_size = 1,448		.size = SPD5118_EEPROM_SIZE,449	};450	struct nvmem_device *nvmem;451 452	nvmem = devm_nvmem_register(dev, &nvmem_config);453	return PTR_ERR_OR_ZERO(nvmem);454}455 456/* regmap */457 458static bool spd5118_writeable_reg(struct device *dev, unsigned int reg)459{460	switch (reg) {461	case SPD5118_REG_I2C_LEGACY_MODE:462	case SPD5118_REG_TEMP_CLR:463	case SPD5118_REG_TEMP_CONFIG:464	case SPD5118_REG_TEMP_MAX:465	case SPD5118_REG_TEMP_MAX + 1:466	case SPD5118_REG_TEMP_MIN:467	case SPD5118_REG_TEMP_MIN + 1:468	case SPD5118_REG_TEMP_CRIT:469	case SPD5118_REG_TEMP_CRIT + 1:470	case SPD5118_REG_TEMP_LCRIT:471	case SPD5118_REG_TEMP_LCRIT + 1:472		return true;473	default:474		return false;475	}476}477 478static bool spd5118_volatile_reg(struct device *dev, unsigned int reg)479{480	switch (reg) {481	case SPD5118_REG_TEMP_CLR:482	case SPD5118_REG_ERROR_CLR:483	case SPD5118_REG_TEMP:484	case SPD5118_REG_TEMP + 1:485	case SPD5118_REG_TEMP_STATUS:486		return true;487	default:488		return false;489	}490}491 492static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {493	{494	.selector_reg   = SPD5118_REG_I2C_LEGACY_MODE,495	.selector_mask  = SPD5118_LEGACY_PAGE_MASK,496	.selector_shift = 0,497	.window_start   = 0,498	.window_len     = 0x100,499	.range_min      = 0,500	.range_max      = 0x7ff,501	},502};503 504static const struct regmap_config spd5118_regmap_config = {505	.reg_bits = 8,506	.val_bits = 8,507	.max_register = 0x7ff,508	.writeable_reg = spd5118_writeable_reg,509	.volatile_reg = spd5118_volatile_reg,510	.cache_type = REGCACHE_MAPLE,511 512	.ranges = spd5118_regmap_range_cfg,513	.num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),514};515 516static int spd5118_init(struct i2c_client *client)517{518	struct i2c_adapter *adapter = client->adapter;519	int err, regval, mode;520 521	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |522				     I2C_FUNC_SMBUS_WORD_DATA))523		return -ENODEV;524 525	regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);526	if (regval < 0 || (regval && regval != 0x5118))527		return -ENODEV;528 529	/*530	 * If the device type registers return 0, it is possible that the chip531	 * has a non-zero page selected and takes the specification literally,532	 * i.e. disables access to volatile registers besides the page register533	 * if the page is not 0. Try to identify such chips.534	 */535	if (!regval) {536		/* Vendor ID registers must also be 0 */537		regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);538		if (regval)539			return -ENODEV;540 541		/* The selected page in MR11 must not be 0 */542		mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);543		if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||544		    !(mode & SPD5118_LEGACY_PAGE_MASK))545			return -ENODEV;546 547		err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,548						mode & SPD5118_LEGACY_MODE_ADDR);549		if (err)550			return -ENODEV;551 552		/*553		 * If the device type registers are still bad after selecting554		 * page 0, this is not a SPD5118 device. Restore original555		 * legacy mode register value and abort.556		 */557		regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);558		if (regval != 0x5118) {559			i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);560			return -ENODEV;561		}562	}563 564	/* We are reasonably sure that this is really a SPD5118 hub controller */565	return 0;566}567 568static int spd5118_probe(struct i2c_client *client)569{570	struct device *dev = &client->dev;571	unsigned int regval, revision, vendor, bank;572	struct spd5118_data *data;573	struct device *hwmon_dev;574	struct regmap *regmap;575	int err;576 577	err = spd5118_init(client);578	if (err)579		return err;580 581	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);582	if (!data)583		return -ENOMEM;584 585	regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);586	if (IS_ERR(regmap))587		return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");588 589	err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &regval);590	if (err)591		return err;592	if (!(regval & SPD5118_CAP_TS_SUPPORT))593		return -ENODEV;594 595	err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);596	if (err)597		return err;598 599	err = regmap_read(regmap, SPD5118_REG_VENDOR, &bank);600	if (err)601		return err;602	err = regmap_read(regmap, SPD5118_REG_VENDOR + 1, &vendor);603	if (err)604		return err;605	if (!spd5118_vendor_valid(bank, vendor))606		return -ENODEV;607 608	data->regmap = regmap;609	mutex_init(&data->nvmem_lock);610	dev_set_drvdata(dev, data);611 612	err = spd5118_nvmem_init(dev, data);613	/* Ignore if NVMEM support is disabled */614	if (err && err != -EOPNOTSUPP) {615		dev_err_probe(dev, err, "failed to register nvmem\n");616		return err;617	}618 619	hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118",620							 regmap, &spd5118_chip_info,621							 NULL);622	if (IS_ERR(hwmon_dev))623		return PTR_ERR(hwmon_dev);624 625	/*626	 * From JESD300-5B627	 *   MR2 bits [5:4]: Major revision, 1..4628	 *   MR2 bits [3:1]: Minor revision, 0..8? Probably a typo, assume 1..8629	 */630	dev_info(dev, "DDR5 temperature sensor: vendor 0x%02x:0x%02x revision %d.%d\n",631		 bank & 0x7f, vendor, ((revision >> 4) & 0x03) + 1, ((revision >> 1) & 0x07) + 1);632 633	return 0;634}635 636static int spd5118_suspend(struct device *dev)637{638	struct spd5118_data *data = dev_get_drvdata(dev);639	struct regmap *regmap = data->regmap;640	u32 regval;641	int err;642 643	/*644	 * Make sure the configuration register in the regmap cache is current645	 * before bypassing it.646	 */647	err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);648	if (err < 0)649		return err;650 651	regcache_cache_bypass(regmap, true);652	regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,653			   SPD5118_TS_DISABLE);654	regcache_cache_bypass(regmap, false);655 656	regcache_cache_only(regmap, true);657	regcache_mark_dirty(regmap);658 659	return 0;660}661 662static int spd5118_resume(struct device *dev)663{664	struct spd5118_data *data = dev_get_drvdata(dev);665	struct regmap *regmap = data->regmap;666 667	regcache_cache_only(regmap, false);668	return regcache_sync(regmap);669}670 671static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);672 673static const struct i2c_device_id spd5118_id[] = {674	{ "spd5118", 0 },675	{ }676};677MODULE_DEVICE_TABLE(i2c, spd5118_id);678 679static const struct of_device_id spd5118_of_ids[] = {680	{ .compatible = "jedec,spd5118", },681	{ }682};683MODULE_DEVICE_TABLE(of, spd5118_of_ids);684 685static struct i2c_driver spd5118_driver = {686	.class		= I2C_CLASS_HWMON,687	.driver = {688		.name	= "spd5118",689		.of_match_table = spd5118_of_ids,690		.pm = pm_sleep_ptr(&spd5118_pm_ops),691	},692	.probe		= spd5118_probe,693	.id_table	= spd5118_id,694	.detect		= IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL,695	.address_list	= IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,696};697 698module_i2c_driver(spd5118_driver);699 700MODULE_AUTHOR("René Rebe <rene@exactcode.de>");701MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");702MODULE_DESCRIPTION("SPD 5118 driver");703MODULE_LICENSE("GPL");704