brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 1bd375f Raw
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