diff --git a/drivers/zio-ad7888.c b/drivers/zio-ad7888.c index 5df190b8b958b26e5e5ea6f96b4856d4cfddb313..578d984f91795c2e4fefbccd9da01eee8eb64e05 100644 --- a/drivers/zio-ad7888.c +++ b/drivers/zio-ad7888.c @@ -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");