Commit 8210cfe9 authored by Barry Song's avatar Barry Song Committed by Greg Kroah-Hartman

staging: iio: meter: new driver for ADE7758 devices

Signed-off-by: default avatarBarry Song <barry.song@analog.com>
Signed-off-by: default avatarMichael Hennerich <michael.hennerich@analog.com>
Acked-by: default avatarJonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 8d97a587
......@@ -16,3 +16,12 @@ config ADE7754
help
Say yes here to build support for Analog Devices ADE7754 Polyphase
Multifunction Energy Metering IC Driver.
config ADE7758
tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver"
depends on SPI
select IIO_TRIGGER if IIO_RING_BUFFER
select IIO_SW_RING if IIO_RING_BUFFER
help
Say yes here to build support for Analog Devices ADE7758 Polyphase
Multifunction Energy Metering IC with Per Phase Information Driver.
......@@ -4,3 +4,7 @@
obj-$(CONFIG_ADE7753) += ade7753.o
obj-$(CONFIG_ADE7754) += ade7754.o
ade7758-y := ade7758_core.o
ade7758-$(CONFIG_IIO_RING_BUFFER) += ade7758_ring.o ade7758_trigger.o
obj-$(CONFIG_ADE7758) += ade7758.o
#ifndef _ADE7758_H
#define _ADE7758_H
#define ADE7758_AWATTHR 0x01
#define ADE7758_BWATTHR 0x02
#define ADE7758_CWATTHR 0x03
#define ADE7758_AVARHR 0x04
#define ADE7758_BVARHR 0x05
#define ADE7758_CVARHR 0x06
#define ADE7758_AVAHR 0x07
#define ADE7758_BVAHR 0x08
#define ADE7758_CVAHR 0x09
#define ADE7758_AIRMS 0x0A
#define ADE7758_BIRMS 0x0B
#define ADE7758_CIRMS 0x0C
#define ADE7758_AVRMS 0x0D
#define ADE7758_BVRMS 0x0E
#define ADE7758_CVRMS 0x0F
#define ADE7758_FREQ 0x10
#define ADE7758_TEMP 0x11
#define ADE7758_WFORM 0x12
#define ADE7758_OPMODE 0x13
#define ADE7758_MMODE 0x14
#define ADE7758_WAVMODE 0x15
#define ADE7758_COMPMODE 0x16
#define ADE7758_LCYCMODE 0x17
#define ADE7758_MASK 0x18
#define ADE7758_STATUS 0x19
#define ADE7758_RSTATUS 0x1A
#define ADE7758_ZXTOUT 0x1B
#define ADE7758_LINECYC 0x1C
#define ADE7758_SAGCYC 0x1D
#define ADE7758_SAGLVL 0x1E
#define ADE7758_VPINTLVL 0x1F
#define ADE7758_IPINTLVL 0x20
#define ADE7758_VPEAK 0x21
#define ADE7758_IPEAK 0x22
#define ADE7758_GAIN 0x23
#define ADE7758_AVRMSGAIN 0x24
#define ADE7758_BVRMSGAIN 0x25
#define ADE7758_CVRMSGAIN 0x26
#define ADE7758_AIGAIN 0x27
#define ADE7758_BIGAIN 0x28
#define ADE7758_CIGAIN 0x29
#define ADE7758_AWG 0x2A
#define ADE7758_BWG 0x2B
#define ADE7758_CWG 0x2C
#define ADE7758_AVARG 0x2D
#define ADE7758_BVARG 0x2E
#define ADE7758_CVARG 0x2F
#define ADE7758_AVAG 0x30
#define ADE7758_BVAG 0x31
#define ADE7758_CVAG 0x32
#define ADE7758_AVRMSOS 0x33
#define ADE7758_BVRMSOS 0x34
#define ADE7758_CVRMSOS 0x35
#define ADE7758_AIRMSOS 0x36
#define ADE7758_BIRMSOS 0x37
#define ADE7758_CIRMSOS 0x38
#define ADE7758_AWAITOS 0x39
#define ADE7758_BWAITOS 0x3A
#define ADE7758_CWAITOS 0x3B
#define ADE7758_AVAROS 0x3C
#define ADE7758_BVAROS 0x3D
#define ADE7758_CVAROS 0x3E
#define ADE7758_APHCAL 0x3F
#define ADE7758_BPHCAL 0x40
#define ADE7758_CPHCAL 0x41
#define ADE7758_WDIV 0x42
#define ADE7758_VADIV 0x44
#define ADE7758_VARDIV 0x43
#define ADE7758_APCFNUM 0x45
#define ADE7758_APCFDEN 0x46
#define ADE7758_VARCFNUM 0x47
#define ADE7758_VARCFDEN 0x48
#define ADE7758_CHKSUM 0x7E
#define ADE7758_VERSION 0x7F
#define ADE7758_READ_REG(a) a
#define ADE7758_WRITE_REG(a) ((a) | 0x80)
#define ADE7758_MAX_TX 8
#define ADE7758_MAX_RX 4
#define ADE7758_STARTUP_DELAY 1
#define ADE7758_SPI_SLOW (u32)(300 * 1000)
#define ADE7758_SPI_BURST (u32)(1000 * 1000)
#define ADE7758_SPI_FAST (u32)(2000 * 1000)
#define DRIVER_NAME "ade7758"
/**
* struct ade7758_state - device instance specific data
* @us: actual spi_device
* @work_trigger_to_ring: bh for triggered event handling
* @inter: used to check if new interrupt has been triggered
* @last_timestamp: passing timestamp from th to bh of interrupt handler
* @indio_dev: industrial I/O device structure
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
* @rx: recieve buffer
* @buf_lock: mutex to protect tx and rx
**/
struct ade7758_state {
struct spi_device *us;
struct work_struct work_trigger_to_ring;
s64 last_timestamp;
struct iio_dev *indio_dev;
struct iio_trigger *trig;
u8 *tx;
u8 *rx;
struct mutex buf_lock;
};
#ifdef CONFIG_IIO_RING_BUFFER
/* At the moment triggers are only used for ring buffer
* filling. This may change!
*/
enum ade7758_scan {
ADE7758_SCAN_WFORM,
};
void ade7758_remove_trigger(struct iio_dev *indio_dev);
int ade7758_probe_trigger(struct iio_dev *indio_dev);
ssize_t ade7758_read_data_from_ring(struct device *dev,
struct device_attribute *attr,
char *buf);
int ade7758_configure_ring(struct iio_dev *indio_dev);
void ade7758_unconfigure_ring(struct iio_dev *indio_dev);
int ade7758_initialize_ring(struct iio_ring_buffer *ring);
void ade7758_uninitialize_ring(struct iio_ring_buffer *ring);
int ade7758_set_irq(struct device *dev, bool enable);
#else /* CONFIG_IIO_RING_BUFFER */
static inline void ade7758_remove_trigger(struct iio_dev *indio_dev)
{
}
static inline int ade7758_probe_trigger(struct iio_dev *indio_dev)
{
return 0;
}
static inline ssize_t
ade7758_read_data_from_ring(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return 0;
}
static int ade7758_configure_ring(struct iio_dev *indio_dev)
{
return 0;
}
static inline void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
{
}
static inline int ade7758_initialize_ring(struct iio_ring_buffer *ring)
{
return 0;
}
static inline void ade7758_uninitialize_ring(struct iio_ring_buffer *ring)
{
}
#endif /* CONFIG_IIO_RING_BUFFER */
#endif
This diff is collapsed.
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include "../iio.h"
#include "../sysfs.h"
#include "../ring_sw.h"
#include "../accel/accel.h"
#include "../trigger.h"
#include "ade7758.h"
/**
* combine_8_to_32() utility function to munge to u8s into u32
**/
static inline u32 combine_8_to_32(u8 lower, u8 mid, u8 upper)
{
u32 _lower = lower;
u32 _mid = mid;
u32 _upper = upper;
return _lower | (_mid << 8) | (_upper << 16);
}
static IIO_SCAN_EL_C(wform, ADE7758_SCAN_WFORM, ADE7758_WFORM, NULL);
static IIO_CONST_ATTR_SCAN_EL_TYPE(wform, s, 24, 32);
static IIO_SCAN_EL_TIMESTAMP(1);
static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64);
static struct attribute *ade7758_scan_el_attrs[] = {
&iio_scan_el_wform.dev_attr.attr,
&iio_const_attr_wform_index.dev_attr.attr,
&iio_const_attr_wform_type.dev_attr.attr,
&iio_scan_el_timestamp.dev_attr.attr,
&iio_const_attr_timestamp_index.dev_attr.attr,
&iio_const_attr_timestamp_type.dev_attr.attr,
NULL,
};
static struct attribute_group ade7758_scan_el_group = {
.attrs = ade7758_scan_el_attrs,
.name = "scan_elements",
};
/**
* ade7758_poll_func_th() top half interrupt handler called by trigger
* @private_data: iio_dev
**/
static void ade7758_poll_func_th(struct iio_dev *indio_dev, s64 time)
{
struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
st->last_timestamp = time;
schedule_work(&st->work_trigger_to_ring);
/* Indicate that this interrupt is being handled */
/* Technically this is trigger related, but without this
* handler running there is currently no way for the interrupt
* to clear.
*/
}
/**
* ade7758_spi_read_burst() - read all data registers
* @dev: device associated with child of actual device (iio_dev or iio_trig)
* @rx: somewhere to pass back the value read (min size is 24 bytes)
**/
static int ade7758_spi_read_burst(struct device *dev, u8 *rx)
{
struct spi_message msg;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ade7758_state *st = iio_dev_get_devdata(indio_dev);
int ret;
struct spi_transfer xfers[] = {
{
.tx_buf = st->tx,
.rx_buf = rx,
.bits_per_word = 8,
.len = 4,
}, {
.tx_buf = st->tx + 4,
.rx_buf = rx,
.bits_per_word = 8,
.len = 4,
},
};
mutex_lock(&st->buf_lock);
st->tx[0] = ADE7758_READ_REG(ADE7758_RSTATUS);
st->tx[1] = 0;
st->tx[2] = 0;
st->tx[3] = 0;
st->tx[4] = ADE7758_READ_REG(ADE7758_WFORM);
st->tx[5] = 0;
st->tx[6] = 0;
st->tx[7] = 0;
spi_message_init(&msg);
spi_message_add_tail(&xfers[0], &msg);
spi_message_add_tail(&xfers[1], &msg);
ret = spi_sync(st->us, &msg);
if (ret)
dev_err(&st->us->dev, "problem when reading WFORM value\n");
mutex_unlock(&st->buf_lock);
return ret;
}
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
* specific to be rolled into the core.
*/
static void ade7758_trigger_bh_to_ring(struct work_struct *work_s)
{
struct ade7758_state *st
= container_of(work_s, struct ade7758_state,
work_trigger_to_ring);
struct iio_ring_buffer *ring = st->indio_dev->ring;
int i = 0;
s32 *data;
size_t datasize = ring->access.get_bytes_per_datum(ring);
data = kmalloc(datasize, GFP_KERNEL);
if (data == NULL) {
dev_err(&st->us->dev, "memory alloc failed in ring bh");
return;
}
if (ring->scan_count)
if (ade7758_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0)
for (; i < ring->scan_count; i++)
data[i] = combine_8_to_32(st->rx[i*2+2],
st->rx[i*2+1],
st->rx[i*2]);
/* Guaranteed to be aligned with 8 byte boundary */
if (ring->scan_timestamp)
*((s64 *)
(((u32)data + 4 * ring->scan_count + 4) & ~0x7)) =
st->last_timestamp;
ring->access.store_to(ring,
(u8 *)data,
st->last_timestamp);
iio_trigger_notify_done(st->indio_dev->trig);
kfree(data);
return;
}
void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
{
kfree(indio_dev->pollfunc);
iio_sw_rb_free(indio_dev->ring);
}
int ade7758_configure_ring(struct iio_dev *indio_dev)
{
int ret = 0;
struct ade7758_state *st = indio_dev->dev_data;
struct iio_ring_buffer *ring;
INIT_WORK(&st->work_trigger_to_ring, ade7758_trigger_bh_to_ring);
ring = iio_sw_rb_allocate(indio_dev);
if (!ring) {
ret = -ENOMEM;
return ret;
}
indio_dev->ring = ring;
/* Effectively select the ring buffer implementation */
iio_ring_sw_register_funcs(&ring->access);
ring->bpe = 4;
ring->scan_el_attrs = &ade7758_scan_el_group;
ring->scan_timestamp = true;
ring->preenable = &iio_sw_ring_preenable;
ring->postenable = &iio_triggered_ring_postenable;
ring->predisable = &iio_triggered_ring_predisable;
ring->owner = THIS_MODULE;
/* Set default scan mode */
iio_scan_mask_set(ring, iio_scan_el_wform.number);
ret = iio_alloc_pollfunc(indio_dev, NULL, &ade7758_poll_func_th);
if (ret)
goto error_iio_sw_rb_free;
indio_dev->modes |= INDIO_RING_TRIGGERED;
return 0;
error_iio_sw_rb_free:
iio_sw_rb_free(indio_dev->ring);
return ret;
}
int ade7758_initialize_ring(struct iio_ring_buffer *ring)
{
return iio_ring_buffer_register(ring, 0);
}
void ade7758_uninitialize_ring(struct iio_ring_buffer *ring)
{
iio_ring_buffer_unregister(ring);
}
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
#include "../iio.h"
#include "../sysfs.h"
#include "../trigger.h"
#include "ade7758.h"
/**
* ade7758_data_rdy_trig_poll() the event handler for the data rdy trig
**/
static int ade7758_data_rdy_trig_poll(struct iio_dev *dev_info,
int index,
s64 timestamp,
int no_test)
{
struct ade7758_state *st = iio_dev_get_devdata(dev_info);
struct iio_trigger *trig = st->trig;
iio_trigger_poll(trig, timestamp);
return IRQ_HANDLED;
}
IIO_EVENT_SH(data_rdy_trig, &ade7758_data_rdy_trig_poll);
static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
static struct attribute *ade7758_trigger_attrs[] = {
&dev_attr_name.attr,
NULL,
};
static const struct attribute_group ade7758_trigger_attr_group = {
.attrs = ade7758_trigger_attrs,
};
/**
* ade7758_data_rdy_trigger_set_state() set datardy interrupt state
**/
static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct ade7758_state *st = trig->private_data;
struct iio_dev *indio_dev = st->indio_dev;
int ret = 0;
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
ret = ade7758_set_irq(&st->indio_dev->dev, state);
if (state == false) {
iio_remove_event_from_list(&iio_event_data_rdy_trig,
&indio_dev->interrupts[0]
->ev_list);
/* possible quirk with handler currently worked around
by ensuring the work queue is empty */
flush_scheduled_work();
} else {
iio_add_event_to_list(&iio_event_data_rdy_trig,
&indio_dev->interrupts[0]->ev_list);
}
return ret;
}
/**
* ade7758_trig_try_reen() try renabling irq for data rdy trigger
* @trig: the datardy trigger
**/
static int ade7758_trig_try_reen(struct iio_trigger *trig)
{
struct ade7758_state *st = trig->private_data;
enable_irq(st->us->irq);
/* irq reenabled so success! */
return 0;
}
int ade7758_probe_trigger(struct iio_dev *indio_dev)
{
int ret;
struct ade7758_state *st = indio_dev->dev_data;
st->trig = iio_allocate_trigger();
st->trig->name = kasprintf(GFP_KERNEL,
"ade7758-dev%d",
indio_dev->id);
if (!st->trig->name) {
ret = -ENOMEM;
goto error_free_trig;
}
st->trig->dev.parent = &st->us->dev;
st->trig->owner = THIS_MODULE;
st->trig->private_data = st;
st->trig->set_trigger_state = &ade7758_data_rdy_trigger_set_state;
st->trig->try_reenable = &ade7758_trig_try_reen;
st->trig->control_attrs = &ade7758_trigger_attr_group;
ret = iio_trigger_register(st->trig);
/* select default trigger */
indio_dev->trig = st->trig;
if (ret)
goto error_free_trig_name;
return 0;
error_free_trig_name:
kfree(st->trig->name);
error_free_trig:
iio_free_trigger(st->trig);
return ret;
}
void ade7758_remove_trigger(struct iio_dev *indio_dev)
{
struct ade7758_state *state = indio_dev->dev_data;
iio_trigger_unregister(state->trig);
kfree(state->trig->name);
iio_free_trigger(state->trig);
}
Markdown is supported
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