brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · 9b6d03c Raw
400 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * max1619.c - Part of lm_sensors, Linux kernel modules for hardware4 *             monitoring5 * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net>6 *                         Jean Delvare <jdelvare@suse.de>7 *8 * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim.9 * It reports up to two temperatures (its own plus up to10 * one external one). Complete datasheet can be11 * obtained from Maxim's website at:12 *   http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf13 */14 15#include <linux/err.h>16#include <linux/hwmon.h>17#include <linux/i2c.h>18#include <linux/init.h>19#include <linux/module.h>20#include <linux/regmap.h>21#include <linux/util_macros.h>22 23static const unsigned short normal_i2c[] = {24	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };25 26#define MAX1619_REG_LOCAL_TEMP		0x0027#define MAX1619_REG_REMOTE_TEMP		0x0128#define MAX1619_REG_STATUS		0x0229#define MAX1619_REG_CONFIG		0x0330#define MAX1619_REG_CONVRATE		0x0431#define MAX1619_REG_REMOTE_HIGH		0x0732#define MAX1619_REG_REMOTE_LOW		0x0833#define MAX1619_REG_REMOTE_CRIT		0x1034#define MAX1619_REG_REMOTE_CRIT_HYST	0x1135#define MAX1619_REG_MAN_ID		0xFE36#define MAX1619_REG_CHIP_ID		0xFF37 38static int get_alarms(struct regmap *regmap)39{40	static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG };41	u8 regdata[2];42	int ret;43 44	ret = regmap_multi_reg_read(regmap, regs, regdata, 2);45	if (ret)46		return ret;47 48	/* OVERT status bit may be reversed */49	if (!(regdata[1] & 0x20))50		regdata[0] ^= 0x02;51 52	return regdata[0] & 0x1e;53}54 55static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long *val)56{57	int reg = -1, alarm_bit = 0;58	u32 temp;59	int ret;60 61	switch (attr) {62	case hwmon_temp_input:63		reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP;64		break;65	case hwmon_temp_min:66		reg = MAX1619_REG_REMOTE_LOW;67		break;68	case hwmon_temp_max:69		reg = MAX1619_REG_REMOTE_HIGH;70		break;71	case hwmon_temp_crit:72		reg = MAX1619_REG_REMOTE_CRIT;73		break;74	case hwmon_temp_crit_hyst:75		reg = MAX1619_REG_REMOTE_CRIT_HYST;76		break;77	case hwmon_temp_min_alarm:78		alarm_bit = 3;79		break;80	case hwmon_temp_max_alarm:81		alarm_bit = 4;82		break;83	case hwmon_temp_crit_alarm:84		alarm_bit = 1;85		break;86	case hwmon_temp_fault:87		alarm_bit = 2;88		break;89	default:90		return -EOPNOTSUPP;91	}92	if (reg >= 0) {93		ret = regmap_read(regmap, reg, &temp);94		if (ret < 0)95			return ret;96		*val = sign_extend32(temp, 7) * 1000;97	} else {98		ret = get_alarms(regmap);99		if (ret < 0)100			return ret;101		*val = !!(ret & BIT(alarm_bit));102	}103	return 0;104}105 106static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 };107 108static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val)109{110	int alarms, ret;111	u32 regval;112 113	switch (attr) {114	case hwmon_chip_update_interval:115		ret = regmap_read(regmap, MAX1619_REG_CONVRATE, &regval);116		if (ret < 0)117			return ret;118		*val = update_intervals[regval & 7];119		break;120	case hwmon_chip_alarms:121		alarms = get_alarms(regmap);122		if (alarms < 0)123			return alarms;124		*val = alarms;125		break;126	default:127		return -EOPNOTSUPP;128	}129	return 0;130}131 132static int max1619_read(struct device *dev, enum hwmon_sensor_types type,133			u32 attr, int channel, long *val)134{135	struct regmap *regmap = dev_get_drvdata(dev);136 137	switch (type) {138	case hwmon_chip:139		return max1619_chip_read(regmap, attr, val);140	case hwmon_temp:141		return max1619_temp_read(regmap, attr, channel, val);142	default:143		return -EOPNOTSUPP;144	}145}146 147static int max1619_chip_write(struct regmap *regmap, u32 attr, long val)148{149	switch (attr) {150	case hwmon_chip_update_interval:151		val = find_closest_descending(val, update_intervals, ARRAY_SIZE(update_intervals));152		return regmap_write(regmap, MAX1619_REG_CONVRATE, val);153	default:154		return -EOPNOTSUPP;155	}156}157 158static int max1619_temp_write(struct regmap *regmap,159			      u32 attr, int channel, long val)160{161	int reg;162 163	switch (attr) {164	case hwmon_temp_min:165		reg = MAX1619_REG_REMOTE_LOW;166		break;167	case hwmon_temp_max:168		reg = MAX1619_REG_REMOTE_HIGH;169		break;170	case hwmon_temp_crit:171		reg = MAX1619_REG_REMOTE_CRIT;172		break;173	case hwmon_temp_crit_hyst:174		reg = MAX1619_REG_REMOTE_CRIT_HYST;175		break;176	default:177		return -EOPNOTSUPP;178	}179	val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);180	return regmap_write(regmap, reg, val);181}182 183static int max1619_write(struct device *dev, enum hwmon_sensor_types type,184			 u32 attr, int channel, long val)185{186	struct regmap *regmap = dev_get_drvdata(dev);187 188	switch (type) {189	case hwmon_chip:190		return max1619_chip_write(regmap, attr, val);191	case hwmon_temp:192		return max1619_temp_write(regmap, attr, channel, val);193	default:194		return -EOPNOTSUPP;195	}196}197 198static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type,199				  u32 attr, int channel)200{201	switch (type) {202	case hwmon_chip:203		switch (attr) {204		case hwmon_chip_update_interval:205			return 0644;206		case hwmon_chip_alarms:207			return 0444;208		default:209			break;210		}211		break;212	case hwmon_temp:213		switch (attr) {214		case hwmon_temp_input:215			return 0444;216		case hwmon_temp_min:217		case hwmon_temp_max:218		case hwmon_temp_crit:219		case hwmon_temp_crit_hyst:220			return 0644;221		case hwmon_temp_min_alarm:222		case hwmon_temp_max_alarm:223		case hwmon_temp_crit_alarm:224		case hwmon_temp_fault:225			return 0444;226		default:227			break;228		}229		break;230	default:231		break;232	}233	return 0;234}235 236static const struct hwmon_channel_info * const max1619_info[] = {237	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL),238	HWMON_CHANNEL_INFO(temp,239			   HWMON_T_INPUT,240			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |241			   HWMON_T_CRIT | HWMON_T_CRIT_HYST |242			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |243			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT),244	NULL245};246 247static const struct hwmon_ops max1619_hwmon_ops = {248	.is_visible = max1619_is_visible,249	.read = max1619_read,250	.write = max1619_write,251};252 253static const struct hwmon_chip_info max1619_chip_info = {254	.ops = &max1619_hwmon_ops,255	.info = max1619_info,256};257 258/* Return 0 if detection is successful, -ENODEV otherwise */259static int max1619_detect(struct i2c_client *client,260			  struct i2c_board_info *info)261{262	struct i2c_adapter *adapter = client->adapter;263	int regval;264 265	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))266		return -ENODEV;267 268	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG);269	if (regval < 0 || (regval & 0x03))270		return -ENODEV;271	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE);272	if (regval < 0 || regval > 0x07)273		return -ENODEV;274	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS);275	if (regval < 0 || (regval & 0x61))276		return -ENODEV;277 278	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID);279	if (regval != 0x4d)280		return -ENODEV;281	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID);282	if (regval != 0x04)283		return -ENODEV;284 285	strscpy(info->type, "max1619", I2C_NAME_SIZE);286 287	return 0;288}289 290static int max1619_init_chip(struct regmap *regmap)291{292	int ret;293 294	ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5);	/* 2 Hz */295	if (ret)296		return ret;297 298	/* Start conversions */299	return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40);300}301 302/* regmap */303 304static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val)305{306	int ret;307 308	ret = i2c_smbus_read_byte_data(context, reg);309	if (ret < 0)310		return ret;311 312	*val = ret;313	return 0;314}315 316static int max1619_reg_write(void *context, unsigned int reg, unsigned int val)317{318	int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2;319 320	return i2c_smbus_write_byte_data(context, reg + offset, val);321}322 323static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg)324{325	return reg <= MAX1619_REG_STATUS;326}327 328static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg)329{330	return reg > MAX1619_REG_STATUS && reg <= MAX1619_REG_REMOTE_CRIT_HYST;331}332 333static const struct regmap_config max1619_regmap_config = {334	.reg_bits = 8,335	.val_bits = 8,336	.max_register = MAX1619_REG_REMOTE_CRIT_HYST,337	.cache_type = REGCACHE_MAPLE,338	.volatile_reg = max1619_regmap_is_volatile,339	.writeable_reg = max1619_regmap_is_writeable,340};341 342static const struct regmap_bus max1619_regmap_bus = {343	.reg_write = max1619_reg_write,344	.reg_read = max1619_reg_read,345};346 347static int max1619_probe(struct i2c_client *client)348{349	struct device *dev = &client->dev;350	struct device *hwmon_dev;351	struct regmap *regmap;352	int ret;353 354	regmap = devm_regmap_init(dev, &max1619_regmap_bus, client,355				  &max1619_regmap_config);356	if (IS_ERR(regmap))357		return PTR_ERR(regmap);358 359	ret = max1619_init_chip(regmap);360	if (ret)361		return ret;362 363	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,364							 regmap, &max1619_chip_info, NULL);365	return PTR_ERR_OR_ZERO(hwmon_dev);366}367 368static const struct i2c_device_id max1619_id[] = {369	{ "max1619" },370	{ }371};372MODULE_DEVICE_TABLE(i2c, max1619_id);373 374#ifdef CONFIG_OF375static const struct of_device_id max1619_of_match[] = {376	{ .compatible = "maxim,max1619", },377	{},378};379 380MODULE_DEVICE_TABLE(of, max1619_of_match);381#endif382 383static struct i2c_driver max1619_driver = {384	.class		= I2C_CLASS_HWMON,385	.driver = {386		.name	= "max1619",387		.of_match_table = of_match_ptr(max1619_of_match),388	},389	.probe		= max1619_probe,390	.id_table	= max1619_id,391	.detect		= max1619_detect,392	.address_list	= normal_i2c,393};394 395module_i2c_driver(max1619_driver);396 397MODULE_AUTHOR("Oleksij Rempel <bug-track@fisher-privat.net>, Jean Delvare <jdelvare@suse.de>");398MODULE_DESCRIPTION("MAX1619 sensor driver");399MODULE_LICENSE("GPL");400