-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for MEN 16z188 ADC IP Core on MCB FPGAs. Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de> Acked-by: Jonathan Cameron <jic23@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
- Loading branch information
Johannes Thumshirn
authored and
Greg Kroah-Hartman
committed
Feb 28, 2014
1 parent
b71bb86
commit 74aeac4
Showing
3 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/* | ||
* MEN 16z188 Analog to Digial Converter | ||
* | ||
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) | ||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de> | ||
* | ||
* 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; version 2 of the License. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
#include <linux/mcb.h> | ||
#include <linux/iio/iio.h> | ||
|
||
#define Z188_ADC_MAX_CHAN 8 | ||
#define Z188_ADC_GAIN 0x0700000 | ||
#define Z188_MODE_VOLTAGE BIT(27) | ||
#define Z188_CFG_AUTO 0x1 | ||
#define Z188_CTRL_REG 0x40 | ||
|
||
#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) | ||
#define ADC_OVR(x) ((x) & 0x1) | ||
|
||
struct z188_adc { | ||
struct resource *mem; | ||
void __iomem *base; | ||
}; | ||
|
||
#define Z188_ADC_CHANNEL(idx) { \ | ||
.type = IIO_VOLTAGE, \ | ||
.indexed = 1, \ | ||
.channel = (idx), \ | ||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
} | ||
|
||
static const struct iio_chan_spec z188_adc_iio_channels[] = { | ||
Z188_ADC_CHANNEL(0), | ||
Z188_ADC_CHANNEL(1), | ||
Z188_ADC_CHANNEL(2), | ||
Z188_ADC_CHANNEL(3), | ||
Z188_ADC_CHANNEL(4), | ||
Z188_ADC_CHANNEL(5), | ||
Z188_ADC_CHANNEL(6), | ||
Z188_ADC_CHANNEL(7), | ||
}; | ||
|
||
static int z188_iio_read_raw(struct iio_dev *iio_dev, | ||
struct iio_chan_spec const *chan, | ||
int *val, | ||
int *val2, | ||
long info) | ||
{ | ||
struct z188_adc *adc = iio_priv(iio_dev); | ||
int ret; | ||
u16 tmp; | ||
|
||
switch (info) { | ||
case IIO_CHAN_INFO_RAW: | ||
tmp = readw(adc->base + chan->channel * 4); | ||
|
||
if (ADC_OVR(tmp)) { | ||
dev_info(&iio_dev->dev, | ||
"Oversampling error on ADC channel %d\n", | ||
chan->channel); | ||
return -EIO; | ||
} | ||
*val = ADC_DATA(tmp); | ||
ret = IIO_VAL_INT; | ||
break; | ||
default: | ||
ret = -EINVAL; | ||
break; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static struct iio_info z188_adc_info = { | ||
.read_raw = &z188_iio_read_raw, | ||
.driver_module = THIS_MODULE, | ||
}; | ||
|
||
static void men_z188_config_channels(void __iomem *addr) | ||
{ | ||
int i; | ||
u32 cfg; | ||
u32 ctl; | ||
|
||
ctl = readl(addr + Z188_CTRL_REG); | ||
ctl |= Z188_CFG_AUTO; | ||
writel(ctl, addr + Z188_CTRL_REG); | ||
|
||
for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { | ||
cfg = readl(addr + i); | ||
cfg &= ~Z188_ADC_GAIN; | ||
cfg |= Z188_MODE_VOLTAGE; | ||
writel(cfg, addr + i); | ||
} | ||
} | ||
|
||
static int men_z188_probe(struct mcb_device *dev, | ||
const struct mcb_device_id *id) | ||
{ | ||
struct z188_adc *adc; | ||
struct iio_dev *indio_dev; | ||
struct resource *mem; | ||
|
||
indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); | ||
if (!indio_dev) | ||
return -ENOMEM; | ||
|
||
adc = iio_priv(indio_dev); | ||
indio_dev->name = "z188-adc"; | ||
indio_dev->dev.parent = &dev->dev; | ||
indio_dev->info = &z188_adc_info; | ||
indio_dev->modes = INDIO_DIRECT_MODE; | ||
indio_dev->channels = z188_adc_iio_channels; | ||
indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); | ||
|
||
mem = mcb_request_mem(dev, "z188-adc"); | ||
if (!mem) | ||
return -ENOMEM; | ||
|
||
adc->base = ioremap(mem->start, resource_size(mem)); | ||
if (adc->base == NULL) | ||
goto err; | ||
|
||
men_z188_config_channels(adc->base); | ||
|
||
adc->mem = mem; | ||
mcb_set_drvdata(dev, indio_dev); | ||
|
||
return iio_device_register(indio_dev); | ||
|
||
err: | ||
mcb_release_mem(mem); | ||
return -ENXIO; | ||
} | ||
|
||
static void men_z188_remove(struct mcb_device *dev) | ||
{ | ||
struct iio_dev *indio_dev = mcb_get_drvdata(dev); | ||
struct z188_adc *adc = iio_priv(indio_dev); | ||
|
||
iio_device_unregister(indio_dev); | ||
iounmap(adc->base); | ||
mcb_release_mem(adc->mem); | ||
} | ||
|
||
static const struct mcb_device_id men_z188_ids[] = { | ||
{ .device = 0xbc }, | ||
}; | ||
MODULE_DEVICE_TABLE(mcb, men_z188_ids); | ||
|
||
static struct mcb_driver men_z188_driver = { | ||
.driver = { | ||
.name = "z188-adc", | ||
.owner = THIS_MODULE, | ||
}, | ||
.probe = men_z188_probe, | ||
.remove = men_z188_remove, | ||
.id_table = men_z188_ids, | ||
}; | ||
module_mcb_driver(men_z188_driver); | ||
|
||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); | ||
MODULE_ALIAS("mcb:16z188"); |