linux/drivers/hwmon/iio_hwmon.c
<<
>>
Prefs
   1/* Hwmon client for industrial I/O devices
   2 *
   3 * Copyright (c) 2011 Jonathan Cameron
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published by
   7 * the Free Software Foundation.
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/slab.h>
  12#include <linux/module.h>
  13#include <linux/err.h>
  14#include <linux/platform_device.h>
  15#include <linux/hwmon.h>
  16#include <linux/of.h>
  17#include <linux/hwmon-sysfs.h>
  18#include <linux/iio/consumer.h>
  19#include <linux/iio/types.h>
  20
  21/**
  22 * struct iio_hwmon_state - device instance state
  23 * @channels:           filled with array of channels from iio
  24 * @num_channels:       number of channels in channels (saves counting twice)
  25 * @hwmon_dev:          associated hwmon device
  26 * @attr_group:         the group of attributes
  27 * @groups:             null terminated array of attribute groups
  28 * @attrs:              null terminated array of attribute pointers.
  29 */
  30struct iio_hwmon_state {
  31        struct iio_channel *channels;
  32        int num_channels;
  33        struct device *hwmon_dev;
  34        struct attribute_group attr_group;
  35        const struct attribute_group *groups[2];
  36        struct attribute **attrs;
  37};
  38
  39/*
  40 * Assumes that IIO and hwmon operate in the same base units.
  41 * This is supposed to be true, but needs verification for
  42 * new channel types.
  43 */
  44static ssize_t iio_hwmon_read_val(struct device *dev,
  45                                  struct device_attribute *attr,
  46                                  char *buf)
  47{
  48        int result;
  49        int ret;
  50        struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
  51        struct iio_hwmon_state *state = dev_get_drvdata(dev);
  52
  53        ret = iio_read_channel_processed(&state->channels[sattr->index],
  54                                        &result);
  55        if (ret < 0)
  56                return ret;
  57
  58        return sprintf(buf, "%d\n", result);
  59}
  60
  61static int iio_hwmon_probe(struct platform_device *pdev)
  62{
  63        struct device *dev = &pdev->dev;
  64        struct iio_hwmon_state *st;
  65        struct sensor_device_attribute *a;
  66        int ret, i;
  67        int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
  68        enum iio_chan_type type;
  69        struct iio_channel *channels;
  70        const char *name = "iio_hwmon";
  71        char *sname;
  72
  73        if (dev->of_node && dev->of_node->name)
  74                name = dev->of_node->name;
  75
  76        channels = iio_channel_get_all(dev);
  77        if (IS_ERR(channels)) {
  78                if (PTR_ERR(channels) == -ENODEV)
  79                        return -EPROBE_DEFER;
  80                return PTR_ERR(channels);
  81        }
  82
  83        st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
  84        if (st == NULL) {
  85                ret = -ENOMEM;
  86                goto error_release_channels;
  87        }
  88
  89        st->channels = channels;
  90
  91        /* count how many attributes we have */
  92        while (st->channels[st->num_channels].indio_dev)
  93                st->num_channels++;
  94
  95        st->attrs = devm_kcalloc(dev,
  96                                 st->num_channels + 1, sizeof(*st->attrs),
  97                                 GFP_KERNEL);
  98        if (st->attrs == NULL) {
  99                ret = -ENOMEM;
 100                goto error_release_channels;
 101        }
 102
 103        for (i = 0; i < st->num_channels; i++) {
 104                a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL);
 105                if (a == NULL) {
 106                        ret = -ENOMEM;
 107                        goto error_release_channels;
 108                }
 109
 110                sysfs_attr_init(&a->dev_attr.attr);
 111                ret = iio_get_channel_type(&st->channels[i], &type);
 112                if (ret < 0)
 113                        goto error_release_channels;
 114
 115                switch (type) {
 116                case IIO_VOLTAGE:
 117                        a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
 118                                                               "in%d_input",
 119                                                               in_i++);
 120                        break;
 121                case IIO_TEMP:
 122                        a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
 123                                                               "temp%d_input",
 124                                                               temp_i++);
 125                        break;
 126                case IIO_CURRENT:
 127                        a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
 128                                                               "curr%d_input",
 129                                                               curr_i++);
 130                        break;
 131                case IIO_HUMIDITYRELATIVE:
 132                        a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
 133                                                               "humidity%d_input",
 134                                                               humidity_i++);
 135                        break;
 136                default:
 137                        ret = -EINVAL;
 138                        goto error_release_channels;
 139                }
 140                if (a->dev_attr.attr.name == NULL) {
 141                        ret = -ENOMEM;
 142                        goto error_release_channels;
 143                }
 144                a->dev_attr.show = iio_hwmon_read_val;
 145                a->dev_attr.attr.mode = S_IRUGO;
 146                a->index = i;
 147                st->attrs[i] = &a->dev_attr.attr;
 148        }
 149
 150        st->attr_group.attrs = st->attrs;
 151        st->groups[0] = &st->attr_group;
 152
 153        sname = devm_kstrdup(dev, name, GFP_KERNEL);
 154        if (!sname) {
 155                ret = -ENOMEM;
 156                goto error_release_channels;
 157        }
 158
 159        strreplace(sname, '-', '_');
 160        st->hwmon_dev = hwmon_device_register_with_groups(dev, sname, st,
 161                                                          st->groups);
 162        if (IS_ERR(st->hwmon_dev)) {
 163                ret = PTR_ERR(st->hwmon_dev);
 164                goto error_release_channels;
 165        }
 166        platform_set_drvdata(pdev, st);
 167        return 0;
 168
 169error_release_channels:
 170        iio_channel_release_all(channels);
 171        return ret;
 172}
 173
 174static int iio_hwmon_remove(struct platform_device *pdev)
 175{
 176        struct iio_hwmon_state *st = platform_get_drvdata(pdev);
 177
 178        hwmon_device_unregister(st->hwmon_dev);
 179        iio_channel_release_all(st->channels);
 180
 181        return 0;
 182}
 183
 184static const struct of_device_id iio_hwmon_of_match[] = {
 185        { .compatible = "iio-hwmon", },
 186        { }
 187};
 188MODULE_DEVICE_TABLE(of, iio_hwmon_of_match);
 189
 190static struct platform_driver __refdata iio_hwmon_driver = {
 191        .driver = {
 192                .name = "iio_hwmon",
 193                .of_match_table = iio_hwmon_of_match,
 194        },
 195        .probe = iio_hwmon_probe,
 196        .remove = iio_hwmon_remove,
 197};
 198
 199module_platform_driver(iio_hwmon_driver);
 200
 201MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
 202MODULE_DESCRIPTION("IIO to hwmon driver");
 203MODULE_LICENSE("GPL v2");
 204