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