18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Maxim MAX197 A/D Converter driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Savoir-faire Linux Inc. 68c2ecf20Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * For further information, see the Documentation/hwmon/max197.rst file. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 218c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_data/max197.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MAX199_LIMIT 4000 /* 4V */ 268c2ecf20Sopenharmony_ci#define MAX197_LIMIT 10000 /* 10V */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Control byte format */ 318c2ecf20Sopenharmony_ci#define MAX197_BIP (1 << 3) /* Bipolarity */ 328c2ecf20Sopenharmony_ci#define MAX197_RNG (1 << 4) /* Full range */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MAX197_SCALE 12207 /* Scale coefficient for raw data */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* List of supported chips */ 378c2ecf20Sopenharmony_cienum max197_chips { max197, max199 }; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * struct max197_data - device instance specific data 418c2ecf20Sopenharmony_ci * @pdata: Platform data. 428c2ecf20Sopenharmony_ci * @hwmon_dev: The hwmon device. 438c2ecf20Sopenharmony_ci * @lock: Read/Write mutex. 448c2ecf20Sopenharmony_ci * @limit: Max range value (10V for MAX197, 4V for MAX199). 458c2ecf20Sopenharmony_ci * @scale: Need to scale. 468c2ecf20Sopenharmony_ci * @ctrl_bytes: Channels control byte. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistruct max197_data { 498c2ecf20Sopenharmony_ci struct max197_platform_data *pdata; 508c2ecf20Sopenharmony_ci struct device *hwmon_dev; 518c2ecf20Sopenharmony_ci struct mutex lock; 528c2ecf20Sopenharmony_ci int limit; 538c2ecf20Sopenharmony_ci bool scale; 548c2ecf20Sopenharmony_ci u8 ctrl_bytes[MAX197_NUM_CH]; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline void max197_set_unipolarity(struct max197_data *data, int channel) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci data->ctrl_bytes[channel] &= ~MAX197_BIP; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic inline void max197_set_bipolarity(struct max197_data *data, int channel) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci data->ctrl_bytes[channel] |= MAX197_BIP; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline void max197_set_half_range(struct max197_data *data, int channel) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci data->ctrl_bytes[channel] &= ~MAX197_RNG; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline void max197_set_full_range(struct max197_data *data, int channel) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci data->ctrl_bytes[channel] |= MAX197_RNG; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline bool max197_is_bipolar(struct max197_data *data, int channel) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return data->ctrl_bytes[channel] & MAX197_BIP; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline bool max197_is_full_range(struct max197_data *data, int channel) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return data->ctrl_bytes[channel] & MAX197_RNG; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */ 888c2ecf20Sopenharmony_cistatic ssize_t max197_show_range(struct device *dev, 898c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 928c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 938c2ecf20Sopenharmony_ci int channel = attr->index; 948c2ecf20Sopenharmony_ci bool is_min = attr->nr; 958c2ecf20Sopenharmony_ci int range; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 988c2ecf20Sopenharmony_ci return -ERESTARTSYS; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci range = max197_is_full_range(data, channel) ? 1018c2ecf20Sopenharmony_ci data->limit : data->limit / 2; 1028c2ecf20Sopenharmony_ci if (is_min) { 1038c2ecf20Sopenharmony_ci if (max197_is_bipolar(data, channel)) 1048c2ecf20Sopenharmony_ci range = -range; 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci range = 0; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", range); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */ 1158c2ecf20Sopenharmony_cistatic ssize_t max197_store_range(struct device *dev, 1168c2ecf20Sopenharmony_ci struct device_attribute *devattr, 1178c2ecf20Sopenharmony_ci const char *buf, size_t count) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 1208c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 1218c2ecf20Sopenharmony_ci int channel = attr->index; 1228c2ecf20Sopenharmony_ci bool is_min = attr->nr; 1238c2ecf20Sopenharmony_ci long value; 1248c2ecf20Sopenharmony_ci int half = data->limit / 2; 1258c2ecf20Sopenharmony_ci int full = data->limit; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (kstrtol(buf, 10, &value)) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (is_min) { 1318c2ecf20Sopenharmony_ci if (value <= -full) 1328c2ecf20Sopenharmony_ci value = -full; 1338c2ecf20Sopenharmony_ci else if (value < 0) 1348c2ecf20Sopenharmony_ci value = -half; 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci value = 0; 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci if (value >= full) 1398c2ecf20Sopenharmony_ci value = full; 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci value = half; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 1458c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (value == 0) { 1488c2ecf20Sopenharmony_ci /* We can deduce only the polarity */ 1498c2ecf20Sopenharmony_ci max197_set_unipolarity(data, channel); 1508c2ecf20Sopenharmony_ci } else if (value == -half) { 1518c2ecf20Sopenharmony_ci max197_set_bipolarity(data, channel); 1528c2ecf20Sopenharmony_ci max197_set_half_range(data, channel); 1538c2ecf20Sopenharmony_ci } else if (value == -full) { 1548c2ecf20Sopenharmony_ci max197_set_bipolarity(data, channel); 1558c2ecf20Sopenharmony_ci max197_set_full_range(data, channel); 1568c2ecf20Sopenharmony_ci } else if (value == half) { 1578c2ecf20Sopenharmony_ci /* We can deduce only the range */ 1588c2ecf20Sopenharmony_ci max197_set_half_range(data, channel); 1598c2ecf20Sopenharmony_ci } else if (value == full) { 1608c2ecf20Sopenharmony_ci /* We can deduce only the range */ 1618c2ecf20Sopenharmony_ci max197_set_full_range(data, channel); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return count; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */ 1708c2ecf20Sopenharmony_cistatic ssize_t max197_show_input(struct device *dev, 1718c2ecf20Sopenharmony_ci struct device_attribute *devattr, 1728c2ecf20Sopenharmony_ci char *buf) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct max197_data *data = dev_get_drvdata(dev); 1758c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1768c2ecf20Sopenharmony_ci int channel = attr->index; 1778c2ecf20Sopenharmony_ci s32 value; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&data->lock)) 1818c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ret = data->pdata->convert(data->ctrl_bytes[channel]); 1848c2ecf20Sopenharmony_ci if (ret < 0) { 1858c2ecf20Sopenharmony_ci dev_err(dev, "conversion failed\n"); 1868c2ecf20Sopenharmony_ci goto unlock; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci value = ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * Coefficient to apply on raw value. 1928c2ecf20Sopenharmony_ci * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci if (data->scale) { 1958c2ecf20Sopenharmony_ci value *= MAX197_SCALE; 1968c2ecf20Sopenharmony_ci if (max197_is_full_range(data, channel)) 1978c2ecf20Sopenharmony_ci value *= 2; 1988c2ecf20Sopenharmony_ci value /= 10000; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = sprintf(buf, "%d\n", value); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciunlock: 2048c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 2098c2ecf20Sopenharmony_ci char *buf) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 2128c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", pdev->name); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \ 2168c2ecf20Sopenharmony_ci static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \ 2178c2ecf20Sopenharmony_ci max197_show_input, NULL, chan); \ 2188c2ecf20Sopenharmony_ci static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \ 2198c2ecf20Sopenharmony_ci max197_show_range, \ 2208c2ecf20Sopenharmony_ci max197_store_range, \ 2218c2ecf20Sopenharmony_ci true, chan); \ 2228c2ecf20Sopenharmony_ci static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \ 2238c2ecf20Sopenharmony_ci max197_show_range, \ 2248c2ecf20Sopenharmony_ci max197_store_range, \ 2258c2ecf20Sopenharmony_ci false, chan) 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci#define MAX197_SENSOR_DEV_ATTR_IN(chan) \ 2288c2ecf20Sopenharmony_ci &sensor_dev_attr_in##chan##_input.dev_attr.attr, \ 2298c2ecf20Sopenharmony_ci &sensor_dev_attr_in##chan##_max.dev_attr.attr, \ 2308c2ecf20Sopenharmony_ci &sensor_dev_attr_in##chan##_min.dev_attr.attr 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(0); 2358c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(1); 2368c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(2); 2378c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(3); 2388c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(4); 2398c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(5); 2408c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(6); 2418c2ecf20Sopenharmony_ciMAX197_SENSOR_DEVICE_ATTR_CH(7); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct attribute_group max197_sysfs_group = { 2448c2ecf20Sopenharmony_ci .attrs = (struct attribute *[]) { 2458c2ecf20Sopenharmony_ci &dev_attr_name.attr, 2468c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(0), 2478c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(1), 2488c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(2), 2498c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(3), 2508c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(4), 2518c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(5), 2528c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(6), 2538c2ecf20Sopenharmony_ci MAX197_SENSOR_DEV_ATTR_IN(7), 2548c2ecf20Sopenharmony_ci NULL 2558c2ecf20Sopenharmony_ci }, 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int max197_probe(struct platform_device *pdev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int ch, ret; 2618c2ecf20Sopenharmony_ci struct max197_data *data; 2628c2ecf20Sopenharmony_ci struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev); 2638c2ecf20Sopenharmony_ci enum max197_chips chip = platform_get_device_id(pdev)->driver_data; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (pdata == NULL) { 2668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data supplied\n"); 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (pdata->convert == NULL) { 2718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no convert function supplied\n"); 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL); 2768c2ecf20Sopenharmony_ci if (!data) 2778c2ecf20Sopenharmony_ci return -ENOMEM; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci data->pdata = pdata; 2808c2ecf20Sopenharmony_ci mutex_init(&data->lock); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (chip == max197) { 2838c2ecf20Sopenharmony_ci data->limit = MAX197_LIMIT; 2848c2ecf20Sopenharmony_ci data->scale = true; 2858c2ecf20Sopenharmony_ci } else { 2868c2ecf20Sopenharmony_ci data->limit = MAX199_LIMIT; 2878c2ecf20Sopenharmony_ci data->scale = false; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (ch = 0; ch < MAX197_NUM_CH; ch++) 2918c2ecf20Sopenharmony_ci data->ctrl_bytes[ch] = (u8) ch; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group); 2968c2ecf20Sopenharmony_ci if (ret) { 2978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "sysfs create group failed\n"); 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(&pdev->dev); 3028c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 3038c2ecf20Sopenharmony_ci ret = PTR_ERR(data->hwmon_dev); 3048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "hwmon device register failed\n"); 3058c2ecf20Sopenharmony_ci goto error; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cierror: 3118c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int max197_remove(struct platform_device *pdev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct max197_data *data = platform_get_drvdata(pdev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 3208c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const struct platform_device_id max197_device_ids[] = { 3268c2ecf20Sopenharmony_ci { "max197", max197 }, 3278c2ecf20Sopenharmony_ci { "max199", max199 }, 3288c2ecf20Sopenharmony_ci { } 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max197_device_ids); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct platform_driver max197_driver = { 3338c2ecf20Sopenharmony_ci .driver = { 3348c2ecf20Sopenharmony_ci .name = "max197", 3358c2ecf20Sopenharmony_ci }, 3368c2ecf20Sopenharmony_ci .probe = max197_probe, 3378c2ecf20Sopenharmony_ci .remove = max197_remove, 3388c2ecf20Sopenharmony_ci .id_table = max197_device_ids, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_cimodule_platform_driver(max197_driver); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); 3448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver"); 345