cregit-Linux how code gets into the kernel

Release 4.7 drivers/iio/adc/cc10001_adc.c

Directory: drivers/iio/adc
/*
 * Copyright (c) 2014-2015 Imagination Technologies Ltd.
 *
 * 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/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

/* Registers */

#define CC10001_ADC_CONFIG		0x00

#define CC10001_ADC_START_CONV		BIT(4)

#define CC10001_ADC_MODE_SINGLE_CONV	BIT(5)


#define CC10001_ADC_DDATA_OUT		0x04

#define CC10001_ADC_EOC			0x08

#define CC10001_ADC_EOC_SET		BIT(0)


#define CC10001_ADC_CHSEL_SAMPLED	0x0c

#define CC10001_ADC_POWER_DOWN		0x10

#define CC10001_ADC_POWER_DOWN_SET	BIT(0)


#define CC10001_ADC_DEBUG		0x14

#define CC10001_ADC_DATA_COUNT		0x20


#define CC10001_ADC_DATA_MASK		GENMASK(9, 0)

#define CC10001_ADC_NUM_CHANNELS	8

#define CC10001_ADC_CH_MASK		GENMASK(2, 0)


#define CC10001_INVALID_SAMPLED		0xffff

#define CC10001_MAX_POLL_COUNT		20

/*
 * As per device specification, wait six clock cycles after power-up to
 * activate START. Since adding two more clock cycles delay does not
 * impact the performance too much, we are adding two additional cycles delay
 * intentionally here.
 */

#define	CC10001_WAIT_CYCLES		8


struct cc10001_adc_device {
	
void __iomem *reg_base;
	
struct clk *adc_clk;
	
struct regulator *reg;
	
u16 *buf;

	
bool shared;
	
struct mutex lock;
	
unsigned int start_delay_ns;
	
unsigned int eoc_delay_ns;
};


static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev, u32 reg, u32 val) { writel(val, adc_dev->reg_base + reg); }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva29100.00%1100.00%
Total29100.00%1100.00%


static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev, u32 reg) { return readl(adc_dev->reg_base + reg); }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva25100.00%1100.00%
Total25100.00%1100.00%


static void cc10001_adc_power_up(struct cc10001_adc_device *adc_dev) { cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, 0); ndelay(adc_dev->start_delay_ns); }

Contributors

PersonTokensPropCommitsCommitProp
naidu tellapatinaidu tellapati27100.00%1100.00%
Total27100.00%1100.00%


static void cc10001_adc_power_down(struct cc10001_adc_device *adc_dev) { cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, CC10001_ADC_POWER_DOWN_SET); }

Contributors

PersonTokensPropCommitsCommitProp
naidu tellapatinaidu tellapati20100.00%1100.00%
Total20100.00%1100.00%


static void cc10001_adc_start(struct cc10001_adc_device *adc_dev, unsigned int channel) { u32 val; /* Channel selection and mode of operation */ val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV; cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); udelay(1); val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG); val = val | CC10001_ADC_START_CONV; cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva6292.54%150.00%
naidu tellapatinaidu tellapati57.46%150.00%
Total67100.00%2100.00%


static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev, unsigned int channel, unsigned int delay) { struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); unsigned int poll_count = 0; while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) & CC10001_ADC_EOC_SET)) { ndelay(delay); if (poll_count++ == CC10001_MAX_POLL_COUNT) return CC10001_INVALID_SAMPLED; } poll_count = 0; while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) & CC10001_ADC_CH_MASK) != channel) { ndelay(delay); if (poll_count++ == CC10001_MAX_POLL_COUNT) return CC10001_INVALID_SAMPLED; } /* Read the 10 bit output register */ return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) & CC10001_ADC_DATA_MASK; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva113100.00%1100.00%
Total113100.00%1100.00%


static irqreturn_t cc10001_adc_trigger_h(int irq, void *p) { struct cc10001_adc_device *adc_dev; struct iio_poll_func *pf = p; struct iio_dev *indio_dev; unsigned int delay_ns; unsigned int channel; unsigned int scan_idx; bool sample_invalid; u16 *data; int i; indio_dev = pf->indio_dev; adc_dev = iio_priv(indio_dev); data = adc_dev->buf; mutex_lock(&adc_dev->lock); if (!adc_dev->shared) cc10001_adc_power_up(adc_dev); /* Calculate delay step for eoc and sampled data */ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; i = 0; sample_invalid = false; for_each_set_bit(scan_idx, indio_dev->active_scan_mask, indio_dev->masklength) { channel = indio_dev->channels[scan_idx].channel; cc10001_adc_start(adc_dev, channel); data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns); if (data[i] == CC10001_INVALID_SAMPLED) { dev_warn(&indio_dev->dev, "invalid sample on channel %d\n", channel); sample_invalid = true; goto done; } i++; } done: if (!adc_dev->shared) cc10001_adc_power_down(adc_dev); mutex_unlock(&adc_dev->lock); if (!sample_invalid) iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva19886.09%125.00%
naidu tellapatinaidu tellapati3213.91%375.00%
Total230100.00%4100.00%


static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); unsigned int delay_ns; u16 val; if (!adc_dev->shared) cc10001_adc_power_up(adc_dev); /* Calculate delay step for eoc and sampled data */ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; cc10001_adc_start(adc_dev, chan->channel); val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns); if (!adc_dev->shared) cc10001_adc_power_down(adc_dev); return val; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva7682.61%133.33%
naidu tellapatinaidu tellapati1617.39%266.67%
Total92100.00%3100.00%


static int cc10001_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: if (iio_buffer_enabled(indio_dev)) return -EBUSY; mutex_lock(&adc_dev->lock); *val = cc10001_adc_read_raw_voltage(indio_dev, chan); mutex_unlock(&adc_dev->lock); if (*val == CC10001_INVALID_SAMPLED) return -EIO; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = regulator_get_voltage(adc_dev->reg); if (ret < 0) return ret; *val = ret / 1000; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva14498.63%150.00%
naidu tellapatinaidu tellapati21.37%150.00%
Total146100.00%2100.00%


static int cc10001_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); kfree(adc_dev->buf); adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); if (!adc_dev->buf) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva61100.00%1100.00%
Total61100.00%1100.00%

static const struct iio_info cc10001_adc_info = { .driver_module = THIS_MODULE, .read_raw = &cc10001_adc_read_raw, .update_scan_mode = &cc10001_update_scan_mode, };
static int cc10001_adc_channel_init(struct iio_dev *indio_dev, unsigned long channel_map) { struct iio_chan_spec *chan_array, *timestamp; unsigned int bit, idx = 0; indio_dev->num_channels = bitmap_weight(&channel_map, CC10001_ADC_NUM_CHANNELS) + 1; chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels, sizeof(struct iio_chan_spec), GFP_KERNEL); if (!chan_array) return -ENOMEM; for_each_set_bit(bit, &channel_map, CC10001_ADC_NUM_CHANNELS) { struct iio_chan_spec *chan = &chan_array[idx]; chan->type = IIO_VOLTAGE; chan->indexed = 1; chan->channel = bit; chan->scan_index = idx; chan->scan_type.sign = 'u'; chan->scan_type.realbits = 10; chan->scan_type.storagebits = 16; chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); idx++; } timestamp = &chan_array[idx]; timestamp->type = IIO_TIMESTAMP; timestamp->channel = -1; timestamp->scan_index = idx; timestamp->scan_type.sign = 's'; timestamp->scan_type.realbits = 64; timestamp->scan_type.storagebits = 64; indio_dev->channels = chan_array; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva21996.90%150.00%
naidu tellapatinaidu tellapati73.10%150.00%
Total226100.00%2100.00%


static int cc10001_adc_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct cc10001_adc_device *adc_dev; unsigned long adc_clk_rate; struct resource *res; struct iio_dev *indio_dev; unsigned long channel_map; int ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); if (indio_dev == NULL) return -ENOMEM; adc_dev = iio_priv(indio_dev); channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0); if (!of_property_read_u32(node, "adc-reserved-channels", &ret)) { adc_dev->shared = true; channel_map &= ~ret; } adc_dev->reg = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(adc_dev->reg)) return PTR_ERR(adc_dev->reg); ret = regulator_enable(adc_dev->reg); if (ret) return ret; indio_dev->dev.parent = &pdev->dev; indio_dev->name = dev_name(&pdev->dev); indio_dev->info = &cc10001_adc_info; indio_dev->modes = INDIO_DIRECT_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(adc_dev->reg_base)) { ret = PTR_ERR(adc_dev->reg_base); goto err_disable_reg; } adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc"); if (IS_ERR(adc_dev->adc_clk)) { dev_err(&pdev->dev, "failed to get the clock\n"); ret = PTR_ERR(adc_dev->adc_clk); goto err_disable_reg; } ret = clk_prepare_enable(adc_dev->adc_clk); if (ret) { dev_err(&pdev->dev, "failed to enable the clock\n"); goto err_disable_reg; } adc_clk_rate = clk_get_rate(adc_dev->adc_clk); if (!adc_clk_rate) { ret = -EINVAL; dev_err(&pdev->dev, "null clock rate!\n"); goto err_disable_clk; } adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate; adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES; /* * There is only one register to power-up/power-down the AUX ADC. * If the ADC is shared among multiple CPUs, always power it up here. * If the ADC is used only by the MIPS, power-up/power-down at runtime. */ if (adc_dev->shared) cc10001_adc_power_up(adc_dev); /* Setup the ADC channels available on the device */ ret = cc10001_adc_channel_init(indio_dev, channel_map); if (ret < 0) goto err_disable_clk; mutex_init(&adc_dev->lock); ret = iio_triggered_buffer_setup(indio_dev, NULL, &cc10001_adc_trigger_h, NULL); if (ret < 0) goto err_disable_clk; ret = iio_device_register(indio_dev); if (ret < 0) goto err_cleanup_buffer; platform_set_drvdata(pdev, indio_dev); return 0; err_cleanup_buffer: iio_triggered_buffer_cleanup(indio_dev); err_disable_clk: clk_disable_unprepare(adc_dev->adc_clk); err_disable_reg: regulator_disable(adc_dev->reg); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva46694.72%133.33%
naidu tellapatinaidu tellapati265.28%266.67%
Total492100.00%3100.00%


static int cc10001_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); cc10001_adc_power_down(adc_dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); clk_disable_unprepare(adc_dev->adc_clk); regulator_disable(adc_dev->reg); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva5892.06%150.00%
naidu tellapatinaidu tellapati57.94%150.00%
Total63100.00%2100.00%

static const struct of_device_id cc10001_adc_dt_ids[] = { { .compatible = "cosmic,10001-adc", }, { } }; MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids); static struct platform_driver cc10001_adc_driver = { .driver = { .name = "cc10001-adc", .of_match_table = cc10001_adc_dt_ids, }, .probe = cc10001_adc_probe, .remove = cc10001_adc_remove, }; module_platform_driver(cc10001_adc_driver); MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>"); MODULE_DESCRIPTION("Cosmic Circuits ADC driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
phani movvaphani movva170692.17%116.67%
naidu tellapatinaidu tellapati1457.83%583.33%
Total1851100.00%6100.00%
Directory: drivers/iio/adc
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}