mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
495e6a9cf5
The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis <afd@ti.com> Link: https://lore.kernel.org/r/20240403203633.914389-21-afd@ti.com Signed-off-by: Guenter Roeck <linux@roeck-us.net>
447 lines
13 KiB
C
447 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2011 David George <david.george@ska.ac.za>
|
|
*
|
|
* based on adm1021.c
|
|
* some credit to Christoph Scheurer, but largely a rewrite
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/hwmon.h>
|
|
#include <linux/hwmon-sysfs.h>
|
|
#include <linux/err.h>
|
|
#include <linux/mutex.h>
|
|
|
|
/* Addresses to scan */
|
|
static const unsigned short max1668_addr_list[] = {
|
|
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
|
|
|
|
/* max1668 registers */
|
|
|
|
#define MAX1668_REG_TEMP(nr) (nr)
|
|
#define MAX1668_REG_STAT1 0x05
|
|
#define MAX1668_REG_STAT2 0x06
|
|
#define MAX1668_REG_MAN_ID 0xfe
|
|
#define MAX1668_REG_DEV_ID 0xff
|
|
|
|
/* limits */
|
|
|
|
/* write high limits */
|
|
#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr))
|
|
/* write low limits */
|
|
#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr))
|
|
/* read high limits */
|
|
#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr))
|
|
/* read low limits */
|
|
#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr))
|
|
|
|
/* manufacturer and device ID Constants */
|
|
#define MAN_ID_MAXIM 0x4d
|
|
#define DEV_ID_MAX1668 0x3
|
|
#define DEV_ID_MAX1805 0x5
|
|
#define DEV_ID_MAX1989 0xb
|
|
|
|
/* read only mode module parameter */
|
|
static bool read_only;
|
|
module_param(read_only, bool, 0);
|
|
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
|
|
|
|
enum chips { max1668, max1805, max1989 };
|
|
|
|
struct max1668_data {
|
|
struct i2c_client *client;
|
|
const struct attribute_group *groups[3];
|
|
enum chips type;
|
|
|
|
struct mutex update_lock;
|
|
bool valid; /* true if following fields are valid */
|
|
unsigned long last_updated; /* In jiffies */
|
|
|
|
/* 1x local and 4x remote */
|
|
s8 temp_max[5];
|
|
s8 temp_min[5];
|
|
s8 temp[5];
|
|
u16 alarms;
|
|
};
|
|
|
|
static struct max1668_data *max1668_update_device(struct device *dev)
|
|
{
|
|
struct max1668_data *data = dev_get_drvdata(dev);
|
|
struct i2c_client *client = data->client;
|
|
struct max1668_data *ret = data;
|
|
s32 val;
|
|
int i;
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
if (data->valid && !time_after(jiffies,
|
|
data->last_updated + HZ + HZ / 2))
|
|
goto abort;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
|
|
if (unlikely(val < 0)) {
|
|
ret = ERR_PTR(val);
|
|
goto abort;
|
|
}
|
|
data->temp[i] = (s8) val;
|
|
|
|
val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
|
|
if (unlikely(val < 0)) {
|
|
ret = ERR_PTR(val);
|
|
goto abort;
|
|
}
|
|
data->temp_max[i] = (s8) val;
|
|
|
|
val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
|
|
if (unlikely(val < 0)) {
|
|
ret = ERR_PTR(val);
|
|
goto abort;
|
|
}
|
|
data->temp_min[i] = (s8) val;
|
|
}
|
|
|
|
val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
|
|
if (unlikely(val < 0)) {
|
|
ret = ERR_PTR(val);
|
|
goto abort;
|
|
}
|
|
data->alarms = val << 8;
|
|
|
|
val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
|
|
if (unlikely(val < 0)) {
|
|
ret = ERR_PTR(val);
|
|
goto abort;
|
|
}
|
|
data->alarms |= val;
|
|
|
|
data->last_updated = jiffies;
|
|
data->valid = true;
|
|
abort:
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_temp(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = max1668_update_device(dev);
|
|
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
|
|
return sprintf(buf, "%d\n", data->temp[index] * 1000);
|
|
}
|
|
|
|
static ssize_t show_temp_max(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = max1668_update_device(dev);
|
|
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
|
|
return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
|
|
}
|
|
|
|
static ssize_t show_temp_min(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = max1668_update_device(dev);
|
|
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
|
|
return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
|
|
}
|
|
|
|
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
int index = to_sensor_dev_attr(attr)->index;
|
|
struct max1668_data *data = max1668_update_device(dev);
|
|
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
|
|
return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
|
|
}
|
|
|
|
static ssize_t show_fault(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = max1668_update_device(dev);
|
|
|
|
if (IS_ERR(data))
|
|
return PTR_ERR(data);
|
|
|
|
return sprintf(buf, "%u\n",
|
|
(data->alarms & (1 << 12)) && data->temp[index] == 127);
|
|
}
|
|
|
|
static ssize_t set_temp_max(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = dev_get_drvdata(dev);
|
|
struct i2c_client *client = data->client;
|
|
long temp;
|
|
int ret;
|
|
|
|
ret = kstrtol(buf, 10, &temp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
mutex_lock(&data->update_lock);
|
|
data->temp_max[index] = clamp_val(temp/1000, -128, 127);
|
|
ret = i2c_smbus_write_byte_data(client,
|
|
MAX1668_REG_LIMH_WR(index),
|
|
data->temp_max[index]);
|
|
if (ret < 0)
|
|
count = ret;
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t set_temp_min(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int index = to_sensor_dev_attr(devattr)->index;
|
|
struct max1668_data *data = dev_get_drvdata(dev);
|
|
struct i2c_client *client = data->client;
|
|
long temp;
|
|
int ret;
|
|
|
|
ret = kstrtol(buf, 10, &temp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
mutex_lock(&data->update_lock);
|
|
data->temp_min[index] = clamp_val(temp/1000, -128, 127);
|
|
ret = i2c_smbus_write_byte_data(client,
|
|
MAX1668_REG_LIML_WR(index),
|
|
data->temp_min[index]);
|
|
if (ret < 0)
|
|
count = ret;
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
|
|
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
|
|
set_temp_max, 0);
|
|
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
|
|
set_temp_min, 0);
|
|
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
|
|
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
|
|
set_temp_max, 1);
|
|
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
|
|
set_temp_min, 1);
|
|
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
|
|
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
|
|
set_temp_max, 2);
|
|
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
|
|
set_temp_min, 2);
|
|
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
|
|
static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
|
|
set_temp_max, 3);
|
|
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
|
|
set_temp_min, 3);
|
|
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
|
|
static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
|
|
set_temp_max, 4);
|
|
static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
|
|
set_temp_min, 4);
|
|
|
|
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
|
|
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
|
|
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
|
|
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
|
|
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
|
|
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
|
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
|
|
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
|
|
static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
|
|
static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
|
|
|
|
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
|
|
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
|
|
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
|
|
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
|
|
|
|
/* Attributes common to MAX1668, MAX1989 and MAX1805 */
|
|
static struct attribute *max1668_attribute_common[] = {
|
|
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
|
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
|
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
|
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
|
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
|
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
|
|
|
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
|
|
|
|
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
|
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
|
NULL
|
|
};
|
|
|
|
/* Attributes not present on MAX1805 */
|
|
static struct attribute *max1668_attribute_unique[] = {
|
|
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
|
&sensor_dev_attr_temp4_min.dev_attr.attr,
|
|
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_max.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_min.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_input.dev_attr.attr,
|
|
|
|
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
|
|
|
|
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
|
&sensor_dev_attr_temp5_fault.dev_attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static umode_t max1668_attribute_mode(struct kobject *kobj,
|
|
struct attribute *attr, int index)
|
|
{
|
|
umode_t ret = S_IRUGO;
|
|
if (read_only)
|
|
return ret;
|
|
if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
|
|
attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
|
|
ret |= S_IWUSR;
|
|
return ret;
|
|
}
|
|
|
|
static const struct attribute_group max1668_group_common = {
|
|
.attrs = max1668_attribute_common,
|
|
.is_visible = max1668_attribute_mode
|
|
};
|
|
|
|
static const struct attribute_group max1668_group_unique = {
|
|
.attrs = max1668_attribute_unique,
|
|
.is_visible = max1668_attribute_mode
|
|
};
|
|
|
|
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
static int max1668_detect(struct i2c_client *client,
|
|
struct i2c_board_info *info)
|
|
{
|
|
struct i2c_adapter *adapter = client->adapter;
|
|
const char *type_name;
|
|
int man_id, dev_id;
|
|
|
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
|
return -ENODEV;
|
|
|
|
/* Check for unsupported part */
|
|
man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID);
|
|
if (man_id != MAN_ID_MAXIM)
|
|
return -ENODEV;
|
|
|
|
dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID);
|
|
if (dev_id < 0)
|
|
return -ENODEV;
|
|
|
|
type_name = NULL;
|
|
if (dev_id == DEV_ID_MAX1668)
|
|
type_name = "max1668";
|
|
else if (dev_id == DEV_ID_MAX1805)
|
|
type_name = "max1805";
|
|
else if (dev_id == DEV_ID_MAX1989)
|
|
type_name = "max1989";
|
|
|
|
if (!type_name)
|
|
return -ENODEV;
|
|
|
|
strscpy(info->type, type_name, I2C_NAME_SIZE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int max1668_probe(struct i2c_client *client)
|
|
{
|
|
struct i2c_adapter *adapter = client->adapter;
|
|
struct device *dev = &client->dev;
|
|
struct device *hwmon_dev;
|
|
struct max1668_data *data;
|
|
|
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
|
return -ENODEV;
|
|
|
|
data = devm_kzalloc(dev, sizeof(struct max1668_data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
data->client = client;
|
|
data->type = (uintptr_t)i2c_get_match_data(client);
|
|
mutex_init(&data->update_lock);
|
|
|
|
/* sysfs hooks */
|
|
data->groups[0] = &max1668_group_common;
|
|
if (data->type == max1668 || data->type == max1989)
|
|
data->groups[1] = &max1668_group_unique;
|
|
|
|
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
|
data, data->groups);
|
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
|
}
|
|
|
|
static const struct i2c_device_id max1668_id[] = {
|
|
{ "max1668", max1668 },
|
|
{ "max1805", max1805 },
|
|
{ "max1989", max1989 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, max1668_id);
|
|
|
|
/* This is the driver that will be inserted */
|
|
static struct i2c_driver max1668_driver = {
|
|
.class = I2C_CLASS_HWMON,
|
|
.driver = {
|
|
.name = "max1668",
|
|
},
|
|
.probe = max1668_probe,
|
|
.id_table = max1668_id,
|
|
.detect = max1668_detect,
|
|
.address_list = max1668_addr_list,
|
|
};
|
|
|
|
module_i2c_driver(max1668_driver);
|
|
|
|
MODULE_AUTHOR("David George <david.george@ska.ac.za>");
|
|
MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver");
|
|
MODULE_LICENSE("GPL");
|