linux/drivers/iio/adc/xilinx-xadc-events.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Xilinx XADC driver
   4 *
   5 * Copyright 2013 Analog Devices Inc.
   6 *  Author: Lars-Peter Clauen <lars@metafoo.de>
   7 */
   8
   9#include <linux/iio/events.h>
  10#include <linux/iio/iio.h>
  11#include <linux/kernel.h>
  12
  13#include "xilinx-xadc.h"
  14
  15static const struct iio_chan_spec *xadc_event_to_channel(
  16        struct iio_dev *indio_dev, unsigned int event)
  17{
  18        switch (event) {
  19        case XADC_THRESHOLD_OT_MAX:
  20        case XADC_THRESHOLD_TEMP_MAX:
  21                return &indio_dev->channels[0];
  22        case XADC_THRESHOLD_VCCINT_MAX:
  23        case XADC_THRESHOLD_VCCAUX_MAX:
  24                return &indio_dev->channels[event];
  25        default:
  26                return &indio_dev->channels[event-1];
  27        }
  28}
  29
  30static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event)
  31{
  32        const struct iio_chan_spec *chan;
  33
  34        /* Temperature threshold error, we don't handle this yet */
  35        if (event == 0)
  36                return;
  37
  38        chan = xadc_event_to_channel(indio_dev, event);
  39
  40        if (chan->type == IIO_TEMP) {
  41                /*
  42                 * The temperature channel only supports over-temperature
  43                 * events.
  44                 */
  45                iio_push_event(indio_dev,
  46                        IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
  47                                IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
  48                        iio_get_time_ns(indio_dev));
  49        } else {
  50                /*
  51                 * For other channels we don't know whether it is a upper or
  52                 * lower threshold event. Userspace will have to check the
  53                 * channel value if it wants to know.
  54                 */
  55                iio_push_event(indio_dev,
  56                        IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
  57                                IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER),
  58                        iio_get_time_ns(indio_dev));
  59        }
  60}
  61
  62void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events)
  63{
  64        unsigned int i;
  65
  66        for_each_set_bit(i, &events, 8)
  67                xadc_handle_event(indio_dev, i);
  68}
  69
  70static unsigned int xadc_get_threshold_offset(const struct iio_chan_spec *chan,
  71        enum iio_event_direction dir)
  72{
  73        unsigned int offset;
  74
  75        if (chan->type == IIO_TEMP) {
  76                offset = XADC_THRESHOLD_OT_MAX;
  77        } else {
  78                if (chan->channel < 2)
  79                        offset = chan->channel + 1;
  80                else
  81                        offset = chan->channel + 6;
  82        }
  83
  84        if (dir == IIO_EV_DIR_FALLING)
  85                offset += 4;
  86
  87        return offset;
  88}
  89
  90static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan)
  91{
  92        if (chan->type == IIO_TEMP)
  93                return XADC_ALARM_OT_MASK;
  94        switch (chan->channel) {
  95        case 0:
  96                return XADC_ALARM_VCCINT_MASK;
  97        case 1:
  98                return XADC_ALARM_VCCAUX_MASK;
  99        case 2:
 100                return XADC_ALARM_VCCBRAM_MASK;
 101        case 3:
 102                return XADC_ALARM_VCCPINT_MASK;
 103        case 4:
 104                return XADC_ALARM_VCCPAUX_MASK;
 105        case 5:
 106                return XADC_ALARM_VCCODDR_MASK;
 107        default:
 108                /* We will never get here */
 109                return 0;
 110        }
 111}
 112
 113int xadc_read_event_config(struct iio_dev *indio_dev,
 114        const struct iio_chan_spec *chan, enum iio_event_type type,
 115        enum iio_event_direction dir)
 116{
 117        struct xadc *xadc = iio_priv(indio_dev);
 118
 119        return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan));
 120}
 121
 122int xadc_write_event_config(struct iio_dev *indio_dev,
 123        const struct iio_chan_spec *chan, enum iio_event_type type,
 124        enum iio_event_direction dir, int state)
 125{
 126        unsigned int alarm = xadc_get_alarm_mask(chan);
 127        struct xadc *xadc = iio_priv(indio_dev);
 128        uint16_t cfg, old_cfg;
 129        int ret;
 130
 131        mutex_lock(&xadc->mutex);
 132
 133        if (state)
 134                xadc->alarm_mask |= alarm;
 135        else
 136                xadc->alarm_mask &= ~alarm;
 137
 138        xadc->ops->update_alarm(xadc, xadc->alarm_mask);
 139
 140        ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg);
 141        if (ret)
 142                goto err_out;
 143
 144        old_cfg = cfg;
 145        cfg |= XADC_CONF1_ALARM_MASK;
 146        cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */
 147        cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */
 148        cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */
 149        if (old_cfg != cfg)
 150                ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg);
 151
 152err_out:
 153        mutex_unlock(&xadc->mutex);
 154
 155        return ret;
 156}
 157
 158/* Register value is msb aligned, the lower 4 bits are ignored */
 159#define XADC_THRESHOLD_VALUE_SHIFT 4
 160
 161int xadc_read_event_value(struct iio_dev *indio_dev,
 162        const struct iio_chan_spec *chan, enum iio_event_type type,
 163        enum iio_event_direction dir, enum iio_event_info info,
 164        int *val, int *val2)
 165{
 166        unsigned int offset = xadc_get_threshold_offset(chan, dir);
 167        struct xadc *xadc = iio_priv(indio_dev);
 168
 169        switch (info) {
 170        case IIO_EV_INFO_VALUE:
 171                *val = xadc->threshold[offset];
 172                break;
 173        case IIO_EV_INFO_HYSTERESIS:
 174                *val = xadc->temp_hysteresis;
 175                break;
 176        default:
 177                return -EINVAL;
 178        }
 179
 180        *val >>= XADC_THRESHOLD_VALUE_SHIFT;
 181
 182        return IIO_VAL_INT;
 183}
 184
 185int xadc_write_event_value(struct iio_dev *indio_dev,
 186        const struct iio_chan_spec *chan, enum iio_event_type type,
 187        enum iio_event_direction dir, enum iio_event_info info,
 188        int val, int val2)
 189{
 190        unsigned int offset = xadc_get_threshold_offset(chan, dir);
 191        struct xadc *xadc = iio_priv(indio_dev);
 192        int ret = 0;
 193
 194        val <<= XADC_THRESHOLD_VALUE_SHIFT;
 195
 196        if (val < 0 || val > 0xffff)
 197                return -EINVAL;
 198
 199        mutex_lock(&xadc->mutex);
 200
 201        switch (info) {
 202        case IIO_EV_INFO_VALUE:
 203                xadc->threshold[offset] = val;
 204                break;
 205        case IIO_EV_INFO_HYSTERESIS:
 206                xadc->temp_hysteresis = val;
 207                break;
 208        default:
 209                mutex_unlock(&xadc->mutex);
 210                return -EINVAL;
 211        }
 212
 213        if (chan->type == IIO_TEMP) {
 214                /*
 215                 * According to the datasheet we need to set the lower 4 bits to
 216                 * 0x3, otherwise 125 degree celsius will be used as the
 217                 * threshold.
 218                 */
 219                val |= 0x3;
 220
 221                /*
 222                 * Since we store the hysteresis as relative (to the threshold)
 223                 * value, but the hardware expects an absolute value we need to
 224                 * recalcualte this value whenever the hysteresis or the
 225                 * threshold changes.
 226                 */
 227                if (xadc->threshold[offset] < xadc->temp_hysteresis)
 228                        xadc->threshold[offset + 4] = 0;
 229                else
 230                        xadc->threshold[offset + 4] = xadc->threshold[offset] -
 231                                        xadc->temp_hysteresis;
 232                ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),
 233                        xadc->threshold[offset + 4]);
 234                if (ret)
 235                        goto out_unlock;
 236        }
 237
 238        if (info == IIO_EV_INFO_VALUE)
 239                ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);
 240
 241out_unlock:
 242        mutex_unlock(&xadc->mutex);
 243
 244        return ret;
 245}
 246