2023-07-10 linux IIO子系统使用学习,在TI 的ads1015驱动里面看到相关使用,故花点时间进行简单的学习,入门级别,纪录点滴。

一、Linux IIO(Industrial I/O)架构是Linux内核提供的一种用于支持各种类型传感器和数据采集设备的子系统,包括温度、压力、湿度、加速度、光度等多种传感器。

二、这个就是ads1015的驱动,里面用到iio子系统。

ti-ads1015.c « adc « iio « drivers - kernel/git/torvalds/linux.git - Linux kernel source tree

三、rk的adc 也是用iio ,简单来分析adc 按键调用iio 获取adc值的例子。

     3.1 adc 的 iio 驱动 kernel\drivers\iio\adc\rockchip_saradc.c,可以简单理解为提供iio接口给其他的驱动调用。

/** Rockchip Successive Approximation Register (SAR) A/D Converter* Copyright (C) 2014 ROCKCHIP, Inc.** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.*/#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>#define SARADC_DATA			0x00#define SARADC_STAS			0x04
#define SARADC_STAS_BUSY		BIT(0)#define SARADC_CTRL			0x08
#define SARADC_CTRL_IRQ_STATUS		BIT(6)
#define SARADC_CTRL_IRQ_ENABLE		BIT(5)
#define SARADC_CTRL_POWER_CTRL		BIT(3)
#define SARADC_CTRL_CHN_MASK		0x7#define SARADC_DLY_PU_SOC		0x0c
#define SARADC_DLY_PU_SOC_MASK		0x3f#define SARADC_TIMEOUT			msecs_to_jiffies(100)struct rockchip_saradc_data {int				num_bits;const struct iio_chan_spec	*channels;int				num_channels;unsigned long			clk_rate;
};struct rockchip_saradc {void __iomem		*regs;struct clk		*pclk;struct clk		*clk;struct completion	completion;struct regulator	*vref;int			uv_vref;struct reset_control	*reset;const struct rockchip_saradc_data *data;u16			last_val;
};static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask)
{struct rockchip_saradc *info = iio_priv(indio_dev);switch (mask) {case IIO_CHAN_INFO_RAW:mutex_lock(&indio_dev->mlock);reinit_completion(&info->completion);/* 8 clock periods as delay between power up and start cmd */writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);/* Select the channel to be used and trigger conversion */writel(SARADC_CTRL_POWER_CTRL| (chan->channel & SARADC_CTRL_CHN_MASK)| SARADC_CTRL_IRQ_ENABLE,info->regs + SARADC_CTRL);if (!wait_for_completion_timeout(&info->completion,SARADC_TIMEOUT)) {writel_relaxed(0, info->regs + SARADC_CTRL);mutex_unlock(&indio_dev->mlock);return -ETIMEDOUT;}*val = info->last_val;mutex_unlock(&indio_dev->mlock);return IIO_VAL_INT;case IIO_CHAN_INFO_SCALE:/* It is a dummy regulator */if (info->uv_vref < 0)return info->uv_vref;*val = info->uv_vref / 1000;*val2 = info->data->num_bits;return IIO_VAL_FRACTIONAL_LOG2;default:return -EINVAL;}
}static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
{struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;/* Read value */info->last_val = readl_relaxed(info->regs + SARADC_DATA);info->last_val &= GENMASK(info->data->num_bits - 1, 0);/* Clear irq & power down adc */writel_relaxed(0, info->regs + SARADC_CTRL);complete(&info->completion);return IRQ_HANDLED;
}static const struct iio_info rockchip_saradc_iio_info = {.read_raw = rockchip_saradc_read_raw,.driver_module = THIS_MODULE,
};#define ADC_CHANNEL(_index, _id) {				\.type = IIO_VOLTAGE,					\.indexed = 1,						\.channel = _index,					\.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\.datasheet_name = _id,					\
}static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {ADC_CHANNEL(0, "adc0"),ADC_CHANNEL(1, "adc1"),ADC_CHANNEL(2, "adc2"),
};static const struct rockchip_saradc_data saradc_data = {.num_bits = 10,.channels = rockchip_saradc_iio_channels,.num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),.clk_rate = 1000000,
};static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {ADC_CHANNEL(0, "adc0"),ADC_CHANNEL(1, "adc1"),
};static const struct rockchip_saradc_data rk3066_tsadc_data = {.num_bits = 12,.channels = rockchip_rk3066_tsadc_iio_channels,.num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),.clk_rate = 50000,
};static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = {ADC_CHANNEL(0, "adc0"),ADC_CHANNEL(1, "adc1"),ADC_CHANNEL(2, "adc2"),ADC_CHANNEL(3, "adc3"),ADC_CHANNEL(4, "adc4"),ADC_CHANNEL(5, "adc5"),
};static const struct rockchip_saradc_data rk3399_saradc_data = {.num_bits = 10,.channels = rockchip_rk3399_saradc_iio_channels,.num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels),.clk_rate = 1000000,
};static const struct of_device_id rockchip_saradc_match[] = {{.compatible = "rockchip,saradc",.data = &saradc_data,}, {.compatible = "rockchip,rk3066-tsadc",.data = &rk3066_tsadc_data,}, {.compatible = "rockchip,rk3399-saradc",.data = &rk3399_saradc_data,},{},
};
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);/*** Reset SARADC Controller.*/
static void rockchip_saradc_reset_controller(struct reset_control *reset)
{reset_control_assert(reset);usleep_range(10, 20);reset_control_deassert(reset);
}static int rockchip_saradc_probe(struct platform_device *pdev)
{struct rockchip_saradc *info = NULL;struct device_node *np = pdev->dev.of_node;struct iio_dev *indio_dev = NULL;struct resource	*mem;const struct of_device_id *match;int ret;int irq;if (!np)return -ENODEV;indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));if (!indio_dev) {dev_err(&pdev->dev, "failed allocating iio device\n");return -ENOMEM;}info = iio_priv(indio_dev);match = of_match_device(rockchip_saradc_match, &pdev->dev);info->data = match->data;mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);info->regs = devm_ioremap_resource(&pdev->dev, mem);if (IS_ERR(info->regs))return PTR_ERR(info->regs);/** The reset should be an optional property, as it should work* with old devicetrees as well*/info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");if (IS_ERR(info->reset)) {ret = PTR_ERR(info->reset);if (ret != -ENOENT)return ret;dev_dbg(&pdev->dev, "no reset control found\n");info->reset = NULL;}init_completion(&info->completion);irq = platform_get_irq(pdev, 0);if (irq < 0) {dev_err(&pdev->dev, "no irq resource?\n");return irq;}ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,0, dev_name(&pdev->dev), info);if (ret < 0) {dev_err(&pdev->dev, "failed requesting irq %d\n", irq);return ret;}info->pclk = devm_clk_get(&pdev->dev, "apb_pclk");if (IS_ERR(info->pclk)) {dev_err(&pdev->dev, "failed to get pclk\n");return PTR_ERR(info->pclk);}info->clk = devm_clk_get(&pdev->dev, "saradc");if (IS_ERR(info->clk)) {dev_err(&pdev->dev, "failed to get adc clock\n");return PTR_ERR(info->clk);}info->vref = devm_regulator_get(&pdev->dev, "vref");if (IS_ERR(info->vref)) {dev_err(&pdev->dev, "failed to get regulator, %ld\n",PTR_ERR(info->vref));return PTR_ERR(info->vref);}if (info->reset)rockchip_saradc_reset_controller(info->reset);/** Use a default value for the converter clock.* This may become user-configurable in the future.*/ret = clk_set_rate(info->clk, info->data->clk_rate);if (ret < 0) {dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);return ret;}ret = regulator_enable(info->vref);if (ret < 0) {dev_err(&pdev->dev, "failed to enable vref regulator\n");return ret;}info->uv_vref = regulator_get_voltage(info->vref);ret = clk_prepare_enable(info->pclk);if (ret < 0) {dev_err(&pdev->dev, "failed to enable pclk\n");goto err_reg_voltage;}ret = clk_prepare_enable(info->clk);if (ret < 0) {dev_err(&pdev->dev, "failed to enable converter clock\n");goto err_pclk;}platform_set_drvdata(pdev, indio_dev);indio_dev->name = dev_name(&pdev->dev);indio_dev->dev.parent = &pdev->dev;indio_dev->dev.of_node = pdev->dev.of_node;indio_dev->info = &rockchip_saradc_iio_info;indio_dev->modes = INDIO_DIRECT_MODE;indio_dev->channels = info->data->channels;indio_dev->num_channels = info->data->num_channels;ret = iio_device_register(indio_dev);if (ret)goto err_clk;return 0;err_clk:clk_disable_unprepare(info->clk);
err_pclk:clk_disable_unprepare(info->pclk);
err_reg_voltage:regulator_disable(info->vref);return ret;
}static int rockchip_saradc_remove(struct platform_device *pdev)
{struct iio_dev *indio_dev = platform_get_drvdata(pdev);struct rockchip_saradc *info = iio_priv(indio_dev);iio_device_unregister(indio_dev);clk_disable_unprepare(info->clk);clk_disable_unprepare(info->pclk);regulator_disable(info->vref);return 0;
}#ifdef CONFIG_PM_SLEEP
static int rockchip_saradc_suspend(struct device *dev)
{struct iio_dev *indio_dev = dev_get_drvdata(dev);struct rockchip_saradc *info = iio_priv(indio_dev);clk_disable_unprepare(info->clk);clk_disable_unprepare(info->pclk);regulator_disable(info->vref);return 0;
}static int rockchip_saradc_resume(struct device *dev)
{struct iio_dev *indio_dev = dev_get_drvdata(dev);struct rockchip_saradc *info = iio_priv(indio_dev);int ret;ret = regulator_enable(info->vref);if (ret)return ret;ret = clk_prepare_enable(info->pclk);if (ret)return ret;ret = clk_prepare_enable(info->clk);if (ret)return ret;return ret;
}
#endifstatic SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,rockchip_saradc_suspend, rockchip_saradc_resume);static struct platform_driver rockchip_saradc_driver = {.probe		= rockchip_saradc_probe,.remove		= rockchip_saradc_remove,.driver		= {.name	= "rockchip-saradc",.of_match_table = rockchip_saradc_match,.pm	= &rockchip_saradc_pm_ops,},
};module_platform_driver(rockchip_saradc_driver);MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
MODULE_DESCRIPTION("Rockchip SARADC driver");
MODULE_LICENSE("GPL v2");

        3.2 kernel\drivers\input\keyboard\rk_keys.c 里面通过iio_channel_get 和iio_read_channel_raw 去读取。最简单的理解其实就是调iio驱动。

/** Driver for keys on GPIO lines capable of generating interrupts.** Copyright (C) 2015, Fuzhou Rockchip Electronics Co., Ltd* Copyright 2005 Phil Blundell** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*/#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/wakelock.h>#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iio/consumer.h>#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/rk_keys.h>#define EMPTY_DEFAULT_ADVALUE		1024
#define DRIFT_DEFAULT_ADVALUE		70
#define INVALID_ADVALUE			-1
#define EV_ENCALL			KEY_F4
#define EV_MENU				KEY_F1#if 0
#define key_dbg(bdata, format, arg...)		\dev_info(&bdata->input->dev, format, ##arg)
#else
#define key_dbg(bdata, format, arg...)
#endif#define DEBOUNCE_JIFFIES	(10 / (MSEC_PER_SEC / HZ))	/* 10ms */
#define ADC_SAMPLE_JIFFIES	(100 / (MSEC_PER_SEC / HZ))	/* 100ms */
#define WAKE_LOCK_JIFFIES	(1 * HZ)			/* 1s */enum rk_key_type {TYPE_GPIO = 1,TYPE_ADC
};struct rk_keys_button {struct device *dev;u32 type;		/* TYPE_GPIO, TYPE_ADC */u32 code;		/* key code */const char *desc;	/* key label */u32 state;		/* key up & down state */int gpio;		/* gpio only */int adc_value;		/* adc only */int adc_state;		/* adc only */int active_low;		/* gpio only */int wakeup;		/* gpio only */struct timer_list timer;
};struct rk_keys_drvdata {int nbuttons;/* flag to indicate if we're suspending/resuming */bool in_suspend;int result;int rep;int drift_advalue;struct wake_lock wake_lock;struct input_dev *input;struct delayed_work adc_poll_work;struct iio_channel *chan;struct rk_keys_button button[0];
};static struct input_dev *sinput_dev;void rk_send_power_key(int state)
{if (!sinput_dev)return;if (state) {input_report_key(sinput_dev, KEY_POWER, 1);input_sync(sinput_dev);} else {input_report_key(sinput_dev, KEY_POWER, 0);input_sync(sinput_dev);}
}
EXPORT_SYMBOL(rk_send_power_key);void rk_send_wakeup_key(void)
{if (!sinput_dev)return;input_report_key(sinput_dev, KEY_WAKEUP, 1);input_sync(sinput_dev);input_report_key(sinput_dev, KEY_WAKEUP, 0);input_sync(sinput_dev);
}
EXPORT_SYMBOL(rk_send_wakeup_key);static void keys_timer(unsigned long _data)
{struct rk_keys_button *button = (struct rk_keys_button *)_data;struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);struct input_dev *input = pdata->input;int state;if (button->type == TYPE_GPIO)state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^button->active_low);elsestate = !!button->adc_state;if (button->state != state) {button->state = state;input_event(input, EV_KEY, button->code, button->state);key_dbg(pdata, "%skey[%s]: report event[%d] state[%d]\n",button->type == TYPE_ADC ? "adc" : "gpio",button->desc, button->code, button->state);input_event(input, EV_KEY, button->code, button->state);input_sync(input);}if (state)mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);
}static irqreturn_t keys_isr(int irq, void *dev_id)
{struct rk_keys_button *button = (struct rk_keys_button *)dev_id;struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);struct input_dev *input = pdata->input;BUG_ON(irq != gpio_to_irq(button->gpio));if (button->wakeup && pdata->in_suspend) {button->state = 1;key_dbg(pdata,"wakeup: %skey[%s]: report event[%d] state[%d]\n",(button->type == TYPE_ADC) ? "adc" : "gpio",button->desc, button->code, button->state);input_event(input, EV_KEY, button->code, button->state);input_sync(input);}if (button->wakeup)wake_lock_timeout(&pdata->wake_lock, WAKE_LOCK_JIFFIES);mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);return IRQ_HANDLED;
}/*
static ssize_t adc_value_show(struct device *dev, struct device_attribute *attr,char *buf)
{struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);return sprintf(buf, "adc_value: %d\n", ddata->result);
}
static DEVICE_ATTR(get_adc_value, S_IRUGO | S_IWUSR, adc_value_show, NULL);
*/static const struct of_device_id rk_key_match[] = {{ .compatible = "rockchip,key", .data = NULL},{},
};
MODULE_DEVICE_TABLE(of, rk_key_match);static int rk_key_adc_iio_read(struct rk_keys_drvdata *data)
{struct iio_channel *channel = data->chan;int val, ret;if (!channel)return INVALID_ADVALUE;ret = iio_read_channel_raw(channel, &val);if (ret < 0) {pr_err("read channel() error: %d\n", ret);return ret;}return val;
}static void adc_key_poll(struct work_struct *work)
{struct rk_keys_drvdata *ddata;int i, result = -1;ddata = container_of(work, struct rk_keys_drvdata, adc_poll_work.work);if (!ddata->in_suspend) {result = rk_key_adc_iio_read(ddata);if (result > INVALID_ADVALUE &&result < (EMPTY_DEFAULT_ADVALUE - ddata->drift_advalue))ddata->result = result;for (i = 0; i < ddata->nbuttons; i++) {struct rk_keys_button *button = &ddata->button[i];if (!button->adc_value)continue;if (result < button->adc_value + ddata->drift_advalue &&result > button->adc_value - ddata->drift_advalue)button->adc_state = 1;elsebutton->adc_state = 0;if (button->state != button->adc_state)mod_timer(&button->timer,jiffies + DEBOUNCE_JIFFIES);}}schedule_delayed_work(&ddata->adc_poll_work, ADC_SAMPLE_JIFFIES);
}static int rk_key_type_get(struct device_node *node,struct rk_keys_button *button)
{u32 adc_value;if (!of_property_read_u32(node, "rockchip,adc_value", &adc_value))return TYPE_ADC;else if (of_get_gpio(node, 0) >= 0)return TYPE_GPIO;elsereturn -1;
}static int rk_keys_parse_dt(struct rk_keys_drvdata *pdata,struct platform_device *pdev)
{struct device_node *node = pdev->dev.of_node;struct device_node *child_node;struct iio_channel *chan;int ret, gpio, i = 0;u32 code, adc_value, flags, drift;if (of_property_read_u32(node, "adc-drift", &drift))pdata->drift_advalue = DRIFT_DEFAULT_ADVALUE;elsepdata->drift_advalue = (int)drift;chan = iio_channel_get(&pdev->dev, NULL);if (IS_ERR(chan)) {dev_info(&pdev->dev, "no io-channels defined\n");chan = NULL;}pdata->chan = chan;for_each_child_of_node(node, child_node) {if (of_property_read_u32(child_node, "linux,code", &code)) {dev_err(&pdev->dev,"Missing linux,code property in the DT.\n");ret = -EINVAL;goto error_ret;}pdata->button[i].code = code;pdata->button[i].desc =of_get_property(child_node, "label", NULL);pdata->button[i].type =rk_key_type_get(child_node, &pdata->button[i]);switch (pdata->button[i].type) {case TYPE_GPIO:gpio = of_get_gpio_flags(child_node, 0, &flags);if (gpio < 0) {ret = gpio;if (ret != -EPROBE_DEFER)dev_err(&pdev->dev,"Failed to get gpio flags, error: %d\n",ret);goto error_ret;}pdata->button[i].gpio = gpio;pdata->button[i].active_low =flags & OF_GPIO_ACTIVE_LOW;pdata->button[i].wakeup =!!of_get_property(child_node, "gpio-key,wakeup",NULL);break;case TYPE_ADC:if (of_property_read_u32(child_node, "rockchip,adc_value", &adc_value)) {dev_err(&pdev->dev,"Missing rockchip,adc_value property in the DT.\n");ret = -EINVAL;goto error_ret;}pdata->button[i].adc_value = adc_value;break;default:dev_err(&pdev->dev,"Error rockchip,type property in the DT.\n");ret = -EINVAL;goto error_ret;}i++;}return 0;error_ret:return ret;
}static int keys_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct device_node *np = pdev->dev.of_node;struct rk_keys_drvdata *ddata = NULL;struct input_dev *input = NULL;int i, error = 0;int wakeup, key_num = 0;key_num = of_get_child_count(np);if (key_num == 0)dev_info(&pdev->dev, "no key defined\n");ddata = devm_kzalloc(dev, sizeof(struct rk_keys_drvdata) +key_num * sizeof(struct rk_keys_button),GFP_KERNEL);input = devm_input_allocate_device(dev);if (!ddata || !input) {error = -ENOMEM;return error;}platform_set_drvdata(pdev, ddata);dev_set_drvdata(&pdev->dev, ddata);input->name = "rk29-keypad";	/* pdev->name; */input->phys = "gpio-keys/input0";input->dev.parent = dev;input->id.bustype = BUS_HOST;input->id.vendor = 0x0001;input->id.product = 0x0001;input->id.version = 0x0100;ddata->input = input;/* parse info from dt */ddata->nbuttons = key_num;error = rk_keys_parse_dt(ddata, pdev);if (error)goto fail0;/* Enable auto repeat feature of Linux input subsystem */if (ddata->rep)__set_bit(EV_REP, input->evbit);error = input_register_device(input);if (error) {pr_err("gpio-keys: Unable to register input device, error: %d\n",error);goto fail0;}sinput_dev = input;for (i = 0; i < ddata->nbuttons; i++) {struct rk_keys_button *button = &ddata->button[i];if (button->code) {setup_timer(&button->timer,keys_timer, (unsigned long)button);}if (button->wakeup)wakeup = 1;input_set_capability(input, EV_KEY, button->code);}wake_lock_init(&ddata->wake_lock, WAKE_LOCK_SUSPEND, input->name);device_init_wakeup(dev, wakeup);for (i = 0; i < ddata->nbuttons; i++) {struct rk_keys_button *button = &ddata->button[i];button->dev = &pdev->dev;if (button->type == TYPE_GPIO) {int irq;error =devm_gpio_request(dev, button->gpio,button->desc ? : "keys");if (error < 0) {pr_err("gpio-keys: failed to request GPIO %d, error %d\n",button->gpio, error);goto fail1;}error = gpio_direction_input(button->gpio);if (error < 0) {pr_err("gpio-keys: failed to configure input direction for GPIO %d, error %d\n",button->gpio, error);gpio_free(button->gpio);goto fail1;}irq = gpio_to_irq(button->gpio);if (irq < 0) {error = irq;pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %d\n",button->gpio, error);gpio_free(button->gpio);goto fail1;}error = devm_request_irq(dev, irq, keys_isr,button->active_low ?IRQF_TRIGGER_FALLING :IRQF_TRIGGER_RISING,button->desc ?button->desc : "keys",button);if (error) {pr_err("gpio-keys: Unable to claim irq %d; error %d\n",irq, error);gpio_free(button->gpio);goto fail1;}}}input_set_capability(input, EV_KEY, KEY_WAKEUP);/* adc polling work */if (ddata->chan) {INIT_DELAYED_WORK(&ddata->adc_poll_work, adc_key_poll);schedule_delayed_work(&ddata->adc_poll_work,ADC_SAMPLE_JIFFIES);}return error;fail1:while (--i >= 0)del_timer_sync(&ddata->button[i].timer);device_init_wakeup(dev, 0);wake_lock_destroy(&ddata->wake_lock);
fail0:platform_set_drvdata(pdev, NULL);return error;
}static int keys_remove(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);struct input_dev *input = ddata->input;int i;device_init_wakeup(dev, 0);for (i = 0; i < ddata->nbuttons; i++)del_timer_sync(&ddata->button[i].timer);if (ddata->chan)cancel_delayed_work_sync(&ddata->adc_poll_work);input_unregister_device(input);wake_lock_destroy(&ddata->wake_lock);sinput_dev = NULL;return 0;
}#ifdef CONFIG_PM
static int keys_suspend(struct device *dev)
{struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);int i;ddata->in_suspend = true;if (device_may_wakeup(dev)) {for (i = 0; i < ddata->nbuttons; i++) {struct rk_keys_button *button = ddata->button + i;if (button->wakeup)enable_irq_wake(gpio_to_irq(button->gpio));}}return 0;
}static int keys_resume(struct device *dev)
{struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);int i;if (device_may_wakeup(dev)) {for (i = 0; i < ddata->nbuttons; i++) {struct rk_keys_button *button = ddata->button + i;if (button->wakeup)disable_irq_wake(gpio_to_irq(button->gpio));}preempt_disable();/* for call resend_irqs, which may call keys_isr */if (local_softirq_pending())do_softirq();preempt_enable_no_resched();}ddata->in_suspend = false;return 0;
}static const struct dev_pm_ops keys_pm_ops = {.suspend	= keys_suspend,.resume		= keys_resume,
};
#endifstatic struct platform_driver keys_device_driver = {.probe		= keys_probe,.remove		= keys_remove,.driver		= {.name	= "rk-keypad",.owner	= THIS_MODULE,.of_match_table = rk_key_match,
#ifdef CONFIG_PM.pm	= &keys_pm_ops,
#endif}
};static int __init rk_keys_driver_init(void)
{return platform_driver_register(&keys_device_driver);
}static void __exit rk_keys_driver_exit(void)
{platform_driver_unregister(&keys_device_driver);
}late_initcall_sync(rk_keys_driver_init);
module_exit(rk_keys_driver_exit);

        3.3 dts 通过io-channels = <&saradc 2>; 设置通道

saradc: saradc@ff288000 {compatible = "rockchip,px30-saradc", "rockchip,rk3399-saradc";reg = <0x0 0xff288000 0x0 0x100>;interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;#io-channel-cells = <1>;clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;clock-names = "saradc", "apb_pclk";resets = <&cru SRST_SARADC_P>;reset-names = "saradc-apb";};adc-keys {compatible = "adc-keys";io-channels = <&saradc 2>;io-channel-names = "buttons";poll-interval = <100>;keyup-threshold-microvolt = <1800000>;esc-key {linux,code = <KEY_ESC>;label = "esc";press-threshold-microvolt = <1310000>;};home-key {linux,code = <KEY_HOME>;label = "home";press-threshold-microvolt = <624000>;};menu-key {linux,code = <KEY_MENU>;label = "menu";press-threshold-microvolt = <987000>;};vol-down-key {linux,code = <KEY_VOLUMEDOWN>;label = "volume down";press-threshold-microvolt = <300000>;};vol-up-key {linux,code = <KEY_VOLUMEUP>;label = "volume up";press-threshold-microvolt = <17000>;};};

        3.4  相关的iio 节点在/sys/devices/platform/ff288000.saradc/iio:device1/路径下面,可以直接cat获取相关值

四、我自己写的一个demo。

        4.1 注册 iio 接口 dac8xxxx.c 驱动,里面会使用iio_device_register 进行注册。

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iio/consumer.h>
#include <linux/miscdevice.h>
#include <linux/irq.h>
#include <linux/property.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>/*
****************************************
0h NOOP No operation NOOP Register
1h DEVID Device identification DEVID Register
2h SYNC Synchronization SYNC Register
3h CONFIG Configuration CONFIG Register
4h GAIN Gain GAIN Register
5h TRIGGER Trigger TRIGGER Register
7h STATUS Status STATUS Register
8h DAC
****************************************
*/
#define   NOOP_REG      0x00
#define   DEVID_REG     0x01
#define   SYNC_REG      0x02
#define   CONFIG_REG    0x03
#define   GAIN_REG      0x04
#define   TRIGGER_REG   0x05
#define   STATUS_REG    0x07
#define   DAC_REG       0x08
#define   DAC80501Z_ID           0X0115
#define   DAC80501_DRV_NAME    "dac80501"#define   DAC80501Z_INTERNAL_REFERENCE_VOLTAGE       2500 * 1000
#define   DAC80501Z_EXTERNAL_REFERENCE_VOLTAGE       2500 * 1000#define   DAC80501_GAIN_REG_BUFF_GAIN_MSK	          BIT(0)
#define   DAC80501_GAIN_REG_BUFF_GAIN_TWO		      BIT(0)
#define   DAC80501_GAIN_REG_BUFF_GAIN_ONE			  (0)#define   DAC80501_CONFIG_REG_REF_PWDWN_MSK           BIT(8)
#define   DAC80501_CONFIG_REG_OUTSIDE_REFERENCE       BIT(8)
#define   DAC80501_CONFIG_REG_INTERNAL_REFERENCE      0#define DAC80501_IOCTL_MAGIC		'L'
#define CMD_DAC_VOLTAGE_OUTPUT          _IOW(DAC80501_IOCTL_MAGIC, 2, int)
#define CMD_DAC_GAIN_REG_BUFF_GAIN      _IOW(DAC80501_IOCTL_MAGIC, 3, int)
#define CMD_WRITE_DAC_REG_VALUE             _IOW(DAC80501_IOCTL_MAGIC, 4, int)
#define CMD_READ_DAC_REG_VALUE             _IOW(DAC80501_IOCTL_MAGIC, 5, int)struct regmap *regmap;
struct iio_channel *chan;
static struct i2c_client *dac80501_i2c_client;#define ADS_CHANNEL(_index, _id) {				\.type = IIO_VOLTAGE,					\.indexed = 1,						\.channel = _index,					\.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\.datasheet_name = _id,					\
}static const struct iio_chan_spec voltage_channel[] = {ADS_CHANNEL(0, "ads"),ADS_CHANNEL(1, "ads"),/*{//.type = IIO_VOLTAGE,//.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),.channel = 0,	.type = IIO_VOLTAGE,.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},*/};struct regval {unsigned char  addr;unsigned int   val;
};struct  dac80501_data{struct i2c_client   *client;struct regmap       *regmap;//struct regmap_config regmap_cfg;struct mutex         lock;
};static const struct regval dac80501_regs[] = {{NOOP_REG    , 0x00},{SYNC_REG    , 0x00},{CONFIG_REG  , 0x00},{GAIN_REG    , 0x01},{TRIGGER_REG , 0x00},{STATUS_REG  , 0x00},{DAC_REG     , 0x00},
};static struct regmap_config dac80501_regmap_config = {.reg_bits = 8,.val_bits = 16,.max_register = DAC_REG,
};static int dac80501_drv_open(struct inode *inode, struct file *file)
{//printk("[%s]\r\n",__func__);return 0;
}
ssize_t dac80501_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{printk("[%s]\r\n",__func__);return size;
}ssize_t dac80501_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{char kbuf[10] = {0};int reg_value=0 ,gain_value = 0;int  value;if (copy_from_user(kbuf, ubuf, size) != 0){printk("copy_from_user error \n "); return -1;}sscanf(kbuf, "%d",&value);printk("[%s] value = %d\r\n",__func__,value );if(value > DAC80501Z_INTERNAL_REFERENCE_VOLTAGE){regmap_update_bits(regmap, GAIN_REG,DAC80501_GAIN_REG_BUFF_GAIN_MSK, DAC80501_GAIN_REG_BUFF_GAIN_TWO);}regmap_read(regmap, GAIN_REG, &reg_value);gain_value = (reg_value & DAC80501_GAIN_REG_BUFF_GAIN_TWO) > 0	? 2 : 1;reg_value = (value * 0XFFFF) / (DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * gain_value);regmap_write(regmap, DAC_REG, reg_value);pr_info("GAIN_REG reg_value=0X%X\n",reg_value);return size;
}long dac80501_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{int reg_value=0 ,gain_value = 0;long temp = 0,temp_gain_value;void __user *argp = (void __user *)value;switch (cmd){case CMD_DAC_VOLTAGE_OUTPUT:if(value > DAC80501Z_INTERNAL_REFERENCE_VOLTAGE){regmap_update_bits(regmap, GAIN_REG,DAC80501_GAIN_REG_BUFF_GAIN_MSK, DAC80501_GAIN_REG_BUFF_GAIN_TWO);}regmap_read(regmap, GAIN_REG, &reg_value);gain_value = (reg_value & DAC80501_GAIN_REG_BUFF_GAIN_TWO) > 0	? 2 : 1;if(value >= DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * gain_value)reg_value = 0XFFFF;else{temp_gain_value = gain_value;temp = (value * 0XFFFF) / (DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * temp_gain_value);reg_value = temp;//reg_value = (value * 0XFFFF) / (DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * gain_value);}regmap_write(regmap, DAC_REG, reg_value);//pr_info("dac80501_ioctl  value=%ld,temp = 0X%lX,reg_value=0X%X\n",value,temp,reg_value);break;case CMD_WRITE_DAC_REG_VALUE:regmap_write(regmap, DAC_REG, value);break;case CMD_READ_DAC_REG_VALUE:regmap_read(regmap, DAC_REG, &reg_value);if (copy_to_user(argp , &reg_value , sizeof(reg_value)))return -EFAULT;break;	case CMD_DAC_GAIN_REG_BUFF_GAIN:if(value == 1)regmap_update_bits(regmap, GAIN_REG,DAC80501_GAIN_REG_BUFF_GAIN_MSK, DAC80501_GAIN_REG_BUFF_GAIN_TWO);elseregmap_update_bits(regmap, GAIN_REG,DAC80501_GAIN_REG_BUFF_GAIN_MSK, DAC80501_GAIN_REG_BUFF_GAIN_ONE);break;}return 0;
}static struct file_operations dac80501_drv_fops = {.owner		     = THIS_MODULE,.open		     = dac80501_drv_open,.write		     = dac80501_write,.read            = dac80501_read,.unlocked_ioctl  = dac80501_ioctl, 
};static struct miscdevice dac80501_miscdev = 
{.minor	        = MISC_DYNAMIC_MINOR,.name	        = DAC80501_DRV_NAME,.fops	        = &dac80501_drv_fops,
};static ssize_t dac80501_dbg_show(struct device *dev, struct device_attribute *attr, char *buf)
{int ret,reg_offset,reg_data;//struct dac80501_data *data = i2c_get_clientdata(rk808_i2c_client);unsigned char  iten_str[20] , total_reg_data_str[150] = {0};for( reg_offset = 0 ; reg_offset <= DAC_REG ; reg_offset++){if(reg_offset == 0x06)continue;ret = regmap_read(regmap, reg_offset, &reg_data);sprintf(iten_str, "[0x%02x]=0x%04x\r\n", reg_offset,reg_data);printk("%s\n", iten_str);strcat(total_reg_data_str, iten_str);}return sprintf(buf, "%s\n", total_reg_data_str);
}static ssize_t dac80501_dbg_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{unsigned int ret ,reg_addr,reg_value;ret = sscanf(buf, "%x %x ", &reg_addr, &reg_value);regmap_write(regmap, reg_addr, reg_value);printk("[%s]ret=%d,reg_addr=0x%02x,reg_value=0x%04x\r\n",__func__,ret,reg_addr,reg_value);return count;
}static DEVICE_ATTR(dac80501_dbg,    0644, dac80501_dbg_show, dac80501_dbg_store);int dac80501_iio_readraw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,int *val, int *val2, long mask) {pr_info("myiio_readraw start\n");pr_info("myiio_readraw: iio_dev.name = %s\n", indio_dev->name);pr_info("myiio_readraw: iio_dev.type = %d\n", chan->type);pr_info("myiio_readraw: channel= %d,channel2 = %d", chan->channel,chan->channel2);pr_info("myiio_readraw: mask = %ld", mask);switch (mask) {case IIO_CHAN_INFO_PROCESSED:pr_info("mask = IIO_CHAN_INFO_PROCESSED\n");break;default:pr_info("who are you \n");break;}*val = 389;*val2 = 52;pr_info("dac80501_iio_readraw  end\n");return IIO_VAL_FRACTIONAL;
}static int dac80501_iio_writeraw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int val,int val2,long mask)
{int  ret = 0 ;int  reg_value=0 ,gain_value = 0;long temp = 0,temp_val,temp_gain_value;struct dac80501_data *data = iio_priv(indio_dev);switch (mask) {case IIO_CHAN_INFO_PROCESSED:if(val > DAC80501Z_INTERNAL_REFERENCE_VOLTAGE){regmap_update_bits(data->regmap, GAIN_REG,DAC80501_GAIN_REG_BUFF_GAIN_MSK, DAC80501_GAIN_REG_BUFF_GAIN_TWO);}regmap_read(data->regmap, GAIN_REG, &reg_value);gain_value = (reg_value & DAC80501_GAIN_REG_BUFF_GAIN_TWO) > 0  ? 2 : 1;if(val >= DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * gain_value)reg_value = 0XFFFF;else{temp_val = val;temp_gain_value = gain_value;temp = (temp_val * 0XFFFF) / (DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * temp_gain_value);//reg_value = (val * 0XFFFF) / (DAC80501Z_INTERNAL_REFERENCE_VOLTAGE * gain_value);reg_value = temp;}regmap_write(data->regmap, DAC_REG, reg_value);break;default:ret = -EINVAL;pr_info("EINVAL\n");break;}pr_info("myiio_writeraw end  val=%d,val2=%d,mask=%ld temp = 0X%lX,reg_value=0X%X\n",val,val2,mask,temp,reg_value);return ret;
}    static const struct iio_info info = { .driver_module = THIS_MODULE, .write_raw = dac80501_iio_writeraw, .read_raw   = dac80501_iio_readraw, 
};static int dac80501_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret ,i;unsigned int chip_id;struct iio_dev *iiodev;struct dac80501_data *data;struct device *dev = &client->dev;printk("[%s]",__func__);iiodev = devm_iio_device_alloc(&client->dev, sizeof(struct dac80501_data));if (!iiodev) {return -ENOMEM;}data = iio_priv(iiodev);data->client = client;i2c_set_clientdata(client, iiodev);iiodev->name = "dac80501";iiodev->dev.parent = &client->dev;iiodev->info = &info;iiodev->modes = INDIO_DIRECT_MODE;iiodev->channels = voltage_channel;iiodev->num_channels = ARRAY_SIZE(voltage_channel);ret = iio_device_register(iiodev);if (ret < 0) {dev_err(&client->dev, "iio_device_register failed\n");goto err_iio_register;}data->regmap = regmap_init_i2c(client, &dac80501_regmap_config);if (IS_ERR(data->regmap)) {ret = PTR_ERR(data->regmap);goto err_regmap_init;}dac80501_i2c_client = client;regmap = data->regmap;ret = regmap_read(data->regmap, DEVID_REG, &chip_id);if(DAC80501Z_ID == chip_id){printk("[%s] ret=%d , chip_id = 0X%x \r\n",__func__,ret,chip_id);}else{dev_err(dev, "failed to read dac80501Z chip id\n");return -ENOMEM;}for(i =0 ; i< sizeof(dac80501_regs) / sizeof(dac80501_regs[0]) ; i++){ret = regmap_write(data->regmap, dac80501_regs[i].addr, dac80501_regs[i].val);printk("[%s] ret=%d; dac80501_regs[i].addr = 0x%x, dac80501_regs[i].val = 0x%x\r\n",__func__,ret,dac80501_regs[i].addr, dac80501_regs[i].val);}ret = device_create_file(&client->dev, &dev_attr_dac80501_dbg);ret = misc_register(&dac80501_miscdev); return 0;err_regmap_init:iio_device_unregister(iiodev);
err_iio_register:regmap_exit(data->regmap);return ret;}static int dac80501_remove(struct i2c_client *client)
{// 注销设备return 0;
}
static const struct of_device_id dac80501_match[] = {{ .compatible = "topdon,dac80501" },{ },
};static const struct i2c_device_id dac80501_id[] = {{ "dac80501_driver", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, dac80501_id);static struct i2c_driver dac80501_driver = {.driver = {.name = DAC80501_DRV_NAME,.of_match_table = dac80501_match,.owner = THIS_MODULE,},.id_table = dac80501_id,.probe = dac80501_probe,.remove = dac80501_remove,
};static int __init dac80501_init(void)
{printk("[%s]\r\n",__func__);return i2c_add_driver(&dac80501_driver);
}static void __exit dac80501_exit(void)
{i2c_del_driver(&dac80501_driver);
}module_init(dac80501_init);
module_exit(dac80501_exit);MODULE_AUTHOR("terry");
MODULE_DESCRIPTION("dac80501 driver");
MODULE_LICENSE("GPL");

        4.2 在另外一个驱动里面调用iio接口,主要的代码是

struct iio_channel *chan;	  //#定义 IIO 通道结构体int val,ret;chan = iio_channel_get(&client->dev, NULL);	// #获取 IIO 通道结构体if (IS_ERR(chan)){chan = NULL;printk("%s() have not set adcchan d\n", __FUNCTION__);}else{printk("%s() have set adcchan d\n", __FUNCTION__);ret = iio_read_channel_raw(chan, &val);printk("ret = %d  val = %d",ret,val);
}
/** ads1119.c - lm_sensors driver for ads1119 16-bit 4-input ADC* (C) Copyright 2010* Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de>** Based on the ads7828 driver by Steve Hardy.** Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.pdf** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iio/consumer.h>#define DEVICE_NAME       "ads1119"
#define INTERNALREFERENCE    2.048 #define ADS1119_CHANNELS           4
#define ADS1119_CFG_REG            0x01
#define ADS1119_DEFAULT_GAIN       0x01
#define ADS1119_DEFAULT_CHANNELS   0xff
#define ADS1119_DEFAULT_DATA_RATE  330
#define ADS1119_STATUS_REG_DRDY	          BIT(7)
#define ADS1119_CONF_REG_GAIN_BIT	      BIT(4)#define ADS1119_IOCTL_MAGIC		'A'
#define ADS1119_IOCTL_WRITE		                    _IOW(ADS1119_IOCTL_MAGIC, 0x01, char*)
#define ADS1119_IOCTL_READ_VOLTAGE  	            _IOWR(ADS1119_IOCTL_MAGIC, 0x02, char*)
#define ADS1119_READ_RESI_PARTIAL_CONVERSION_REG  	_IOWR(ADS1119_IOCTL_MAGIC, 0x03, char*)
#define ADS1119_READ_DAC_VOUT_CONVERSION_REG  	    _IOWR(ADS1119_IOCTL_MAGIC, 0x04, char*)struct ads1119_channel_data {bool enabled;unsigned int gain;unsigned int data_rate;
};struct ads1119_platform_data {struct ads1119_channel_data channel_data[ADS1119_CHANNELS];
};/** RESET Reset the device 0000 011x* START/SYNC Start or restart conversions 0000 100x* POWERDOWN Enter power-down mode 0000 001x* RDATA Read data by command 0001 xxxx* RREG Read register at address r 0010 0rxx* WREG Write configuration register 0100 00xx*//* ADS1119 registers */
enum {REG_RESET       = 0X06,REG_STRAT       = 0X08,REG_POWERDOWN   = 0X02,REG_RDATA       = 0X10,REG_CONF_RREG   = 0X20,//ConfigurationREG_STATUS_RREG = 0X24,REG_WREG        = 0X40,
};struct i2c_client *ads1119_i2c_client;/* Data rates in samples per second */static const unsigned int data_rate_table_1119[4] = {20, 90, 330, 1000
};enum ads1119_chips {ads1015,ads1119,
};struct ads1119_data {struct device *hwmon_dev;struct mutex update_lock; /* mutex protect updates */struct ads1119_channel_data channel_data[ADS1119_CHANNELS];enum ads1119_chips id;int reset_gpio, active_low;u32 delays[3];
};static int ads1119_read_adc(struct i2c_client *client, unsigned int channel)
{int res,i;u8  status,config = 0;struct ads1119_data *data = i2c_get_clientdata(client);unsigned int gain = data->channel_data[channel].gain;unsigned int data_rate = data->channel_data[channel].data_rate;unsigned int conversion_time_ms;const unsigned int * const rate_table = data_rate_table_1119;mutex_lock(&data->update_lock);/* get config parameters */config |= (channel + 0x03) << 5;config = (gain == 4) ? (config | ADS1119_CONF_REG_GAIN_BIT ): config;	for(i = 0; i < sizeof(data_rate_table_1119) / sizeof(data_rate_table_1119[0]) ; i++){if(rate_table[i] == data_rate){config |= i << 2;break;}}res = i2c_smbus_write_byte_data(client, REG_WREG , config );if (res < 0)goto err_unlock;res = i2c_smbus_write_byte(client, REG_STRAT);if (res < 0)goto err_unlock;conversion_time_ms = DIV_ROUND_UP(1000, data_rate);/* wait until conversion finished */i = 0;do{msleep(conversion_time_ms);res = i2c_smbus_read_byte_data(client, REG_STATUS_RREG);printk("[%s] read REG_STATUS_RREG,conversion reg = 0x%x ,conversion_time_ms=%d,gain = %d,data_rate = %d\r\n",__func__,res,conversion_time_ms,gain,data_rate);if (res < 0)goto err_unlock;status = res;if (!(status & ADS1119_STATUS_REG_DRDY)) {/* conversion not finished in time */if(i > 3){res = -EIO;goto err_unlock;}}else break;i++;}while(true);res = i2c_smbus_read_word_swapped(client, REG_RDATA);if (res < 0)goto err_unlock;printk("[%s] config = 0x%x,conversion reg data = 0x%x \r\n",__func__,config,res);goto succeed;err_unlock:printk("[%s] Failed to adc value,res = %d\r\n",__func__,res);
succeed:mutex_unlock(&data->update_lock);return res;
}static long ads1119_reg_to_uv(struct i2c_client *client, unsigned int channel,s16 reg)
{//struct ads1119_data *data = i2c_get_clientdata(client);//unsigned int pga = data->channel_data[channel].pga;long fullscale = INTERNALREFERENCE * 1000 * 1000;const int mask = 0X7FFF;return DIV_ROUND_CLOSEST(reg * fullscale, mask);
}/* sysfs callback function */
static ssize_t show_in(struct device *dev, struct device_attribute *da,char *buf)
{struct sensor_device_attribute *attr = to_sensor_dev_attr(da);struct i2c_client *client = to_i2c_client(dev);int res;int index = attr->index;struct iio_channel *chan;	  //#定义 IIO 通道结构体int val,ret;chan = iio_channel_get(&client->dev, NULL);	// #获取 IIO 通道结构体if (IS_ERR(chan)){chan = NULL;printk("%s() have not set adcchan d\n", __FUNCTION__);}else{printk("%s() have set adcchan d\n", __FUNCTION__);ret = iio_read_channel_raw(chan, &val);printk("ret = %d  val = %d",ret,val);}res = ads1119_read_adc(client, index);if (res < 0)return res;return sprintf(buf, "%ld\n", ads1119_reg_to_uv(client, index, res));
}static const struct sensor_device_attribute ads1119_in[] = {SENSOR_ATTR(ads_in0_input, S_IRUGO, show_in, NULL, 0),SENSOR_ATTR(ads_in1_input, S_IRUGO, show_in, NULL, 1),SENSOR_ATTR(ads_in2_input, S_IRUGO, show_in, NULL, 2),SENSOR_ATTR(ads_in3_input, S_IRUGO, show_in, NULL, 3),
};static int ads1119_remove(struct i2c_client *client)
{struct ads1119_data *data = i2c_get_clientdata(client);int k;hwmon_device_unregister(data->hwmon_dev);for (k = 0; k < ADS1119_CHANNELS; ++k)device_remove_file(&client->dev, &ads1119_in[k].dev_attr);return 0;
}static int ads1119_reset_chip(struct i2c_client *client)
{struct ads1119_data *data = i2c_get_clientdata(client);if (devm_gpio_request(&client->dev, data->reset_gpio,"ads1119-reset"))return -EINVAL;gpio_direction_output(data->reset_gpio,data->active_low ? 1 : 0);if (data->delays[0])msleep(DIV_ROUND_UP(data->delays[0], 1000));gpio_set_value(data->reset_gpio, data->active_low ? 0 : 1);if (data->delays[1])msleep(DIV_ROUND_UP(data->delays[1], 1000));gpio_set_value(data->reset_gpio, data->active_low ? 1 : 0);if (data->delays[2])msleep(DIV_ROUND_UP(data->delays[2], 1000));return 0;
}static int ads1119_get_dts_config_of(struct i2c_client *client)
{struct ads1119_data *data = i2c_get_clientdata(client);struct device_node *node;struct device_node *np = client->dev.of_node;if (!client->dev.of_node|| !of_get_next_child(client->dev.of_node, NULL))return -EINVAL;data->reset_gpio = of_get_named_gpio(np,"reset-gpio", 0);if (data->reset_gpio < 0)return -EINVAL;data->active_low = of_property_read_bool(np,"reset-active-low");of_property_read_u32_array(np,"reset-delays-us", data->delays, 3);for_each_child_of_node(client->dev.of_node, node) {u32 pval;unsigned int channel;unsigned int gain = ADS1119_DEFAULT_GAIN;unsigned int data_rate = ADS1119_DEFAULT_DATA_RATE;if (of_property_read_u32(node, "reg", &pval)) {dev_err(&client->dev, "invalid reg on %s\n",node->full_name);continue;}channel = pval;if (channel >= ADS1119_CHANNELS) {dev_err(&client->dev,"invalid channel index %d on %s\n",channel, node->full_name);continue;}if (!of_property_read_u32(node, "ti,gain", &pval)) {gain = pval;if (gain > 4) {dev_err(&client->dev, "invalid gain on %s\n",node->full_name);return -EINVAL;}}if (!of_property_read_u32(node, "ti,datarate", &pval)) {data_rate = pval;if (data_rate > 1000) {dev_err(&client->dev,"invalid data_rate on %s\n",node->full_name);return -EINVAL;}}data->channel_data[channel].enabled = true;data->channel_data[channel].gain = gain;data->channel_data[channel].data_rate = data_rate;}return 0;
}static void ads1119_get_channels_config(struct i2c_client *client)
{unsigned int k;struct ads1119_data *data = i2c_get_clientdata(client);struct ads1119_platform_data *pdata = dev_get_platdata(&client->dev);/* prefer platform data */if (pdata) {memcpy(data->channel_data, pdata->channel_data,sizeof(data->channel_data));return;}if (!ads1119_get_dts_config_of(client)){printk("ads1119 succeeded to get channels config \n");return;}elsedev_warn(&client->dev,"ads1119 Failed to get channels config \n");/* fallback on default configuration */for (k = 0; k < ADS1119_CHANNELS; ++k) {data->channel_data[k].enabled = true;data->channel_data[k].gain = ADS1119_DEFAULT_GAIN;data->channel_data[k].data_rate = ADS1119_DEFAULT_DATA_RATE;}
}static int ads1119_open(struct inode *inode, struct file *file)
{return 0;
}
ssize_t ads1119_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{printk("[%s]\r\n",__func__);return sprintf(buf, "%d\n", __LINE__);
}ssize_t ads1119_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{printk("[%s]\r\n",__func__);return size;
}long ads1119_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int res;unsigned long ads_voltage_value_us = 0;void __user *argp = (void __user *)arg;switch (cmd) {case ADS1119_IOCTL_READ_VOLTAGE:if (!argp)return -EINVAL;res = ads1119_read_adc(ads1119_i2c_client, 0);if (res < 0)return res;ads_voltage_value_us  = ads1119_reg_to_uv(ads1119_i2c_client, 0, res);if (copy_to_user(argp , &ads_voltage_value_us , sizeof(ads_voltage_value_us)))return -EFAULT;break;case ADS1119_READ_RESI_PARTIAL_CONVERSION_REG:if (!argp)return -EINVAL;res = ads1119_read_adc(ads1119_i2c_client, 0);if (res < 0)return res;if (copy_to_user(argp , &res , sizeof(res)))return -EFAULT;break;case ADS1119_READ_DAC_VOUT_CONVERSION_REG:if (!argp)return -EINVAL;res = ads1119_read_adc(ads1119_i2c_client, 1);if (res < 0)return res;if (copy_to_user(argp , &res , sizeof(res)))return -EFAULT;break;	default:return -EFAULT;break;}return 0;
}static struct file_operations ads1119_drv_fops = {.owner		     = THIS_MODULE,.open		     = ads1119_open,.write		     = ads1119_write,.read            = ads1119_read,.unlocked_ioctl  = ads1119_ioctl, 
};static struct miscdevice ads1119_miscdev = 
{.minor	        = MISC_DYNAMIC_MINOR,.name	        = DEVICE_NAME,.fops	        = &ads1119_drv_fops,
};static int ads1119_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct ads1119_data *data;int err ,ret;unsigned int k;printk("[%s]",__func__);data = devm_kzalloc(&client->dev, sizeof(struct ads1119_data),GFP_KERNEL);if (!data)return -ENOMEM;data->id = ads1119;i2c_set_clientdata(client, data);mutex_init(&data->update_lock);/* build sysfs attribute group */ads1119_get_channels_config(client);ads1119_reset_chip(client);for (k = 0; k < ADS1119_CHANNELS; ++k) {if (!data->channel_data[k].enabled)continue;err = device_create_file(&client->dev, &ads1119_in[k].dev_attr);if (err)goto exit_remove;}for(k = 0;k < 5; k++){ret = i2c_smbus_read_byte_data(client, REG_STATUS_RREG);if(ret >= 0)break;else msleep(10);}if (ret < 0){dev_warn(&client->dev,"Failed to detect ads1119 \n");//goto exit_remove;}data->hwmon_dev = hwmon_device_register(&client->dev);if (IS_ERR(data->hwmon_dev)) {err = PTR_ERR(data->hwmon_dev);goto exit_remove;}ret = misc_register(&ads1119_miscdev); ads1119_i2c_client = client;return 0;exit_remove:for (k = 0; k < ADS1119_CHANNELS; ++k)device_remove_file(&client->dev, &ads1119_in[k].dev_attr);return -EINVAL;
}static const struct of_device_id ads1119_match[] = {{ .compatible = "topdon,ads1119" },{ },
};static const struct i2c_device_id ads1119_id[] = {{ "ads1119",  ads1119},{ }
};
MODULE_DEVICE_TABLE(i2c, ads1119_id);static struct i2c_driver ads1119_driver = {.driver = {.name = "ads1119",.of_match_table = ads1119_match,.owner = THIS_MODULE,},.probe = ads1119_probe,.remove = ads1119_remove,.id_table = ads1119_id,
};module_i2c_driver(ads1119_driver);MODULE_AUTHOR("Dirk Eibach <eibach@gdsys.de>");
MODULE_DESCRIPTION("ADS1119 driver");
MODULE_LICENSE("GPL");

        4.3 dts ,主要是添加io-channels = <&dac80501  1>;

   dacxxx:dacxxx@48 {compatible = "topdon,dacxxx";#io-channel-cells = <1>;reg = <0x48>;};
    adsxxx@40 {status = "okay";compatible = "topdon,adsxxx";reg = <0x40>;reset-gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;reset-active-low;reset-delays-us = <0 50000 50000>;#address-cells = <1>;io-channels = <&dac80501  1>; //saradc 5

         4.4 直接用cat 命令也是可以读iio节点

        4.5 通过iio_read_channel_raw函数读iio接口

五、参考文章

Linux学习笔记(22.2)——基于IIC + Regmap + IIO的AP3216C的设备驱动_linux regmap i2c_glen_cao的博客-CSDN博客

RK356X ADC 使用_iio_read_channel_raw_悲伤的小强的博客-CSDN博客

史上最简单的Linux内核IIO子系统入门demo_内核版本4.4.194_hehui0921的博客-CSDN博客

linux IIO子系统使用说明_zimu-zimu的博客-CSDN博客

史上最简单的Linux内核IIO子系统入门demo_内核版本4.4.194_hehui0921的博客-CSDN博客

linux kernel iio 架构_iio子系统介绍_小武~的博客-CSDN博客

linux IIO子系统使用说明

Linux設備驅動之IIO子系統——IIO框架數據讀取 - JavaShuo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28549.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

qiankun框架vue3主应用和子应用生产环境打包部署nginx

首先下载nginx,进行最小化配置 用vscode 打开nginx.conf文件 在http模块的server模块里进行配置 listen 字段监听端口号 http的默认端口号是80(nginx的端口号可以随便写) server_name字段 是ip地址 lochhost就是127.0.0.1 lacation 字段 是在浏览器的地址栏http协议ip地址…

【es报错】:

报错信息 reasonrequest [/test1] contains unrecognized parameter: [include_type_name] 解决方法 统一导入client下的包&#xff1a;

Leetcode-每日一题【1019.链表中的下一个更大结点】

题目 给定一个长度为 n 的链表 head 对于列表中的每个节点&#xff0c;查找下一个 更大节点 的值。也就是说&#xff0c;对于每个节点&#xff0c;找到它旁边的第一个节点的值&#xff0c;这个节点的值 严格大于 它的值。 返回一个整数数组 answer &#xff0c;其中 answer[…

「车型分析」控制系统典型应用车型 —— 停车机器人

如今&#xff0c;城市可用土地的日益稀缺&#xff08;城市化&#xff09;和汽车使用数量的增加&#xff08;机动化&#xff09;,为了可持续性发展和其他生活质量问题相结合&#xff0c;由此孕育出来了一种自动停车系统。停车机器人凭借其灵活、高效、标准化的停车模式&#xff…

Springboot + Vue 上传Word、PDF文档并保留内部格式

因为业务需求&#xff0c;上传Word文件需要编辑&#xff0c;但如何使用Blob方式&#xff0c;在数据库里存文件&#xff0c;就会造成格式消失。所以修改思路&#xff1a;上传文件到服务器本地&#xff0c;保证数据存储的完整性。 前端 <el-upload class"upload-demo&quo…

ChatGPT变现五个思路

一、前言 ChatGPT是一款AI聊天机器人&#xff0c;发布于2022年11月。凭借着在广泛的知识领域为消费者问题做出清晰、详尽解答的出色能力&#xff0c;其一经推出就引发全球轰动&#xff0c;自然也得到零售行业的高度关注。例如&#xff0c;消费者只要询问ChatGPT如何布置一个梦…

微服务架构——配置中心

「配置中心」&#xff0c;顾名思义&#xff0c;就是用来统一管理项目中所有配置的系统。虽然听起来很简单&#xff0c;但也不要小瞧了这个模块。如果一个中型互联网项目&#xff0c;不采用配置中心的模式&#xff0c;一大堆的各类配置项&#xff0c;各种不定时的修改需求&#…

基于STM32 ARM+FPGA伺服控制系统总体设计方案(一)

设计需求 一套完整的伺服控制方案包括了上位机、驱控一体控制器和功率板三者。操作人员 通过上位机发送各种不同指令&#xff0c;然后控制器解析指令后执行相应的伺服功能&#xff0c;其次控 制器将驱动信号传输至功率板驱动电机&#xff0c;最后控制器采集反馈信息进行闭环…

尚硅谷大数据Flink1.17实战教程-笔记03【Flink运行时架构】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷大数据Flink1.17实战教程从入门到精通_哔哩哔哩_bilibili 尚硅谷大数据Flink1.17实战教程-笔记01【Flink概述、Flink快速上手】尚硅谷大数据Flink1.17实战教程-笔记02【Flink部署】尚硅谷…

宋浩线性代数笔记(一)行列式的计算

本帖更新b站宋浩老师的线代网课笔记&#xff0c;内容较为细致详细&#xff0c;参考书用的是科学出版社的第三版&#xff0c;之后会附加同济出版社第六版的教材内容。 &#xff08;字不好看大家将就看吧QAQ&#xff09;

css+js实现点击特效效果

话不多说&#xff0c;先上效果图 实现代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…

Linux(CentOS7)下源码编译 PostgreSQL13.10 安装手册

Linux&#xff08;CentOS7&#xff09;下PostgreSQL安装手册 文章目录 一、准备PostgreSQL二、安装PostgreSQL2.1解压安装包2.2编译PG2.3查看PG安装目录2.4配置PG环境变量2.5查看PG版本2.6创建postgres用户2.7创建PG数据库数据存放目录2.8授权PG数据库数据存放目录2.9切换postg…