一、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;
}
#endif
static 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);
else
state = !!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;
else
button->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;
else
return -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;
else
pdata->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,
};
#endif
static 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, ®_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, ®_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, ®_value);
if (copy_to_user(argp , ®_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);
else
regmap_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, ®_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 ", ®_addr, ®_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, ®_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,//Configuration
REG_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;
}else
dev_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子系统使用说明文章来源:https://www.toymoban.com/news/detail-582427.html
Linux設備驅動之IIO子系統——IIO框架數據讀取 - JavaShuo文章来源地址https://www.toymoban.com/news/detail-582427.html
到了这里,关于2023-07-10 linux IIO子系统使用学习,在TI 的ads1015驱动里面看到相关使用,故花点时间进行简单的学习,入门级别,纪录点滴。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!