brintos

brintos / linux-shallow public Read only

0
0
Text · 6.8 KiB · 401a28f Raw
306 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2017 IBM Corp.4 *5 * Driver for the Nuvoton W83773G SMBus temperature sensor IC.6 * Supported models: W83773G7 */8 9#include <linux/module.h>10#include <linux/init.h>11#include <linux/i2c.h>12#include <linux/hwmon.h>13#include <linux/hwmon-sysfs.h>14#include <linux/err.h>15#include <linux/of.h>16#include <linux/regmap.h>17 18/* W83773 has 3 channels */19#define W83773_CHANNELS				320 21/* The W83773 registers */22#define W83773_CONVERSION_RATE_REG_READ		0x0423#define W83773_CONVERSION_RATE_REG_WRITE	0x0A24#define W83773_MANUFACTURER_ID_REG		0xFE25#define W83773_LOCAL_TEMP			0x0026 27static const u8 W83773_STATUS[2] = { 0x02, 0x17 };28 29static const u8 W83773_TEMP_LSB[2] = { 0x10, 0x25 };30static const u8 W83773_TEMP_MSB[2] = { 0x01, 0x24 };31 32static const u8 W83773_OFFSET_LSB[2] = { 0x12, 0x16 };33static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };34 35/* this is the number of sensors in the device */36static const struct i2c_device_id w83773_id[] = {37	{ "w83773g" },38	{ }39};40 41MODULE_DEVICE_TABLE(i2c, w83773_id);42 43static const struct of_device_id __maybe_unused w83773_of_match[] = {44	{45		.compatible = "nuvoton,w83773g"46	},47	{ },48};49MODULE_DEVICE_TABLE(of, w83773_of_match);50 51static inline long temp_of_local(s8 reg)52{53	return reg * 1000;54}55 56static inline long temp_of_remote(s8 hb, u8 lb)57{58	return (hb << 3 | lb >> 5) * 125;59}60 61static int get_local_temp(struct regmap *regmap, long *val)62{63	unsigned int regval;64	int ret;65 66	ret = regmap_read(regmap, W83773_LOCAL_TEMP, &regval);67	if (ret < 0)68		return ret;69 70	*val = temp_of_local(regval);71	return 0;72}73 74static int get_remote_temp(struct regmap *regmap, int index, long *val)75{76	unsigned int regval_high;77	unsigned int regval_low;78	int ret;79 80	ret = regmap_read(regmap, W83773_TEMP_MSB[index], &regval_high);81	if (ret < 0)82		return ret;83 84	ret = regmap_read(regmap, W83773_TEMP_LSB[index], &regval_low);85	if (ret < 0)86		return ret;87 88	*val = temp_of_remote(regval_high, regval_low);89	return 0;90}91 92static int get_fault(struct regmap *regmap, int index, long *val)93{94	unsigned int regval;95	int ret;96 97	ret = regmap_read(regmap, W83773_STATUS[index], &regval);98	if (ret < 0)99		return ret;100 101	*val = (regval & 0x04) >> 2;102	return 0;103}104 105static int get_offset(struct regmap *regmap, int index, long *val)106{107	unsigned int regval_high;108	unsigned int regval_low;109	int ret;110 111	ret = regmap_read(regmap, W83773_OFFSET_MSB[index], &regval_high);112	if (ret < 0)113		return ret;114 115	ret = regmap_read(regmap, W83773_OFFSET_LSB[index], &regval_low);116	if (ret < 0)117		return ret;118 119	*val = temp_of_remote(regval_high, regval_low);120	return 0;121}122 123static int set_offset(struct regmap *regmap, int index, long val)124{125	int ret;126	u8 high_byte;127	u8 low_byte;128 129	val = clamp_val(val, -127825, 127825);130	/* offset value equals to (high_byte << 3 | low_byte >> 5) * 125 */131	val /= 125;132	high_byte = val >> 3;133	low_byte = (val & 0x07) << 5;134 135	ret = regmap_write(regmap, W83773_OFFSET_MSB[index], high_byte);136	if (ret < 0)137		return ret;138 139	return regmap_write(regmap, W83773_OFFSET_LSB[index], low_byte);140}141 142static int get_update_interval(struct regmap *regmap, long *val)143{144	unsigned int regval;145	int ret;146 147	ret = regmap_read(regmap, W83773_CONVERSION_RATE_REG_READ, &regval);148	if (ret < 0)149		return ret;150 151	*val = 16000 >> regval;152	return 0;153}154 155static int set_update_interval(struct regmap *regmap, long val)156{157	int rate;158 159	/*160	 * For valid rates, interval can be calculated as161	 *	interval = (1 << (8 - rate)) * 62.5;162	 * Rounded rate is therefore163	 *	rate = 8 - __fls(interval * 8 / (62.5 * 7));164	 * Use clamp_val() to avoid overflows, and to ensure valid input165	 * for __fls.166	 */167	val = clamp_val(val, 62, 16000) * 10;168	rate = 8 - __fls((val * 8 / (625 * 7)));169	return regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, rate);170}171 172static int w83773_read(struct device *dev, enum hwmon_sensor_types type,173		       u32 attr, int channel, long *val)174{175	struct regmap *regmap = dev_get_drvdata(dev);176 177	if (type == hwmon_chip) {178		if (attr == hwmon_chip_update_interval)179			return get_update_interval(regmap, val);180		return -EOPNOTSUPP;181	}182 183	switch (attr) {184	case hwmon_temp_input:185		if (channel == 0)186			return get_local_temp(regmap, val);187		return get_remote_temp(regmap, channel - 1, val);188	case hwmon_temp_fault:189		return get_fault(regmap, channel - 1, val);190	case hwmon_temp_offset:191		return get_offset(regmap, channel - 1, val);192	default:193		return -EOPNOTSUPP;194	}195}196 197static int w83773_write(struct device *dev, enum hwmon_sensor_types type,198			u32 attr, int channel, long val)199{200	struct regmap *regmap = dev_get_drvdata(dev);201 202	if (type == hwmon_chip && attr == hwmon_chip_update_interval)203		return set_update_interval(regmap, val);204 205	if (type == hwmon_temp && attr == hwmon_temp_offset)206		return set_offset(regmap, channel - 1, val);207 208	return -EOPNOTSUPP;209}210 211static umode_t w83773_is_visible(const void *data, enum hwmon_sensor_types type,212				 u32 attr, int channel)213{214	switch (type) {215	case hwmon_chip:216		switch (attr) {217		case hwmon_chip_update_interval:218			return 0644;219		}220		break;221	case hwmon_temp:222		switch (attr) {223		case hwmon_temp_input:224		case hwmon_temp_fault:225			return 0444;226		case hwmon_temp_offset:227			return 0644;228		}229		break;230	default:231		break;232	}233	return 0;234}235 236static const struct hwmon_channel_info * const w83773_info[] = {237	HWMON_CHANNEL_INFO(chip,238			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),239	HWMON_CHANNEL_INFO(temp,240			   HWMON_T_INPUT,241			   HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,242			   HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET),243	NULL244};245 246static const struct hwmon_ops w83773_ops = {247	.is_visible = w83773_is_visible,248	.read = w83773_read,249	.write = w83773_write,250};251 252static const struct hwmon_chip_info w83773_chip_info = {253	.ops = &w83773_ops,254	.info = w83773_info,255};256 257static const struct regmap_config w83773_regmap_config = {258	.reg_bits = 8,259	.val_bits = 8,260};261 262static int w83773_probe(struct i2c_client *client)263{264	struct device *dev = &client->dev;265	struct device *hwmon_dev;266	struct regmap *regmap;267	int ret;268 269	regmap = devm_regmap_init_i2c(client, &w83773_regmap_config);270	if (IS_ERR(regmap)) {271		dev_err(dev, "failed to allocate register map\n");272		return PTR_ERR(regmap);273	}274 275	/* Set the conversion rate to 2 Hz */276	ret = regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, 0x05);277	if (ret < 0) {278		dev_err(&client->dev, "error writing config rate register\n");279		return ret;280	}281 282	i2c_set_clientdata(client, regmap);283 284	hwmon_dev = devm_hwmon_device_register_with_info(dev,285							 client->name,286							 regmap,287							 &w83773_chip_info,288							 NULL);289	return PTR_ERR_OR_ZERO(hwmon_dev);290}291 292static struct i2c_driver w83773_driver = {293	.driver = {294		.name	= "w83773g",295		.of_match_table = of_match_ptr(w83773_of_match),296	},297	.probe = w83773_probe,298	.id_table = w83773_id,299};300 301module_i2c_driver(w83773_driver);302 303MODULE_AUTHOR("Lei YU <mine260309@gmail.com>");304MODULE_DESCRIPTION("W83773G temperature sensor driver");305MODULE_LICENSE("GPL");306