Skip to content
Snippets Groups Projects
Commit c77e059e authored by Federico Vaga's avatar Federico Vaga
Browse files

ad7888: support for ad7887 and channel enable managment


ad7887 and ad7888 are now handled by the same driver.
The driver requires data only for enabled channels

Signed-off-by: default avatarFederico Vaga <federico.vaga@gmail.com>
Acked-by: default avatarAlessandro Rubini <rubini@gnudd.com>
parent be10c154
Branches
Tags
No related merge requests found
......@@ -9,77 +9,124 @@
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
enum ad7888_driver_supported_device {
ID_AD7888
ZIO_PARAM_TRIGGER(ad788x_trigger);
ZIO_PARAM_BUFFER(ad788x_buffer);
enum ad788x_devices {
ID_AD7887,
ID_AD7888,
};
ZIO_PARAM_TRIGGER(ad7888_trigger);
ZIO_PARAM_BUFFER(ad7888_buffer);
static const struct spi_device_id ad788x_id[] = {
{"ad7887", ID_AD7887},
{"ad7888", ID_AD7888},
{}
};
#define AD788x_PM_NAME "power-management"
#define AD7887_DUAL_NAME "dual-channel"
#define AD788x_ADDR_SHIFT 11
#define AD788x_PM_ADDR 0x0300
#define AD788x_PM_SHIFT 8
#define AD7888_VREF_ADDR 0x0400
#define AD7888_VREF_SHIFT 10
#define AD7887_VREF_ADDR 0x2000
#define AD7887_VREF_SHIFT 13
#define AD7887_SINDUAL_ADDR 0x1000
#define AD7887_SINDUAL_SHIFT 12
struct ad7888_context {
struct ad788x_context {
struct spi_message message;
struct spi_transfer transfer;
struct zio_cset *cset;
unsigned int chan_enable; /* number of enabled channel */
uint32_t nsamples; /* number of samples */
};
struct ad7888_private {
struct ad788x {
struct zio_device zdev;
enum ad788x_devices type;
struct spi_device *spi;
uint16_t cmd;
};
struct ad7888_private *ad7888_prv;
#define to_ad788x(_zdev) container_of(_zdev, struct ad788x, zdev)
/*
* AD7888 doesn't have register to store data configuration; configuration
* AD788x don't have register to store data configuration; configuration
* option are sent every time when we want acquire. So, there is no address
* register to set but only a value. Address is used as mask in the tx_buf.
*/
DEFINE_ZATTR_STD(ZDEV, zattr_dev) = {
ZATTR_REG(zdev, ZATTR_NBIT, S_IRUGO, 0, 12),
/* Standard attributes for AD7887*/
static DEFINE_ZATTR_STD(ZDEV, zattr_dev_ad7887) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 12),
/* vref_src can be internal (0) or external (1)*/
ZATTR_REG(zdev, ZATTR_VREFTYPE, S_IRUGO | S_IWUGO, 0x40, 0),
ZATTR_REG(zdev, ZATTR_VREFTYPE, S_IRUGO | S_IWUGO, AD7887_VREF_ADDR, 1),
};
struct zio_attribute zattr_dev_ext[] = {
ZATTR_EXT_REG("power_managment", S_IRUGO | S_IWUGO, 0x30, 0x0),
/* Standard attributes for AD7888*/
static DEFINE_ZATTR_STD(ZDEV, zattr_dev_ad7888) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 12),
/* vref_src can be internal (0) or external (1)*/
ZATTR_REG(zdev, ZATTR_VREFTYPE, S_IRUGO | S_IWUGO, AD7888_VREF_ADDR, 0),
};
/* Extended attributes for AD7887 */
static struct zio_attribute zattr_dev_ext_ad7887[] = {
ZATTR_EXT_REG(AD788x_PM_NAME, S_IRUGO | S_IWUGO,
AD788x_PM_ADDR, 0x0),
/* 0 single channel, 1 dual channel*/
ZATTR_EXT_REG(AD7887_DUAL_NAME, S_IRUGO | S_IWUGO,
AD7887_SINDUAL_ADDR, 1),
};
/* Extended attributes for AD7888 */
static struct zio_attribute zattr_dev_ext_ad7888[] = {
ZATTR_EXT_REG(AD788x_PM_NAME, S_IRUGO | S_IWUGO,
AD788x_PM_ADDR, 0x0),
};
int ad7888_conf_set(struct kobject *kobj, struct zio_attribute *zattr,
static int ad788x_conf_set(struct kobject *kobj, struct zio_attribute *zattr,
uint32_t usr_val)
{
unsigned long mask = zattr->priv.addr;
struct ad788x *ad788x_tmp;
pr_debug("%s:%d", __func__, __LINE__);
ad788x_tmp = to_ad788x(to_zio_dev(kobj));
switch (mask) {
case 0x30: /* power management */
if (usr_val & ~mask)
case AD788x_PM_ADDR: /* power management */
if (usr_val < 4)
return -EINVAL;
ad7888_prv->cmd = (ad7888_prv->cmd & ~mask) | usr_val;
ad788x_tmp->cmd = (ad788x_tmp->cmd & ~mask) | usr_val;
break;
case 0x40: /* v_ref source */
if (usr_val > 1) /* single bit: 0 or 1*/
case AD7887_VREF_ADDR: /* v_ref source ad7887 */
case AD7888_VREF_ADDR: /* v_ref source ad7888 */
case AD7887_SINDUAL_ADDR: /* ad7887 single or dual */
if (usr_val > 1) /* single bit: 0 or 1 */
return -EINVAL;
ad7888_prv->cmd = (ad7888_prv->cmd & ~mask) |
(usr_val << (mask - 2));
ad788x_tmp->cmd = (ad788x_tmp->cmd & ~mask) |
(usr_val ? mask : 0);
break;
}
zattr->value = usr_val;
pr_debug("%s:%d 0x%x\n", __func__, __LINE__, ad788x_tmp->cmd);
return 0;
}
/* read from AD7888 and return the pointer to the data */
static void ad7888_complete(void *cont)
/* read from AD788x and return the pointer to the data */
static void ad788x_complete(void *cont)
{
struct ad7888_context *context = (struct ad7888_context *) cont;
struct ad788x_context *context = (struct ad788x_context *) cont;
struct ad788x *ad788x;
struct zio_channel *chan;
struct zio_cset *cset ;
uint16_t *data, *buf;
int i, j;
int i, j = 0;
cset = context->cset;
ad788x = to_ad788x(cset->zdev);
data = (uint16_t *) context->transfer.rx_buf;
data = &data[1];
/* demux data */
/*FIXME this loop is bugged, sometimes it doesn't work */
for (j = 0; j < cset->n_chan; ++j) {
buf = (uint16_t *)cset->chan[j].active_block->data;
for (i = 0; i < cset->ti->current_ctrl->nsamples; ++i)
buf[i] = data[i * cset->n_chan + j];
cset_for_each(cset, chan) {
buf = (uint16_t *)chan->active_block->data;
for (i = 0; i < context->nsamples; ++i)
buf[i] = data[i * context->chan_enable + j];
++j;
}
cset->ti->t_op->data_done(cset);
/* free context */
......@@ -88,20 +135,28 @@ static void ad7888_complete(void *cont)
kfree(context);
}
int ad7888_input_cset(struct zio_cset *cset)
static int ad788x_input_cset(struct zio_cset *cset)
{
int i, j, k, err = -EBUSY;
struct ad7888_context *context;
int i, k, err = -EBUSY;
struct ad788x *ad788x;
struct ad788x_context *context;
struct zio_channel *chan;
uint16_t *command;
uint32_t size;
uint32_t size, nsamples;
/* alloc context */
context = kzalloc(sizeof(struct ad7888_context), GFP_ATOMIC);
context = kzalloc(sizeof(struct ad788x_context), GFP_ATOMIC);
if (!context)
return -ENOMEM;
ad788x = to_ad788x(cset->zdev);
context->chan_enable = __get_n_chan_enabled(cset);
/* prepare SPI message and transfer */
size = (cset->n_chan * cset->ti->current_ctrl->nsamples * 2) + 2;
nsamples = cset->chan->current_ctrl->nsamples;
size = (context->chan_enable * nsamples * 2) + 2; /* +2 for SPI */
context->nsamples = nsamples;
context->transfer.tx_buf = kmalloc(size, GFP_ATOMIC);
context->transfer.rx_buf = kmalloc(size, GFP_ATOMIC);
if (!context->transfer.tx_buf || !context->transfer.rx_buf) {
......@@ -112,19 +167,21 @@ int ad7888_input_cset(struct zio_cset *cset)
context->transfer.len = size;
/* configure transfer buffer*/
command = (uint16_t *)context->transfer.tx_buf;
for (i = 0, k = 0; i < cset->ti->current_ctrl->nsamples; ++i)
for (j = 0; j < cset->n_chan; ++j, ++k)
command[k] = (j << 11) | ad7888_prv->cmd;
command[k] = ad7888_prv->cmd;
/* configure transfer buffer*/
for (i = 0, k = 0; i < nsamples; ++i)
cset_for_each(cset, chan)
command[k++] = (chan->index << AD788x_ADDR_SHIFT) |
ad788x->cmd;
command[k] = ad788x->cmd;
spi_message_init(&context->message);
context->message.complete = ad7888_complete;
context->message.complete = ad788x_complete;
context->message.context = context;
context->cset = cset;
spi_message_add_tail(&context->transfer, &context->message);
/* start acquisition */
err = spi_async_locked(ad7888_prv->spi, &context->message);
err = spi_async_locked(ad788x->spi, &context->message);
if (!err)
return -EAGAIN;
......@@ -134,94 +191,144 @@ int ad7888_input_cset(struct zio_cset *cset)
return err;
}
struct zio_sysfs_operations ad7888_s_op = {
.conf_set = ad7888_conf_set,
struct zio_sysfs_operations ad788x_s_op = {
.conf_set = ad788x_conf_set,
};
/* channel sets available */
static struct zio_cset ad7888_ain_cset[] = {
static struct zio_cset ad7887_ain_cset[] = { /* ad7887 cset */
{
.raw_io = ad788x_input_cset,
.ssize = 2,
.n_chan = 2,
.flags = ZCSET_TYPE_ANALOG | /* is analog */
ZIO_DIR_INPUT /* is input */,
},
};
static struct zio_cset ad7888_ain_cset[] = { /* ad7888 cset */
{
.raw_io = ad7888_input_cset,
.raw_io = ad788x_input_cset,
.ssize = 2,
.n_chan = 8,
.flags = ZCSET_TYPE_ANALOG | /* is analog */
ZIO_DIR_INPUT /* is input */,
},
};
struct zio_device zdev_ad7888 = {
.owner = THIS_MODULE,
.s_op = &ad7888_s_op,
.flags = 0,
.cset = ad7888_ain_cset,
.n_cset = ARRAY_SIZE(ad7888_ain_cset),
.zattr_set = {
.std_zattr = zattr_dev,
.ext_zattr = zattr_dev_ext,
.n_ext_attr = ARRAY_SIZE(zattr_dev_ext),
static struct ad788x ad788x_devices[] = {
[ID_AD7887] = {
.type = ID_AD7887,
.zdev = { /* ad7887 template */
.owner = THIS_MODULE,
.s_op = &ad788x_s_op,
.flags = 0,
.cset = ad7887_ain_cset,
.n_cset = ARRAY_SIZE(ad7887_ain_cset),
.zattr_set = {
.std_zattr = zattr_dev_ad7887,
.ext_zattr = zattr_dev_ext_ad7887,
.n_ext_attr = ARRAY_SIZE(zattr_dev_ext_ad7887),
},
},
},
[ID_AD7888] = {
.type = ID_AD7888,
.zdev = { /* ad7888 template */
.owner = THIS_MODULE,
.s_op = &ad788x_s_op,
.flags = 0,
.cset = ad7888_ain_cset,
.n_cset = ARRAY_SIZE(ad7888_ain_cset),
.zattr_set = {
.std_zattr = zattr_dev_ad7888,
.ext_zattr = zattr_dev_ext_ad7888,
.n_ext_attr = ARRAY_SIZE(zattr_dev_ext_ad7888),
},
},
},
};
static int __devinit ad7888_probe(struct spi_device *spi)
static int __devinit ad788x_probe(struct spi_device *spi)
{
const struct spi_device_id *spi_id;
struct ad788x *ad788x_tmp;
int err;
ad7888_prv = kmalloc(sizeof(struct ad7888_private), GFP_KERNEL);
ad7888_prv->spi = spi;
spi_set_drvdata(spi, &zdev_ad7888);
spi_id = spi_get_device_id(spi);
if (!spi_id)
return -ENODEV;
switch (spi_id->driver_data) {
case ID_AD7887:
ad788x_tmp = &ad788x_devices[ID_AD7887];
ad788x_tmp->cmd =
zattr_dev_ad7887[ZATTR_VREFTYPE].value <<
AD7887_VREF_SHIFT |
zattr_dev_ext_ad7887[1].value <<
AD7887_SINDUAL_SHIFT;
break;
case ID_AD7888:
ad788x_tmp = &ad788x_devices[ID_AD7888];
ad788x_tmp->cmd =
zattr_dev_ad7888[ZATTR_VREFTYPE].value <<
AD7888_VREF_SHIFT;
break;
default:
WARN(1, "%s cannot identify device\n", __func__);
break;
}
/* default value for PM is the same for all the ad788x */
ad788x_tmp->cmd |= zattr_dev_ext_ad7888[0].value << AD788x_PM_SHIFT;
if (ad788x_trigger)
ad788x_devices[ad788x_tmp->type].zdev.preferred_trigger =
ad788x_trigger;
if (ad788x_buffer)
ad788x_devices[ad788x_tmp->type].zdev.preferred_buffer =
ad788x_buffer;
ad788x_tmp->spi = spi;
spi_set_drvdata(spi, &ad788x_tmp);
spi->bits_per_word = 16;
err = spi_setup(spi);
if (err)
return err;
ad7888_prv->cmd = zattr_dev[ZATTR_VREFTYPE].value << 6 |
zattr_dev_ext[0].value << 0;
if (ad7888_trigger)
zdev_ad7888.preferred_trigger = ad7888_trigger;
if (ad7888_buffer)
zdev_ad7888.preferred_buffer = ad7888_buffer;
err = zio_register_dev(&zdev_ad7888, "ad7888");
if (err)
kfree(ad7888_prv);
return err;
return zio_register_dev(&ad788x_devices[ad788x_tmp->type].zdev,
spi_id->name);
}
static int __devexit ad7888_remove(struct spi_device *spi)
static int __devexit ad788x_remove(struct spi_device *spi)
{
zio_unregister_dev(&zdev_ad7888);
kfree(ad7888_prv);
struct ad788x *ad788x_tmp;
ad788x_tmp = spi_get_drvdata(spi);
zio_unregister_dev(&ad788x_devices[ad788x_tmp->type].zdev);
return 0;
}
static const struct spi_device_id ad7888_id[] = {
{"ad7888", ID_AD7888},
{}
};
static struct spi_driver ad7888_driver = {
static struct spi_driver ad788x_driver = {
.driver = {
.name = "ad7888",
.name = "ad788x",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.id_table = ad7888_id,
.probe = ad7888_probe,
.remove = __devexit_p(ad7888_remove),
.id_table = ad788x_id,
.probe = ad788x_probe,
.remove = __devexit_p(ad788x_remove),
};
static int __init ad7888_init(void)
static int __init ad788x_init(void)
{
return spi_register_driver(&ad7888_driver);
return spi_register_driver(&ad788x_driver);
}
static void __exit ad7888_exit(void)
static void __exit ad788x_exit(void)
{
driver_unregister(&ad7888_driver.driver);
driver_unregister(&ad788x_driver.driver);
}
module_init(ad7888_init);
module_exit(ad7888_exit);
module_init(ad788x_init);
module_exit(ad788x_exit);
MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>");
MODULE_DESCRIPTION("AD7888 driver for ZIO framework");
MODULE_DESCRIPTION("AD788x driver for ZIO framework");
MODULE_LICENSE("GPL");
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment