162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Maxim MAX197 A/D Converter driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012 Savoir-faire Linux Inc. 662306a36Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * For further information, see the Documentation/hwmon/max197.rst file. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/sysfs.h> 2062306a36Sopenharmony_ci#include <linux/hwmon.h> 2162306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/platform_data/max197.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MAX199_LIMIT 4000 /* 4V */ 2662306a36Sopenharmony_ci#define MAX197_LIMIT 10000 /* 10V */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Control byte format */ 3162306a36Sopenharmony_ci#define MAX197_BIP (1 << 3) /* Bipolarity */ 3262306a36Sopenharmony_ci#define MAX197_RNG (1 << 4) /* Full range */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MAX197_SCALE 12207 /* Scale coefficient for raw data */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* List of supported chips */ 3762306a36Sopenharmony_cienum max197_chips { max197, max199 }; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * struct max197_data - device instance specific data 4162306a36Sopenharmony_ci * @pdata: Platform data. 4262306a36Sopenharmony_ci * @hwmon_dev: The hwmon device. 4362306a36Sopenharmony_ci * @lock: Read/Write mutex. 4462306a36Sopenharmony_ci * @limit: Max range value (10V for MAX197, 4V for MAX199). 4562306a36Sopenharmony_ci * @scale: Need to scale. 4662306a36Sopenharmony_ci * @ctrl_bytes: Channels control byte. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistruct max197_data { 4962306a36Sopenharmony_ci struct max197_platform_data *pdata; 5062306a36Sopenharmony_ci struct device *hwmon_dev; 5162306a36Sopenharmony_ci struct mutex lock; 5262306a36Sopenharmony_ci int limit; 5362306a36Sopenharmony_ci bool scale; 5462306a36Sopenharmony_ci u8 ctrl_bytes[MAX197_NUM_CH]; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic inline void max197_set_unipolarity(struct max197_data *data, int channel) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci data->ctrl_bytes[channel] &= ~MAX197_BIP; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline void max197_set_bipolarity(struct max197_data *data, int channel) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci data->ctrl_bytes[channel] |= MAX197_BIP; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline void max197_set_half_range(struct max197_data *data, int channel) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci data->ctrl_bytes[channel] &= ~MAX197_RNG; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline void max197_set_full_range(struct max197_data *data, int channel) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci data->ctrl_bytes[channel] |= MAX197_RNG; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline bool max197_is_bipolar(struct max197_data *data, int channel) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return data->ctrl_bytes[channel] & MAX197_BIP; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline bool max197_is_full_range(struct max197_data *data, int channel) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return data->ctrl_bytes[channel] & MAX197_RNG; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */ 8862306a36Sopenharmony_cistatic ssize_t max197_show_range(struct device *dev, 8962306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 9262306a36Sopenharmony_ci struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 9362306a36Sopenharmony_ci int channel = attr->index; 9462306a36Sopenharmony_ci bool is_min = attr->nr; 9562306a36Sopenharmony_ci int range; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 9862306a36Sopenharmony_ci return -ERESTARTSYS; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci range = max197_is_full_range(data, channel) ? 10162306a36Sopenharmony_ci data->limit : data->limit / 2; 10262306a36Sopenharmony_ci if (is_min) { 10362306a36Sopenharmony_ci if (max197_is_bipolar(data, channel)) 10462306a36Sopenharmony_ci range = -range; 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci range = 0; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mutex_unlock(&data->lock); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return sprintf(buf, "%d\n", range); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */ 11562306a36Sopenharmony_cistatic ssize_t max197_store_range(struct device *dev, 11662306a36Sopenharmony_ci struct device_attribute *devattr, 11762306a36Sopenharmony_ci const char *buf, size_t count) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 12062306a36Sopenharmony_ci struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 12162306a36Sopenharmony_ci int channel = attr->index; 12262306a36Sopenharmony_ci bool is_min = attr->nr; 12362306a36Sopenharmony_ci long value; 12462306a36Sopenharmony_ci int half = data->limit / 2; 12562306a36Sopenharmony_ci int full = data->limit; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (kstrtol(buf, 10, &value)) 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (is_min) { 13162306a36Sopenharmony_ci if (value <= -full) 13262306a36Sopenharmony_ci value = -full; 13362306a36Sopenharmony_ci else if (value < 0) 13462306a36Sopenharmony_ci value = -half; 13562306a36Sopenharmony_ci else 13662306a36Sopenharmony_ci value = 0; 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci if (value >= full) 13962306a36Sopenharmony_ci value = full; 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci value = half; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 14562306a36Sopenharmony_ci return -ERESTARTSYS; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (value == 0) { 14862306a36Sopenharmony_ci /* We can deduce only the polarity */ 14962306a36Sopenharmony_ci max197_set_unipolarity(data, channel); 15062306a36Sopenharmony_ci } else if (value == -half) { 15162306a36Sopenharmony_ci max197_set_bipolarity(data, channel); 15262306a36Sopenharmony_ci max197_set_half_range(data, channel); 15362306a36Sopenharmony_ci } else if (value == -full) { 15462306a36Sopenharmony_ci max197_set_bipolarity(data, channel); 15562306a36Sopenharmony_ci max197_set_full_range(data, channel); 15662306a36Sopenharmony_ci } else if (value == half) { 15762306a36Sopenharmony_ci /* We can deduce only the range */ 15862306a36Sopenharmony_ci max197_set_half_range(data, channel); 15962306a36Sopenharmony_ci } else if (value == full) { 16062306a36Sopenharmony_ci /* We can deduce only the range */ 16162306a36Sopenharmony_ci max197_set_full_range(data, channel); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci mutex_unlock(&data->lock); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return count; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */ 17062306a36Sopenharmony_cistatic ssize_t max197_show_input(struct device *dev, 17162306a36Sopenharmony_ci struct device_attribute *devattr, 17262306a36Sopenharmony_ci char *buf) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 17562306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 17662306a36Sopenharmony_ci int channel = attr->index; 17762306a36Sopenharmony_ci s32 value; 17862306a36Sopenharmony_ci int ret; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 18162306a36Sopenharmony_ci return -ERESTARTSYS; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = data->pdata->convert(data->ctrl_bytes[channel]); 18462306a36Sopenharmony_ci if (ret < 0) { 18562306a36Sopenharmony_ci dev_err(dev, "conversion failed\n"); 18662306a36Sopenharmony_ci goto unlock; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci value = ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Coefficient to apply on raw value. 19262306a36Sopenharmony_ci * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci if (data->scale) { 19562306a36Sopenharmony_ci value *= MAX197_SCALE; 19662306a36Sopenharmony_ci if (max197_is_full_range(data, channel)) 19762306a36Sopenharmony_ci value *= 2; 19862306a36Sopenharmony_ci value /= 10000; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = sprintf(buf, "%d\n", value); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciunlock: 20462306a36Sopenharmony_ci mutex_unlock(&data->lock); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 20962306a36Sopenharmony_ci char *buf) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 21262306a36Sopenharmony_ci return sprintf(buf, "%s\n", pdev->name); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \ 21662306a36Sopenharmony_ci static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \ 21762306a36Sopenharmony_ci max197_show_input, NULL, chan); \ 21862306a36Sopenharmony_ci static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \ 21962306a36Sopenharmony_ci max197_show_range, \ 22062306a36Sopenharmony_ci max197_store_range, \ 22162306a36Sopenharmony_ci true, chan); \ 22262306a36Sopenharmony_ci static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \ 22362306a36Sopenharmony_ci max197_show_range, \ 22462306a36Sopenharmony_ci max197_store_range, \ 22562306a36Sopenharmony_ci false, chan) 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci#define MAX197_SENSOR_DEV_ATTR_IN(chan) \ 22862306a36Sopenharmony_ci &sensor_dev_attr_in##chan##_input.dev_attr.attr, \ 22962306a36Sopenharmony_ci &sensor_dev_attr_in##chan##_max.dev_attr.attr, \ 23062306a36Sopenharmony_ci &sensor_dev_attr_in##chan##_min.dev_attr.attr 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(0); 23562306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(1); 23662306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(2); 23762306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(3); 23862306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(4); 23962306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(5); 24062306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(6); 24162306a36Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(7); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic const struct attribute_group max197_sysfs_group = { 24462306a36Sopenharmony_ci .attrs = (struct attribute *[]) { 24562306a36Sopenharmony_ci &dev_attr_name.attr, 24662306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(0), 24762306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(1), 24862306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(2), 24962306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(3), 25062306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(4), 25162306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(5), 25262306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(6), 25362306a36Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(7), 25462306a36Sopenharmony_ci NULL 25562306a36Sopenharmony_ci }, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int max197_probe(struct platform_device *pdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int ch, ret; 26162306a36Sopenharmony_ci struct max197_data *data; 26262306a36Sopenharmony_ci struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev); 26362306a36Sopenharmony_ci enum max197_chips chip = platform_get_device_id(pdev)->driver_data; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (pdata == NULL) { 26662306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data supplied\n"); 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (pdata->convert == NULL) { 27162306a36Sopenharmony_ci dev_err(&pdev->dev, "no convert function supplied\n"); 27262306a36Sopenharmony_ci return -EINVAL; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL); 27662306a36Sopenharmony_ci if (!data) 27762306a36Sopenharmony_ci return -ENOMEM; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci data->pdata = pdata; 28062306a36Sopenharmony_ci mutex_init(&data->lock); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (chip == max197) { 28362306a36Sopenharmony_ci data->limit = MAX197_LIMIT; 28462306a36Sopenharmony_ci data->scale = true; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci data->limit = MAX199_LIMIT; 28762306a36Sopenharmony_ci data->scale = false; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci for (ch = 0; ch < MAX197_NUM_CH; ch++) 29162306a36Sopenharmony_ci data->ctrl_bytes[ch] = (u8) ch; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group); 29662306a36Sopenharmony_ci if (ret) { 29762306a36Sopenharmony_ci dev_err(&pdev->dev, "sysfs create group failed\n"); 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&pdev->dev); 30262306a36Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 30362306a36Sopenharmony_ci ret = PTR_ERR(data->hwmon_dev); 30462306a36Sopenharmony_ci dev_err(&pdev->dev, "hwmon device register failed\n"); 30562306a36Sopenharmony_ci goto error; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cierror: 31162306a36Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int max197_remove(struct platform_device *pdev) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct max197_data *data = platform_get_drvdata(pdev); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 32062306a36Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic const struct platform_device_id max197_device_ids[] = { 32662306a36Sopenharmony_ci { "max197", max197 }, 32762306a36Sopenharmony_ci { "max199", max199 }, 32862306a36Sopenharmony_ci { } 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max197_device_ids); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic struct platform_driver max197_driver = { 33362306a36Sopenharmony_ci .driver = { 33462306a36Sopenharmony_ci .name = "max197", 33562306a36Sopenharmony_ci }, 33662306a36Sopenharmony_ci .probe = max197_probe, 33762306a36Sopenharmony_ci .remove = max197_remove, 33862306a36Sopenharmony_ci .id_table = max197_device_ids, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_cimodule_platform_driver(max197_driver); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 34362306a36Sopenharmony_ciMODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); 34462306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver"); 345