245 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Xilinx XADC driver4 *5 * Copyright 2013 Analog Devices Inc.6 * Author: Lars-Peter Clausen <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-temperature43 * 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 or52 * lower threshold event. Userspace will have to check the53 * 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 else81 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 else136 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 158int xadc_read_event_value(struct iio_dev *indio_dev,159 const struct iio_chan_spec *chan, enum iio_event_type type,160 enum iio_event_direction dir, enum iio_event_info info,161 int *val, int *val2)162{163 unsigned int offset = xadc_get_threshold_offset(chan, dir);164 struct xadc *xadc = iio_priv(indio_dev);165 166 switch (info) {167 case IIO_EV_INFO_VALUE:168 *val = xadc->threshold[offset];169 break;170 case IIO_EV_INFO_HYSTERESIS:171 *val = xadc->temp_hysteresis;172 break;173 default:174 return -EINVAL;175 }176 177 /* MSB aligned */178 *val >>= 16 - chan->scan_type.realbits;179 180 return IIO_VAL_INT;181}182 183int xadc_write_event_value(struct iio_dev *indio_dev,184 const struct iio_chan_spec *chan, enum iio_event_type type,185 enum iio_event_direction dir, enum iio_event_info info,186 int val, int val2)187{188 unsigned int offset = xadc_get_threshold_offset(chan, dir);189 struct xadc *xadc = iio_priv(indio_dev);190 int ret = 0;191 192 /* MSB aligned */193 val <<= 16 - chan->scan_type.realbits;194 195 if (val < 0 || val > 0xffff)196 return -EINVAL;197 198 mutex_lock(&xadc->mutex);199 200 switch (info) {201 case IIO_EV_INFO_VALUE:202 xadc->threshold[offset] = val;203 break;204 case IIO_EV_INFO_HYSTERESIS:205 xadc->temp_hysteresis = val;206 break;207 default:208 mutex_unlock(&xadc->mutex);209 return -EINVAL;210 }211 212 if (chan->type == IIO_TEMP) {213 /*214 * According to the datasheet we need to set the lower 4 bits to215 * 0x3, otherwise 125 degree celsius will be used as the216 * threshold.217 */218 val |= 0x3;219 220 /*221 * Since we store the hysteresis as relative (to the threshold)222 * value, but the hardware expects an absolute value we need to223 * recalcualte this value whenever the hysteresis or the224 * threshold changes.225 */226 if (xadc->threshold[offset] < xadc->temp_hysteresis)227 xadc->threshold[offset + 4] = 0;228 else229 xadc->threshold[offset + 4] = xadc->threshold[offset] -230 xadc->temp_hysteresis;231 ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),232 xadc->threshold[offset + 4]);233 if (ret)234 goto out_unlock;235 }236 237 if (info == IIO_EV_INFO_VALUE)238 ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);239 240out_unlock:241 mutex_unlock(&xadc->mutex);242 243 return ret;244}245