F: drivers/acpi/pmic/
ACPI THERMAL DRIVER
S: Supported
W: https://01.org/linux-acpi
S: Maintained
F: drivers/platform/x86/adv_swbutton.c
+ ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml
+ F: drivers/iio/accel/adxl313*
+
ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346)
S: Supported
F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
F: drivers/input/misc/adxl34x.c
+ ADXL355 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml
+ F: drivers/iio/accel/adxl355.h
+ F: drivers/iio/accel/adxl355_core.c
+ F: drivers/iio/accel/adxl355_i2c.c
+ F: drivers/iio/accel/adxl355_spi.c
+
ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
S: Supported
F: drivers/dma/altera-msgdma.c
ALTERA PIO DRIVER
-M: Joyce Ooi <joyce.ooi@intel.com>
+M: Mun Yew Tham <mun.yew.tham@intel.com>
S: Maintained
F: drivers/gpio/gpio-altera.c
S: Maintained
F: drivers/platform/x86/amd-pmc.*
-AMD POWERPLAY
+AMD POWERPLAY AND SWSMU
S: Supported
T: git https://gitlab.freedesktop.org/agd5f/linux.git
-F: drivers/gpu/drm/amd/pm/powerplay/
+F: drivers/gpu/drm/amd/pm/
AMD PTDMA DRIVER
APPLE DART IOMMU DRIVER
S: Maintained
F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
ARM/APPLE MACHINE SUPPORT
S: Maintained
W: https://asahilinux.org
ARM/MStar/Sigmastar Armv7 SoC support
S: Maintained
W: http://linux-chenxing.org/
ARM/TEXAS INSTRUMENTS K3 ARCHITECTURE
S: Supported
F: arch/arm/mach-pxa/vpac270.c
ARM/VT8500 ARM ARCHITECTURE
-S: Maintained
+S: Orphan
F: Documentation/devicetree/bindings/i2c/i2c-wmt.txt
F: arch/arm/mach-vt8500/
F: drivers/clocksource/timer-vt8500.c
F: include/linux/async_tx.h
AT24 EEPROM DRIVER
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
F: Documentation/userspace-api/ebpf/
F: arch/*/net/*
F: include/linux/bpf*
+F: include/linux/btf*
F: include/linux/filter.h
F: include/trace/events/xdp.h
F: include/uapi/linux/bpf*
+F: include/uapi/linux/btf*
F: include/uapi/linux/filter.h
F: kernel/bpf/
F: kernel/trace/bpf_trace.c
BROADCOM NETXTREME-E ROCE DRIVER
S: Supported
W: http://www.broadcom.com
T: git git://git.samba.org/sfrench/cifs-2.6.git
F: Documentation/admin-guide/cifs/
F: fs/cifs/
-F: fs/cifs_common/
+F: fs/smbfs_common/
COMPACTPCI HOTPLUG CORE
F: Documentation/driver-api/generic-counter.rst
F: drivers/counter/
F: include/linux/counter.h
-F: include/linux/counter_enum.h
+F: include/uapi/linux/counter.h
+F: tools/counter/
CP2615 I2C DRIVER
FPGA MANAGER FRAMEWORK
S: Maintained
-W: http://www.rocketboards.org
Q: http://patchwork.kernel.org/project/linux-fpga/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mdf/linux-fpga.git
F: Documentation/devicetree/bindings/fpga/
S: Maintained
-F: Documentation/devicetree/bindings/net/fsl-fec.txt
+F: Documentation/devicetree/bindings/net/fsl,fec.yaml
F: drivers/net/ethernet/freescale/fec.h
F: drivers/net/ethernet/freescale/fec_main.c
F: drivers/net/ethernet/freescale/fec_ptp.c
GPIO SUBSYSTEM
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
F: drivers/iio/humidity/hts221*
HUAWEI ETHERNET DRIVER
-S: Supported
+S: Orphan
F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst
F: drivers/net/ethernet/huawei/hinic/
F: drivers/platform/x86/intel/atomisp2/led.c
INTEL BIOS SAR INT1092 DRIVER
S: Maintained
F: tools/power/x86/intel-speed-select/
INTEL STRATIX10 FIRMWARE DRIVERS
S: Maintained
F: Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu
S: Maintained
T: git git://git.samba.org/ksmbd.git
-F: fs/cifs_common/
F: fs/ksmbd/
+F: fs/smbfs_common/
KERNEL UNIT TESTING FRAMEWORK (KUnit)
S: Supported
F: Documentation/devicetree/bindings/net/dsa/marvell.txt
F: Documentation/networking/devlink/mv88e6xxx.rst
F: drivers/net/dsa/mv88e6xxx/
+F: include/linux/dsa/mv88e6xxx.h
F: include/linux/platform_data/mv88e6xxx.h
MARVELL ARMADA 3700 PHY DRIVERS
F: drivers/iio/proximity/mb1232.c
MAXIM MAX77650 PMIC MFD DRIVER
S: Maintained
F: Documentation/devicetree/bindings/*/*max77650.yaml
F: drivers/scsi/nsp32*
NIOS2 ARCHITECTURE
S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/lftan/nios2.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git
F: arch/nios2/
NITRO ENCLAVES (NE)
F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml
F: drivers/gpu/drm/imx/dcss/
+ NXP i.MX 8QXP ADC DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml
+ F: drivers/iio/adc/imx8qxp-adc.c
+
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
S: Maintained
F: drivers/pci/controller/pci-ixp4xx.c
PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
S: Supported
F: drivers/pci/controller/vmd.c
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
F: drivers/s390/crypto/vfio_ap_private.h
S390 VFIO-CCW DRIVER
S: Supported
F: drivers/char/pcmcia/scr24x_cs.c
-SCSI CDROM DRIVER
-S: Maintained
-W: http://www.kernel.dk
-F: drivers/scsi/sr*
-
SCSI RDMA PROTOCOL (SRP) INITIATOR
F: drivers/misc/phantom.c
F: include/uapi/linux/phantom.h
+ SENSEAIR SUNRISE 006-0-0007
+ S: Maintained
+ F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2
+ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml
+ F: drivers/iio/chemical/sunrise_co2.c
+
SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
S: Maintained
F: drivers/iio/chemical/scd30_i2c.c
F: drivers/iio/chemical/scd30_serial.c
+ SENSIRION SCD4X CARBON DIOXIDE SENSOR DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml
+ F: drivers/iio/chemical/scd4x.c
+
SENSIRION SGP40 GAS SENSOR DRIVER
S: Maintained
SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
S: Maintained
W: http://wiki.laptop.org/go/DCON
S: Maintained
-F: Documentation/devicetree/bindings/iio/adc/st,stm32-*.yaml
+F: Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml
+F: Documentation/devicetree/bindings/sound/st,stm32-*.yaml
F: sound/soc/stm/
STM32 TIMER/LPTIMER DRIVERS
F: arch/x86/boot/video*
SWIOTLB SUBSYSTEM
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/konrad/swiotlb.git
+W: http://git.infradead.org/users/hch/dma-mapping.git
+T: git git://git.infradead.org/users/hch/dma-mapping.git
F: arch/*/kernel/pci-swiotlb.c
F: include/linux/swiotlb.h
F: kernel/dma/swiotlb.c
SY8106A REGULATOR DRIVER
S: Maintained
-F: Documentation/devicetree/bindings/regulator/sy8106a-regulator.txt
+F: Documentation/devicetree/bindings/regulator/silergy,sy8106a.yaml
F: drivers/regulator/sy8106a-regulator.c
SYNC FILE FRAMEWORK
F: drivers/media/radio/radio-raremono.c
THERMAL
S: Supported
Q: https://patchwork.kernel.org/project/linux-pm/list/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git thermal
F: Documentation/devicetree/bindings/thermal/
F: drivers/thermal/
F: include/linux/cpu_cooling.h
TI DAVINCI MACHINE SUPPORT
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/nsekhar/linux-davinci.git
F: drivers/usb/misc/chaoskey.c
USB CYPRESS C67X00 DRIVER
-S: Maintained
+S: Orphan
F: drivers/usb/c67x00/
USB DAVICOM DM9601 DRIVER
S: Maintained
W: http://www.linux-usb.org/usbnet
F: tools/lib/bpf/xsk*
XEN BLOCK SUBSYSTEM
S: Supported
F: drivers/net/xen-netback/*
XEN PCI SUBSYSTEM
-M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+M: Juergen Gross <jgross@suse.com>
S: Supported
F: arch/x86/pci/*xen*
F: sound/xen/*
XEN SWIOTLB SUBSYSTEM
S: Supported
F: mm/zbud.c
ZD1211RW WIRELESS DRIVER
#include <linux/regmap.h>
#include <linux/iio/buffer.h>
+ #include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#define FXLS8962AF_INT_STATUS 0x00
#define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0)
+ #define FXLS8962AF_INT_STATUS_SRC_SDCD_OT BIT(4)
#define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5)
#define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7)
#define FXLS8962AF_TEMP_OUT 0x01
#define FXLS8962AF_ASLP_COUNT_LSB 0x1e
#define FXLS8962AF_INT_EN 0x20
+ #define FXLS8962AF_INT_EN_SDCD_OT_EN BIT(5)
#define FXLS8962AF_INT_EN_BUF_EN BIT(6)
#define FXLS8962AF_INT_PIN_SEL 0x21
#define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0)
#define FXLS8962AF_ORIENT_THS_REG 0x2c
#define FXLS8962AF_SDCD_INT_SRC1 0x2d
+ #define FXLS8962AF_SDCD_INT_SRC1_X_OT BIT(5)
+ #define FXLS8962AF_SDCD_INT_SRC1_X_POL BIT(4)
+ #define FXLS8962AF_SDCD_INT_SRC1_Y_OT BIT(3)
+ #define FXLS8962AF_SDCD_INT_SRC1_Y_POL BIT(2)
+ #define FXLS8962AF_SDCD_INT_SRC1_Z_OT BIT(1)
+ #define FXLS8962AF_SDCD_INT_SRC1_Z_POL BIT(0)
#define FXLS8962AF_SDCD_INT_SRC2 0x2e
#define FXLS8962AF_SDCD_CONFIG1 0x2f
+ #define FXLS8962AF_SDCD_CONFIG1_Z_OT_EN BIT(3)
+ #define FXLS8962AF_SDCD_CONFIG1_Y_OT_EN BIT(4)
+ #define FXLS8962AF_SDCD_CONFIG1_X_OT_EN BIT(5)
+ #define FXLS8962AF_SDCD_CONFIG1_OT_ELE BIT(7)
#define FXLS8962AF_SDCD_CONFIG2 0x30
+ #define FXLS8962AF_SDCD_CONFIG2_SDCD_EN BIT(7)
+ #define FXLS8962AF_SC2_REF_UPDM_AC GENMASK(6, 5)
#define FXLS8962AF_SDCD_OT_DBCNT 0x31
#define FXLS8962AF_SDCD_WT_DBCNT 0x32
#define FXLS8962AF_SDCD_LTHS_LSB 0x33
} scan;
int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
struct iio_mount_matrix orientation;
+ int irq;
u8 watermark;
+ u8 enable_event;
+ u16 lower_thres;
+ u16 upper_thres;
};
const struct regmap_config fxls8962af_regmap_conf = {
}
ret = regmap_bulk_read(data->regmap, chan->address,
- &raw_val, (chan->scan_type.storagebits / 8));
+ &raw_val, sizeof(data->lower_thres));
if (!is_active)
fxls8962af_power_off(data);
}
}
+ static int fxls8962af_event_setup(struct fxls8962af_data *data, int state)
+ {
+ /* Enable wakeup interrupt */
+ int mask = FXLS8962AF_INT_EN_SDCD_OT_EN;
+ int value = state ? mask : 0;
+
+ return regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, mask, value);
+ }
+
static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val)
{
struct fxls8962af_data *data = iio_priv(indio_dev);
return 0;
}
+ static int __fxls8962af_set_thresholds(struct fxls8962af_data *data,
+ const struct iio_chan_spec *chan,
+ enum iio_event_direction dir,
+ int val)
+ {
+ switch (dir) {
+ case IIO_EV_DIR_FALLING:
+ data->lower_thres = val;
+ return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_LTHS_LSB,
+ &data->lower_thres, sizeof(data->lower_thres));
+ case IIO_EV_DIR_RISING:
+ data->upper_thres = val;
+ return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_UTHS_LSB,
+ &data->upper_thres, sizeof(data->upper_thres));
+ default:
+ return -EINVAL;
+ }
+ }
+
+ static int fxls8962af_read_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+ {
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_LTHS_LSB,
+ &data->lower_thres, sizeof(data->lower_thres));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(data->lower_thres, chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_RISING:
+ ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_UTHS_LSB,
+ &data->upper_thres, sizeof(data->upper_thres));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(data->upper_thres, chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ static int fxls8962af_write_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+ {
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+ int ret, val_masked;
+
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ if (val < -2048 || val > 2047)
+ return -EINVAL;
+
+ if (data->enable_event)
+ return -EBUSY;
+
+ val_masked = val & GENMASK(11, 0);
+ if (fxls8962af_is_active(data)) {
+ ret = fxls8962af_standby(data);
+ if (ret)
+ return ret;
+
+ ret = __fxls8962af_set_thresholds(data, chan, dir, val_masked);
+ if (ret)
+ return ret;
+
+ return fxls8962af_active(data);
+ } else {
+ return __fxls8962af_set_thresholds(data, chan, dir, val_masked);
+ }
+ }
+
+ static int
+ fxls8962af_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+ {
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ return !!(FXLS8962AF_SDCD_CONFIG1_X_OT_EN & data->enable_event);
+ case IIO_MOD_Y:
+ return !!(FXLS8962AF_SDCD_CONFIG1_Y_OT_EN & data->enable_event);
+ case IIO_MOD_Z:
+ return !!(FXLS8962AF_SDCD_CONFIG1_Z_OT_EN & data->enable_event);
+ default:
+ return -EINVAL;
+ }
+ }
+
+ static int
+ fxls8962af_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+ {
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+ u8 enable_event, enable_bits;
+ int ret, value;
+
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ enable_bits = FXLS8962AF_SDCD_CONFIG1_X_OT_EN;
+ break;
+ case IIO_MOD_Y:
+ enable_bits = FXLS8962AF_SDCD_CONFIG1_Y_OT_EN;
+ break;
+ case IIO_MOD_Z:
+ enable_bits = FXLS8962AF_SDCD_CONFIG1_Z_OT_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (state)
+ enable_event = data->enable_event | enable_bits;
+ else
+ enable_event = data->enable_event & ~enable_bits;
+
+ if (data->enable_event == enable_event)
+ return 0;
+
+ ret = fxls8962af_standby(data);
+ if (ret)
+ return ret;
+
+ /* Enable events */
+ value = enable_event | FXLS8962AF_SDCD_CONFIG1_OT_ELE;
+ ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG1, value);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable update of SDCD_REF_X/Y/Z values with the current decimated and
+ * trimmed X/Y/Z acceleration input data. This allows for acceleration
+ * slope detection with Data(n) to Data(n–1) always used as the input
+ * to the window comparator.
+ */
+ value = enable_event ?
+ FXLS8962AF_SDCD_CONFIG2_SDCD_EN | FXLS8962AF_SC2_REF_UPDM_AC :
+ 0x00;
+ ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG2, value);
+ if (ret)
+ return ret;
+
+ ret = fxls8962af_event_setup(data, state);
+ if (ret)
+ return ret;
+
+ data->enable_event = enable_event;
+
+ if (data->enable_event) {
+ fxls8962af_active(data);
+ ret = fxls8962af_power_on(data);
+ } else {
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Not in buffered mode so disable power */
+ ret = fxls8962af_power_off(data);
+
+ iio_device_release_direct_mode(indio_dev);
+ }
+
+ return ret;
+ }
+
+ static const struct iio_event_spec fxls8962af_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ };
+
#define FXLS8962AF_CHANNEL(axis, reg, idx) { \
.type = IIO_ACCEL, \
.address = reg, \
.shift = 4, \
.endianness = IIO_BE, \
}, \
+ .event_spec = fxls8962af_event, \
+ .num_event_specs = ARRAY_SIZE(fxls8962af_event), \
}
#define FXLS8962AF_TEMP_CHANNEL { \
.read_raw = &fxls8962af_read_raw,
.write_raw = &fxls8962af_write_raw,
.write_raw_get_fmt = fxls8962af_write_raw_get_fmt,
+ .read_event_value = fxls8962af_read_event,
+ .write_event_value = fxls8962af_write_event,
+ .read_event_config = fxls8962af_read_event_config,
+ .write_event_config = fxls8962af_write_event_config,
.read_avail = fxls8962af_read_avail,
.hwfifo_set_watermark = fxls8962af_set_watermark,
};
ret = __fxls8962af_fifo_set_mode(data, false);
- fxls8962af_active(data);
+ if (data->enable_event)
+ fxls8962af_active(data);
return ret;
}
{
struct fxls8962af_data *data = iio_priv(indio_dev);
- return fxls8962af_power_off(data);
+ if (!data->enable_event)
+ fxls8962af_power_off(data);
+
+ return 0;
}
static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = {
return count;
}
+ static int fxls8962af_event_interrupt(struct iio_dev *indio_dev)
+ {
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+ s64 ts = iio_get_time_ns(indio_dev);
+ unsigned int reg;
+ u64 ev_code;
+ int ret;
+
+ ret = regmap_read(data->regmap, FXLS8962AF_SDCD_INT_SRC1, ®);
+ if (ret)
+ return ret;
+
+ if (reg & FXLS8962AF_SDCD_INT_SRC1_X_OT) {
+ ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_X_POL ?
+ IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
+ IIO_EV_TYPE_THRESH, ev_code), ts);
+ }
+
+ if (reg & FXLS8962AF_SDCD_INT_SRC1_Y_OT) {
+ ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Y_POL ?
+ IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
+ IIO_EV_TYPE_THRESH, ev_code), ts);
+ }
+
+ if (reg & FXLS8962AF_SDCD_INT_SRC1_Z_OT) {
+ ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Z_POL ?
+ IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
+ IIO_EV_TYPE_THRESH, ev_code), ts);
+ }
+
+ return 0;
+ }
+
static irqreturn_t fxls8962af_interrupt(int irq, void *p)
{
struct iio_dev *indio_dev = p;
if (reg & FXLS8962AF_INT_STATUS_SRC_BUF) {
ret = fxls8962af_fifo_flush(indio_dev);
- if (ret)
+ if (ret < 0)
return IRQ_NONE;
return IRQ_HANDLED;
}
+ if (reg & FXLS8962AF_INT_STATUS_SRC_SDCD_OT) {
+ ret = fxls8962af_event_interrupt(indio_dev);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+ }
+
return IRQ_NONE;
}
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
+ data->irq = irq;
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
if (ret)
return ret;
+ if (device_property_read_bool(dev, "wakeup-source"))
+ device_init_wakeup(dev, true);
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(fxls8962af_core_probe);
return fxls8962af_active(data);
}
+ static int __maybe_unused fxls8962af_suspend(struct device *dev)
+ {
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+
+ if (device_may_wakeup(dev) && data->enable_event) {
+ enable_irq_wake(data->irq);
+
+ /*
+ * Disable buffer, as the buffer is so small the device will wake
+ * almost immediately.
+ */
+ if (iio_buffer_enabled(indio_dev))
+ fxls8962af_buffer_predisable(indio_dev);
+ } else {
+ fxls8962af_runtime_suspend(dev);
+ }
+
+ return 0;
+ }
+
+ static int __maybe_unused fxls8962af_resume(struct device *dev)
+ {
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct fxls8962af_data *data = iio_priv(indio_dev);
+
+ if (device_may_wakeup(dev) && data->enable_event) {
+ disable_irq_wake(data->irq);
+
+ if (iio_buffer_enabled(indio_dev))
+ fxls8962af_buffer_postenable(indio_dev);
+ } else {
+ fxls8962af_runtime_resume(dev);
+ }
+
+ return 0;
+ }
+
const struct dev_pm_ops fxls8962af_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(fxls8962af_suspend, fxls8962af_resume)
SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend,
fxls8962af_runtime_resume, NULL)
};
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Aspeed AST2400/2500 ADC
+ * Aspeed AST2400/2500/2600 ADC
*
* Copyright (C) 2017 Google, Inc.
+ * Copyright (C) 2021 Aspeed Technology Inc.
+ *
+ * ADC clock formula:
+ * Ast2400/Ast2500:
+ * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1)
+ * Ast2600:
+ * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1)
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+ #include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+ #include <linux/bitfield.h>
+ #include <linux/regmap.h>
+ #include <linux/mfd/syscon.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#define ASPEED_REG_INTERRUPT_CONTROL 0x04
#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
#define ASPEED_REG_CLOCK_CONTROL 0x0C
- #define ASPEED_REG_MAX 0xC0
-
- #define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
- #define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
- #define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
-
- #define ASPEED_ENGINE_ENABLE BIT(0)
-
- #define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
+ #define ASPEED_REG_COMPENSATION_TRIM 0xC4
+ /*
+ * The register offset between 0xC8~0xCC can be read and won't affect the
+ * hardware logic in each version of ADC.
+ */
+ #define ASPEED_REG_MAX 0xD0
+
+ #define ASPEED_ADC_ENGINE_ENABLE BIT(0)
+ #define ASPEED_ADC_OP_MODE GENMASK(3, 1)
+ #define ASPEED_ADC_OP_MODE_PWR_DOWN 0
+ #define ASPEED_ADC_OP_MODE_STANDBY 1
+ #define ASPEED_ADC_OP_MODE_NORMAL 7
+ #define ASPEED_ADC_CTRL_COMPENSATION BIT(4)
+ #define ASPEED_ADC_AUTO_COMPENSATION BIT(5)
+ /*
+ * Bit 6 determines not only the reference voltage range but also the dividing
+ * circuit for battery sensing.
+ */
+ #define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6)
+ #define ASPEED_ADC_REF_VOLTAGE_2500mV 0
+ #define ASPEED_ADC_REF_VOLTAGE_1200mV 1
+ #define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2
+ #define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3
+ #define ASPEED_ADC_BAT_SENSING_DIV BIT(6)
+ #define ASPEED_ADC_BAT_SENSING_DIV_2_3 0
+ #define ASPEED_ADC_BAT_SENSING_DIV_1_3 1
+ #define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
+ #define ASPEED_ADC_CH7_MODE BIT(12)
+ #define ASPEED_ADC_CH7_NORMAL 0
+ #define ASPEED_ADC_CH7_BAT 1
+ #define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
+ #define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
+ #define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
#define ASPEED_ADC_INIT_POLLING_TIME 500
#define ASPEED_ADC_INIT_TIMEOUT 500000
+ /*
+ * When the sampling rate is too high, the ADC may not have enough charging
+ * time, resulting in a low voltage value. Thus, the default uses a slow
+ * sampling rate for most use cases.
+ */
+ #define ASPEED_ADC_DEF_SAMPLING_RATE 65000
+
+ struct aspeed_adc_trim_locate {
+ const unsigned int offset;
+ const unsigned int field;
+ };
struct aspeed_adc_model_data {
const char *model_name;
unsigned int min_sampling_rate; // Hz
unsigned int max_sampling_rate; // Hz
- unsigned int vref_voltage; // mV
+ unsigned int vref_fixed_mv;
bool wait_init_sequence;
+ bool need_prescaler;
+ bool bat_sense_sup;
+ u8 scaler_bit_width;
+ unsigned int num_channels;
+ const struct aspeed_adc_trim_locate *trim_locate;
+ };
+
+ struct adc_gain {
+ u8 mult;
+ u8 div;
};
struct aspeed_adc_data {
struct device *dev;
+ const struct aspeed_adc_model_data *model_data;
+ struct regulator *regulator;
void __iomem *base;
spinlock_t clk_lock;
+ struct clk_hw *fixed_div_clk;
struct clk_hw *clk_prescaler;
struct clk_hw *clk_scaler;
struct reset_control *rst;
+ int vref_mv;
+ u32 sample_period_ns;
+ int cv;
+ bool battery_sensing;
+ struct adc_gain battery_mode_gain;
};
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
.address = (_data_reg_addr), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
}
static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
ASPEED_CHAN(15, 0x2E),
};
+ #define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .address = (_data_reg_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ }
+ static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = {
+ ASPEED_CHAN(0, 0x10),
+ ASPEED_CHAN(1, 0x12),
+ ASPEED_CHAN(2, 0x14),
+ ASPEED_CHAN(3, 0x16),
+ ASPEED_CHAN(4, 0x18),
+ ASPEED_CHAN(5, 0x1A),
+ ASPEED_CHAN(6, 0x1C),
+ ASPEED_BAT_CHAN(7, 0x1E),
+ };
+
+ static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev)
+ {
+ struct device_node *syscon;
+ struct regmap *scu;
+ u32 scu_otp, trimming_val;
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ syscon = of_find_node_by_name(NULL, "syscon");
+ if (syscon == NULL) {
+ dev_warn(data->dev, "Couldn't find syscon node\n");
+ return -EOPNOTSUPP;
+ }
+ scu = syscon_node_to_regmap(syscon);
+ if (IS_ERR(scu)) {
+ dev_warn(data->dev, "Failed to get syscon regmap\n");
+ return -EOPNOTSUPP;
+ }
+ if (data->model_data->trim_locate) {
+ if (regmap_read(scu, data->model_data->trim_locate->offset,
+ &scu_otp)) {
+ dev_warn(data->dev,
+ "Failed to get adc trimming data\n");
+ trimming_val = 0x8;
+ } else {
+ trimming_val =
+ ((scu_otp) &
+ (data->model_data->trim_locate->field)) >>
+ __ffs(data->model_data->trim_locate->field);
+ }
+ dev_dbg(data->dev,
+ "trimming val = %d, offset = %08x, fields = %08x\n",
+ trimming_val, data->model_data->trim_locate->offset,
+ data->model_data->trim_locate->field);
+ writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM);
+ }
+ return 0;
+ }
+
+ static int aspeed_adc_compensation(struct iio_dev *indio_dev)
+ {
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ u32 index, adc_raw = 0;
+ u32 adc_engine_control_reg_val;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE;
+ adc_engine_control_reg_val |=
+ (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE);
+ /*
+ * Enable compensating sensing:
+ * After that, the input voltage of ADC will force to half of the reference
+ * voltage. So the expected reading raw data will become half of the max
+ * value. We can get compensating value = 0x200 - ADC read raw value.
+ * It is recommended to average at least 10 samples to get a final CV.
+ */
+ writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION |
+ ASPEED_ADC_CTRL_CHANNEL_ENABLE(0),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable compensating sensing mode need to wait some time for ADC stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+
+ for (index = 0; index < 16; index++) {
+ /*
+ * Waiting for the sampling period ensures that the value acquired
+ * is fresh each time.
+ */
+ ndelay(data->sample_period_ns);
+ adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address);
+ }
+ adc_raw >>= 4;
+ data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ dev_dbg(data->dev, "Compensating value = %d\n", data->cv);
+
+ return 0;
+ }
+
+ static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate)
+ {
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ if (rate < data->model_data->min_sampling_rate ||
+ rate > data->model_data->max_sampling_rate)
+ return -EINVAL;
+ /* Each sampling needs 12 clocks to convert.*/
+ clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE);
+ rate = clk_get_rate(data->clk_scaler->clk);
+ data->sample_period_ns = DIV_ROUND_UP_ULL(
+ (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate);
+ dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate,
+ data->sample_period_ns);
+
+ return 0;
+ }
+
static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct aspeed_adc_data *data = iio_priv(indio_dev);
- const struct aspeed_adc_model_data *model_data =
- of_device_get_match_data(data->dev);
+ u32 adc_engine_control_reg_val;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- *val = readw(data->base + chan->address);
+ if (data->battery_sensing && chan->channel == 7) {
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_CH7_MODE,
+ ASPEED_ADC_CH7_BAT) |
+ ASPEED_ADC_BAT_SENSING_ENABLE,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable battery sensing mode need to wait some time for adc stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+ *val = readw(data->base + chan->address);
+ *val = (*val * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ /* Restore control register value */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ } else
+ *val = readw(data->base + chan->address);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (data->battery_sensing && chan->channel == 7)
+ *val = (data->cv * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ else
+ *val = data->cv;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = model_data->vref_voltage;
+ *val = data->vref_mv;
*val2 = ASPEED_RESOLUTION_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- struct aspeed_adc_data *data = iio_priv(indio_dev);
- const struct aspeed_adc_model_data *model_data =
- of_device_get_match_data(data->dev);
-
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- if (val < model_data->min_sampling_rate ||
- val > model_data->max_sampling_rate)
- return -EINVAL;
-
- clk_set_rate(data->clk_scaler->clk,
- val * ASPEED_CLOCKS_PER_SAMPLE);
- return 0;
+ return aspeed_adc_set_sampling_rate(indio_dev, val);
case IIO_CHAN_INFO_SCALE:
case IIO_CHAN_INFO_RAW:
.debugfs_reg_access = aspeed_adc_reg_access,
};
+ static void aspeed_adc_unregister_fixed_divider(void *data)
+ {
+ struct clk_hw *clk = data;
+
+ clk_hw_unregister_fixed_factor(clk);
+ }
+
+ static void aspeed_adc_reset_assert(void *data)
+ {
+ struct reset_control *rst = data;
+
+ reset_control_assert(rst);
+ }
+
+ static void aspeed_adc_clk_disable_unprepare(void *data)
+ {
+ struct clk *clk = data;
+
+ clk_disable_unprepare(clk);
+ }
+
+ static void aspeed_adc_power_down(void *data)
+ {
+ struct aspeed_adc_data *priv_data = data;
+
+ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN),
+ priv_data->base + ASPEED_REG_ENGINE_CONTROL);
+ }
+
+ static void aspeed_adc_reg_disable(void *data)
+ {
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+ }
+
+ static int aspeed_adc_vref_config(struct iio_dev *indio_dev)
+ {
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ int ret;
+ u32 adc_engine_control_reg_val;
+
+ if (data->model_data->vref_fixed_mv) {
+ data->vref_mv = data->model_data->vref_fixed_mv;
+ return 0;
+ }
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ data->regulator = devm_regulator_get_optional(data->dev, "vref");
+ if (!IS_ERR(data->regulator)) {
+ ret = regulator_enable(data->regulator);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(
+ data->dev, aspeed_adc_reg_disable, data->regulator);
+ if (ret)
+ return ret;
+ data->vref_mv = regulator_get_voltage(data->regulator);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_HIGH),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_LOW),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Regulator voltage %d not support",
+ data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ } else {
+ if (PTR_ERR(data->regulator) != -ENODEV)
+ return PTR_ERR(data->regulator);
+ data->vref_mv = 2500000;
+ of_property_read_u32(data->dev->of_node,
+ "aspeed,int-vref-microvolt",
+ &data->vref_mv);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if (data->vref_mv == 2500)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_2500mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if (data->vref_mv == 1200)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_1200mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Voltage %d not support", data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+ }
+
static int aspeed_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct aspeed_adc_data *data;
- const struct aspeed_adc_model_data *model_data;
- const char *clk_parent_name;
int ret;
u32 adc_engine_control_reg_val;
+ unsigned long scaler_flags = 0;
+ char clk_name[32], clk_parent_name[32];
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev)
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
+ data->model_data = of_device_get_match_data(&pdev->dev);
+ platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->base))
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
- clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
-
- data->clk_prescaler = clk_hw_register_divider(
- &pdev->dev, "prescaler", clk_parent_name, 0,
- data->base + ASPEED_REG_CLOCK_CONTROL,
- 17, 15, 0, &data->clk_lock);
- if (IS_ERR(data->clk_prescaler))
- return PTR_ERR(data->clk_prescaler);
-
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
+ of_clk_get_parent_name(pdev->dev.of_node, 0));
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
+ data->model_data->model_name);
+ data->fixed_div_clk = clk_hw_register_fixed_factor(
+ &pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
+ if (IS_ERR(data->fixed_div_clk))
+ return PTR_ERR(data->fixed_div_clk);
+
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_unregister_fixed_divider,
+ data->fixed_div_clk);
+ if (ret)
+ return ret;
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name);
+
+ if (data->model_data->need_prescaler) {
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
+ data->model_data->model_name);
+ data->clk_prescaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, 0,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
+ &data->clk_lock);
+ if (IS_ERR(data->clk_prescaler))
+ return PTR_ERR(data->clk_prescaler);
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name),
+ clk_name);
+ scaler_flags = CLK_SET_RATE_PARENT;
+ }
/*
* Register ADC clock scaler downstream from the prescaler. Allow rate
* setting to adjust the prescaler as well.
*/
- data->clk_scaler = clk_hw_register_divider(
- &pdev->dev, "scaler", "prescaler",
- CLK_SET_RATE_PARENT,
- data->base + ASPEED_REG_CLOCK_CONTROL,
- 0, 10, 0, &data->clk_lock);
- if (IS_ERR(data->clk_scaler)) {
- ret = PTR_ERR(data->clk_scaler);
- goto scaler_error;
- }
-
- data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
+ data->model_data->model_name);
+ data->clk_scaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, scaler_flags,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 0,
+ data->model_data->scaler_bit_width, 0, &data->clk_lock);
+ if (IS_ERR(data->clk_scaler))
+ return PTR_ERR(data->clk_scaler);
+
+ data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(data->rst)) {
dev_err(&pdev->dev,
"invalid or missing reset controller device tree entry");
- ret = PTR_ERR(data->rst);
- goto reset_error;
+ return PTR_ERR(data->rst);
}
reset_control_deassert(data->rst);
- model_data = of_device_get_match_data(&pdev->dev);
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
+ data->rst);
+ if (ret)
+ return ret;
- if (model_data->wait_init_sequence) {
- /* Enable engine in normal mode. */
- writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE,
- data->base + ASPEED_REG_ENGINE_CONTROL);
+ ret = aspeed_adc_vref_config(indio_dev);
+ if (ret)
+ return ret;
+ if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid",
+ NULL)) {
+ ret = aspeed_adc_set_trim_data(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
+ NULL)) {
+ if (data->model_data->bat_sense_sup) {
+ data->battery_sensing = 1;
+ if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
+ ASPEED_ADC_BAT_SENSING_DIV) {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 1;
+ } else {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 2;
+ }
+ } else
+ dev_warn(&pdev->dev,
+ "Failed to enable battery-sensing mode\n");
+ }
+
+ ret = clk_prepare_enable(data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_clk_disable_unprepare,
+ data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = aspeed_adc_set_sampling_rate(indio_dev,
+ ASPEED_ADC_DEF_SAMPLING_RATE);
+ if (ret)
+ return ret;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |=
+ FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE;
+ /* Enable engine in normal mode. */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
+ data);
+ if (ret)
+ return ret;
+
+ if (data->model_data->wait_init_sequence) {
/* Wait for initial sequence complete. */
ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
adc_engine_control_reg_val,
ASPEED_ADC_INIT_POLLING_TIME,
ASPEED_ADC_INIT_TIMEOUT);
if (ret)
- goto poll_timeout_error;
+ return ret;
}
+ aspeed_adc_compensation(indio_dev);
/* Start all channels in normal mode. */
- ret = clk_prepare_enable(data->clk_scaler->clk);
- if (ret)
- goto clk_enable_error;
-
- adc_engine_control_reg_val = GENMASK(31, 16) |
- ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
writel(adc_engine_control_reg_val,
- data->base + ASPEED_REG_ENGINE_CONTROL);
+ data->base + ASPEED_REG_ENGINE_CONTROL);
- model_data = of_device_get_match_data(&pdev->dev);
- indio_dev->name = model_data->model_name;
+ indio_dev->name = data->model_data->model_name;
indio_dev->info = &aspeed_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = aspeed_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
-
- ret = iio_device_register(indio_dev);
- if (ret)
- goto iio_register_error;
-
- return 0;
+ indio_dev->channels = data->battery_sensing ?
+ aspeed_adc_iio_bat_channels :
+ aspeed_adc_iio_channels;
+ indio_dev->num_channels = data->model_data->num_channels;
- iio_register_error:
- writel(ASPEED_OPERATION_MODE_POWER_DOWN,
- data->base + ASPEED_REG_ENGINE_CONTROL);
- clk_disable_unprepare(data->clk_scaler->clk);
- clk_enable_error:
- poll_timeout_error:
- reset_control_assert(data->rst);
- reset_error:
- clk_hw_unregister_divider(data->clk_scaler);
- scaler_error:
- clk_hw_unregister_divider(data->clk_prescaler);
+ ret = devm_iio_device_register(data->dev, indio_dev);
return ret;
}
- static int aspeed_adc_remove(struct platform_device *pdev)
- {
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct aspeed_adc_data *data = iio_priv(indio_dev);
+ static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
+ .offset = 0x154,
+ .field = GENMASK(31, 28),
+ };
- iio_device_unregister(indio_dev);
- writel(ASPEED_OPERATION_MODE_POWER_DOWN,
- data->base + ASPEED_REG_ENGINE_CONTROL);
- clk_disable_unprepare(data->clk_scaler->clk);
- reset_control_assert(data->rst);
- clk_hw_unregister_divider(data->clk_scaler);
- clk_hw_unregister_divider(data->clk_prescaler);
+ static const struct aspeed_adc_trim_locate ast2600_adc0_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(3, 0),
+ };
- return 0;
- }
+ static const struct aspeed_adc_trim_locate ast2600_adc1_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(7, 4),
+ };
static const struct aspeed_adc_model_data ast2400_model_data = {
.model_name = "ast2400-adc",
- .vref_voltage = 2500, // mV
+ .vref_fixed_mv = 2500,
.min_sampling_rate = 10000,
.max_sampling_rate = 500000,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
};
static const struct aspeed_adc_model_data ast2500_model_data = {
.model_name = "ast2500-adc",
- .vref_voltage = 1800, // mV
+ .vref_fixed_mv = 1800,
.min_sampling_rate = 1,
.max_sampling_rate = 1000000,
.wait_init_sequence = true,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
+ .trim_locate = &ast2500_adc_trim,
+ };
+
+ static const struct aspeed_adc_model_data ast2600_adc0_model_data = {
+ .model_name = "ast2600-adc0",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc0_trim,
+ };
+
+ static const struct aspeed_adc_model_data ast2600_adc1_model_data = {
+ .model_name = "ast2600-adc1",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc1_trim,
};
static const struct of_device_id aspeed_adc_matches[] = {
{ .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
+ { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data },
+ { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data },
{},
};
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
static struct platform_driver aspeed_adc_driver = {
.probe = aspeed_adc_probe,
- .remove = aspeed_adc_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_adc_matches,
module_platform_driver(aspeed_adc_driver);
- MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
+ MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver");
MODULE_LICENSE("GPL");
#define MAX1027_NAVG_32 (0x03 << 2)
#define MAX1027_AVG_EN BIT(4)
+ /* Device can achieve 300ksps so we assume a 3.33us conversion delay */
+ #define MAX1027_CONVERSION_UDELAY 4
+
enum max1027_id {
max1027,
max1029,
.sign = 'u', \
.realbits = depth, \
.storagebits = 16, \
- .shift = 2, \
+ .shift = (depth == 10) ? 2 : 0, \
.endianness = IIO_BE, \
}, \
}
MAX1027_V_CHAN(11, depth)
#define MAX1X31_CHANNELS(depth) \
- MAX1X27_CHANNELS(depth), \
MAX1X29_CHANNELS(depth), \
MAX1027_V_CHAN(12, depth), \
MAX1027_V_CHAN(13, depth), \
MAX1X31_CHANNELS(12),
};
+ /*
+ * These devices are able to scan from 0 to N, N being the highest voltage
+ * channel requested by the user. The temperature can be included or not,
+ * but cannot be retrieved alone. Based on the below
+ * ->available_scan_masks, the core will select the most appropriate
+ * ->active_scan_mask and the "minimum" number of channels will be
+ * scanned and pushed to the buffers.
+ *
+ * For example, if the user wants channels 1, 4 and 5, all channels from
+ * 0 to 5 will be scanned and pushed to the IIO buffers. The core will then
+ * filter out the unneeded samples based on the ->active_scan_mask that has
+ * been selected and only channels 1, 4 and 5 will be available to the user
+ * in the shared buffer.
+ */
+ #define MAX1X27_SCAN_MASK_TEMP BIT(0)
+
+ #define MAX1X27_SCAN_MASKS(temp) \
+ GENMASK(1, 1 - (temp)), GENMASK(2, 1 - (temp)), \
+ GENMASK(3, 1 - (temp)), GENMASK(4, 1 - (temp)), \
+ GENMASK(5, 1 - (temp)), GENMASK(6, 1 - (temp)), \
+ GENMASK(7, 1 - (temp)), GENMASK(8, 1 - (temp))
+
+ #define MAX1X29_SCAN_MASKS(temp) \
+ MAX1X27_SCAN_MASKS(temp), \
+ GENMASK(9, 1 - (temp)), GENMASK(10, 1 - (temp)), \
+ GENMASK(11, 1 - (temp)), GENMASK(12, 1 - (temp))
+
+ #define MAX1X31_SCAN_MASKS(temp) \
+ MAX1X29_SCAN_MASKS(temp), \
+ GENMASK(13, 1 - (temp)), GENMASK(14, 1 - (temp)), \
+ GENMASK(15, 1 - (temp)), GENMASK(16, 1 - (temp))
+
static const unsigned long max1027_available_scan_masks[] = {
- 0x000001ff,
+ MAX1X27_SCAN_MASKS(0),
+ MAX1X27_SCAN_MASKS(1),
0x00000000,
};
static const unsigned long max1029_available_scan_masks[] = {
- 0x00001fff,
+ MAX1X29_SCAN_MASKS(0),
+ MAX1X29_SCAN_MASKS(1),
0x00000000,
};
static const unsigned long max1031_available_scan_masks[] = {
- 0x0001ffff,
+ MAX1X31_SCAN_MASKS(0),
+ MAX1X31_SCAN_MASKS(1),
0x00000000,
};
struct iio_trigger *trig;
__be16 *buffer;
struct mutex lock;
+ struct completion complete;
u8 reg ____cacheline_aligned;
};
+ static int max1027_wait_eoc(struct iio_dev *indio_dev)
+ {
+ struct max1027_state *st = iio_priv(indio_dev);
+ unsigned int conversion_time = MAX1027_CONVERSION_UDELAY;
+ int ret;
+
+ if (st->spi->irq) {
+ ret = wait_for_completion_timeout(&st->complete,
+ msecs_to_jiffies(1000));
+ reinit_completion(&st->complete);
+ if (!ret)
+ return -ETIMEDOUT;
+ } else {
+ if (indio_dev->active_scan_mask)
+ conversion_time *= hweight32(*indio_dev->active_scan_mask);
+
+ usleep_range(conversion_time, conversion_time * 2);
+ }
+
+ return 0;
+ }
+
+ /* Scan from chan 0 to the highest requested channel. Include temperature on demand. */
+ static int max1027_configure_chans_and_start(struct iio_dev *indio_dev)
+ {
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N;
+ st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2);
+ if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP)
+ st->reg |= MAX1027_TEMP;
+
+ return spi_write(st->spi, &st->reg, 1);
+ }
+
+ static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable)
+ {
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2;
+
+ /*
+ * Start acquisition on:
+ * MODE0: external hardware trigger wired to the cnvst input pin
+ * MODE2: conversion register write
+ */
+ if (enable)
+ st->reg |= MAX1027_CKS_MODE0;
+ else
+ st->reg |= MAX1027_CKS_MODE2;
+
+ return spi_write(st->spi, &st->reg, 1);
+ }
+
static int max1027_read_single_value(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
int ret;
struct max1027_state *st = iio_priv(indio_dev);
- if (iio_buffer_enabled(indio_dev)) {
- dev_warn(&indio_dev->dev, "trigger mode already enabled");
- return -EBUSY;
- }
-
- /* Start acquisition on conversion register write */
- st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2;
- ret = spi_write(st->spi, &st->reg, 1);
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "Failed to configure setup register\n");
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
return ret;
- }
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
if (ret < 0) {
dev_err(&indio_dev->dev,
"Failed to configure conversion register\n");
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
/*
* For an unknown reason, when we use the mode "10" (write
* conversion register), the interrupt doesn't occur every time.
- * So we just wait 1 ms.
+ * So we just wait the maximum conversion time and deliver the value.
*/
- mdelay(1);
+ ret = max1027_wait_eoc(indio_dev);
+ if (ret)
+ return ret;
/* Read result */
ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2);
+
+ iio_device_release_direct_mode(indio_dev);
+
if (ret < 0)
return ret;
}
static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
- unsigned reg, unsigned writeval,
- unsigned *readval)
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
{
struct max1027_state *st = iio_priv(indio_dev);
u8 *val = (u8 *)st->buffer;
return spi_write(st->spi, val, 1);
}
- static int max1027_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
+ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state)
{
- struct max1027_state *st = iio_priv(indio_dev);
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ /*
+ * In order to disable the convst trigger, start acquisition on
+ * conversion register write, which basically disables triggering
+ * conversions upon cnvst changes and thus has the effect of disabling
+ * the external hardware trigger.
+ */
+ ret = max1027_enable_trigger(indio_dev, state);
+ if (ret)
+ return ret;
- if (st->trig != trig)
- return -EINVAL;
+ if (state) {
+ ret = max1027_configure_chans_and_start(indio_dev);
+ if (ret)
+ return ret;
+ }
return 0;
}
- static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
+ static int max1027_read_scan(struct iio_dev *indio_dev)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct max1027_state *st = iio_priv(indio_dev);
+ unsigned int scanned_chans;
int ret;
- if (state) {
- /* Start acquisition on cnvst */
- st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 |
- MAX1027_REF_MODE2;
- ret = spi_write(st->spi, &st->reg, 1);
- if (ret < 0)
- return ret;
+ scanned_chans = fls(*indio_dev->active_scan_mask) - 1;
+ if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP)
+ scanned_chans++;
- /* Scan from 0 to max */
- st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) |
- MAX1027_SCAN_N_M | MAX1027_TEMP;
- ret = spi_write(st->spi, &st->reg, 1);
- if (ret < 0)
- return ret;
- } else {
- /* Start acquisition on conversion register write */
- st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 |
- MAX1027_REF_MODE2;
- ret = spi_write(st->spi, &st->reg, 1);
- if (ret < 0)
- return ret;
- }
+ /* fill buffer with all channel */
+ ret = spi_read(st->spi, st->buffer, scanned_chans * 2);
+ if (ret < 0)
+ return ret;
+
+ iio_push_to_buffers(indio_dev, st->buffer);
return 0;
}
+ static irqreturn_t max1027_handler(int irq, void *private)
+ {
+ struct iio_dev *indio_dev = private;
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ /*
+ * If buffers are disabled (raw read) or when using external triggers,
+ * we just need to unlock the waiters which will then handle the data.
+ *
+ * When using the internal trigger, we must hand-off the choice of the
+ * handler to the core which will then lookup through the interrupt tree
+ * for the right handler registered with iio_triggered_buffer_setup()
+ * to execute, as this trigger might very well be used in conjunction
+ * with another device. The core will then call the relevant handler to
+ * perform the data processing step.
+ */
+ if (!iio_buffer_enabled(indio_dev))
+ complete(&st->complete);
+ else
+ iio_trigger_poll(indio_dev->trig);
+
+ return IRQ_HANDLED;
+ }
+
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
- struct max1027_state *st = iio_priv(indio_dev);
+ int ret;
- pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private);
+ if (!iio_trigger_using_own(indio_dev)) {
+ ret = max1027_configure_chans_and_start(indio_dev);
+ if (ret)
+ goto out;
- /* fill buffer with all channel */
- spi_read(st->spi, st->buffer, indio_dev->masklength * 2);
+ /* This is a threaded handler, it is fine to wait for an IRQ */
+ ret = max1027_wait_eoc(indio_dev);
+ if (ret)
+ goto out;
+ }
- iio_push_to_buffers(indio_dev, st->buffer);
+ ret = max1027_read_scan(indio_dev);
+ out:
+ if (ret)
+ dev_err(&indio_dev->dev,
+ "Cannot read scanned values (%d)\n", ret);
iio_trigger_notify_done(indio_dev->trig);
static const struct iio_trigger_ops max1027_trigger_ops = {
.validate_device = &iio_trigger_validate_own_device,
- .set_trigger_state = &max1027_set_trigger_state,
+ .set_trigger_state = &max1027_set_cnvst_trigger_state,
};
static const struct iio_info max1027_info = {
.read_raw = &max1027_read_raw,
- .validate_trigger = &max1027_validate_trigger,
.debugfs_reg_access = &max1027_debugfs_reg_access,
};
struct iio_dev *indio_dev;
struct max1027_state *st;
- pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi);
-
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
- if (indio_dev == NULL) {
+ if (!indio_dev) {
pr_err("Can't allocate iio device\n");
return -ENOMEM;
}
st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data];
mutex_init(&st->lock);
+ init_completion(&st->complete);
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->info = &max1027_info;
indio_dev->available_scan_masks = st->info->available_scan_masks;
st->buffer = devm_kmalloc_array(&indio_dev->dev,
- indio_dev->num_channels, 2,
- GFP_KERNEL);
- if (st->buffer == NULL) {
- dev_err(&indio_dev->dev, "Can't allocate buffer\n");
+ indio_dev->num_channels, 2,
+ GFP_KERNEL);
+ if (!st->buffer)
return -ENOMEM;
+
+ /* Enable triggered buffers */
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &max1027_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to setup buffer\n");
+ return ret;
}
+ /* If there is an EOC interrupt, register the cnvst hardware trigger */
if (spi->irq) {
- ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
- &iio_pollfunc_store_time,
- &max1027_trigger_handler,
- NULL);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "Failed to setup buffer\n");
- return ret;
- }
-
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
indio_dev->name);
- if (st->trig == NULL) {
+ if (!st->trig) {
ret = -ENOMEM;
dev_err(&indio_dev->dev,
"Failed to allocate iio trigger\n");
return ret;
}
- ret = devm_request_threaded_irq(&spi->dev, spi->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_FALLING,
- spi->dev.driver->name,
- st->trig);
+ ret = devm_request_irq(&spi->dev, spi->irq, max1027_handler,
+ IRQF_TRIGGER_FALLING,
+ spi->dev.driver->name, indio_dev);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
return ret;
return ret;
}
+ /* Assume conversion on register write for now */
+ ret = max1027_enable_trigger(indio_dev, false);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(&spi->dev, indio_dev);
}
if (dec > st->info->max_dec)
dec = st->info->max_dec;
- ret = adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec);
+ ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec);
if (ret)
goto error;
+ adis_dev_unlock(&st->adis);
/*
* If decimation is used, then gyro and accel data will have meaningful
* bits on the LSB registers. This info is used on the trigger handler.
[ADIS16475_DIAG_STAT_CLK] = "Clock error",
};
- static int adis16475_enable_irq(struct adis *adis, bool enable)
- {
- /*
- * There is no way to gate the data-ready signal internally inside the
- * ADIS16475. We can only control it's polarity...
- */
- if (enable)
- enable_irq(adis->spi->irq);
- else
- disable_irq(adis->spi->irq);
-
- return 0;
- }
-
#define ADIS16475_DATA(_prod_id, _timeouts) \
{ \
.msc_ctrl_reg = ADIS16475_REG_MSG_CTRL, \
BIT(ADIS16475_DIAG_STAT_SENSOR) | \
BIT(ADIS16475_DIAG_STAT_MEMORY) | \
BIT(ADIS16475_DIAG_STAT_CLK), \
- .enable_irq = adis16475_enable_irq, \
+ .unmasked_drdy = true, \
.timeouts = (_timeouts), \
.burst_reg_cmd = ADIS16475_REG_GLOB_CMD, \
.burst_len = ADIS16475_BURST_MAX_DATA, \
return -EINVAL;
}
- /* We cannot mask the interrupt so ensure it's not enabled at request */
- st->adis.irq_flag |= IRQF_NO_AUTOEN;
-
val = ADIS16475_MSG_CTRL_DR_POL(polarity);
ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL,
ADIS16475_MSG_CTRL_DR_POL_MASK, val);