brintos

brintos / linux-shallow public Read only

0
0
Text · 9.7 KiB · b3f0375 Raw
391 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>4 *5 * This is the driver for the imx25 GCQ (Generic Conversion Queue)6 * connected to the imx25 ADC.7 */8 9#include <dt-bindings/iio/adc/fsl-imx25-gcq.h>10#include <linux/clk.h>11#include <linux/iio/iio.h>12#include <linux/interrupt.h>13#include <linux/mfd/imx25-tsadc.h>14#include <linux/module.h>15#include <linux/mod_devicetable.h>16#include <linux/platform_device.h>17#include <linux/property.h>18#include <linux/regmap.h>19#include <linux/regulator/consumer.h>20 21#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000))22 23static const char * const driver_name = "mx25-gcq";24 25enum mx25_gcq_cfgs {26	MX25_CFG_XP = 0,27	MX25_CFG_YP,28	MX25_CFG_XN,29	MX25_CFG_YN,30	MX25_CFG_WIPER,31	MX25_CFG_INAUX0,32	MX25_CFG_INAUX1,33	MX25_CFG_INAUX2,34	MX25_NUM_CFGS,35};36 37struct mx25_gcq_priv {38	struct regmap *regs;39	struct completion completed;40	struct clk *clk;41	int irq;42	struct regulator *vref[4];43	u32 channel_vref_mv[MX25_NUM_CFGS];44	/*45	 * Lock to protect the device state during a potential concurrent46	 * read access from userspace. Reading a raw value requires a sequence47	 * of register writes, then a wait for a completion callback,48	 * and finally a register read, during which userspace could issue49	 * another read request. This lock protects a read access from50	 * ocurring before another one has finished.51	 */52	struct mutex lock;53};54 55#define MX25_CQG_CHAN(chan, id) {\56	.type = IIO_VOLTAGE,\57	.indexed = 1,\58	.channel = chan,\59	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \60			      BIT(IIO_CHAN_INFO_SCALE),\61	.datasheet_name = id,\62}63 64static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = {65	MX25_CQG_CHAN(MX25_CFG_XP, "xp"),66	MX25_CQG_CHAN(MX25_CFG_YP, "yp"),67	MX25_CQG_CHAN(MX25_CFG_XN, "xn"),68	MX25_CQG_CHAN(MX25_CFG_YN, "yn"),69	MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"),70	MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"),71	MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"),72	MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"),73};74 75static const char * const mx25_gcq_refp_names[] = {76	[MX25_ADC_REFP_YP] = "yp",77	[MX25_ADC_REFP_XP] = "xp",78	[MX25_ADC_REFP_INT] = "int",79	[MX25_ADC_REFP_EXT] = "ext",80};81 82static irqreturn_t mx25_gcq_irq(int irq, void *data)83{84	struct mx25_gcq_priv *priv = data;85	u32 stats;86 87	regmap_read(priv->regs, MX25_ADCQ_SR, &stats);88 89	if (stats & MX25_ADCQ_SR_EOQ) {90		regmap_set_bits(priv->regs, MX25_ADCQ_MR,91				MX25_ADCQ_MR_EOQ_IRQ);92		complete(&priv->completed);93	}94 95	/* Disable conversion queue run */96	regmap_clear_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS);97 98	/* Acknowledge all possible irqs */99	regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |100		     MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |101		     MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD);102 103	return IRQ_HANDLED;104}105 106static int mx25_gcq_get_raw_value(struct device *dev,107				  struct iio_chan_spec const *chan,108				  struct mx25_gcq_priv *priv,109				  int *val)110{111	long time_left;112	u32 data;113 114	/* Setup the configuration we want to use */115	regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,116		     MX25_ADCQ_ITEM(0, chan->channel));117 118	regmap_clear_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ);119 120	/* Trigger queue for one run */121	regmap_set_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS);122 123	time_left = wait_for_completion_interruptible_timeout(124		&priv->completed, MX25_GCQ_TIMEOUT);125	if (time_left < 0) {126		dev_err(dev, "ADC wait for measurement failed\n");127		return time_left;128	} else if (time_left == 0) {129		dev_err(dev, "ADC timed out\n");130		return -ETIMEDOUT;131	}132 133	regmap_read(priv->regs, MX25_ADCQ_FIFO, &data);134 135	*val = MX25_ADCQ_FIFO_DATA(data);136 137	return IIO_VAL_INT;138}139 140static int mx25_gcq_read_raw(struct iio_dev *indio_dev,141			     struct iio_chan_spec const *chan, int *val,142			     int *val2, long mask)143{144	struct mx25_gcq_priv *priv = iio_priv(indio_dev);145	int ret;146 147	switch (mask) {148	case IIO_CHAN_INFO_RAW:149		mutex_lock(&priv->lock);150		ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val);151		mutex_unlock(&priv->lock);152		return ret;153 154	case IIO_CHAN_INFO_SCALE:155		*val = priv->channel_vref_mv[chan->channel];156		*val2 = 12;157		return IIO_VAL_FRACTIONAL_LOG2;158 159	default:160		return -EINVAL;161	}162}163 164static const struct iio_info mx25_gcq_iio_info = {165	.read_raw = mx25_gcq_read_raw,166};167 168static const struct regmap_config mx25_gcq_regconfig = {169	.max_register = 0x5c,170	.reg_bits = 32,171	.val_bits = 32,172	.reg_stride = 4,173};174 175static int mx25_gcq_ext_regulator_setup(struct device *dev,176					struct mx25_gcq_priv *priv, u32 refp)177{178	char reg_name[12];179	int ret;180 181	if (priv->vref[refp])182		return 0;183 184	ret = snprintf(reg_name, sizeof(reg_name), "vref-%s",185		       mx25_gcq_refp_names[refp]);186	if (ret < 0)187		return ret;188 189	priv->vref[refp] = devm_regulator_get_optional(dev, reg_name);190	if (IS_ERR(priv->vref[refp]))191		return dev_err_probe(dev, PTR_ERR(priv->vref[refp]),192				     "Error, trying to use external voltage reference without a %s regulator.",193				     reg_name);194 195	return 0;196}197 198static int mx25_gcq_setup_cfgs(struct platform_device *pdev,199			       struct mx25_gcq_priv *priv)200{201	struct device *dev = &pdev->dev;202	int ret, i;203 204	/*205	 * Setup all configurations registers with a default conversion206	 * configuration for each input207	 */208	for (i = 0; i < MX25_NUM_CFGS; ++i)209		regmap_write(priv->regs, MX25_ADCQ_CFG(i),210			     MX25_ADCQ_CFG_YPLL_OFF |211			     MX25_ADCQ_CFG_XNUR_OFF |212			     MX25_ADCQ_CFG_XPUL_OFF |213			     MX25_ADCQ_CFG_REFP_INT |214			     MX25_ADCQ_CFG_IN(i) |215			     MX25_ADCQ_CFG_REFN_NGND2);216 217	device_for_each_child_node_scoped(dev, child) {218		u32 reg;219		u32 refp = MX25_ADCQ_CFG_REFP_INT;220		u32 refn = MX25_ADCQ_CFG_REFN_NGND2;221 222		ret = fwnode_property_read_u32(child, "reg", &reg);223		if (ret)224			return dev_err_probe(dev, ret,225					     "Failed to get reg property\n");226 227		if (reg >= MX25_NUM_CFGS)228			return dev_err_probe(dev, -EINVAL,229				"reg value is greater than the number of available configuration registers\n");230 231		fwnode_property_read_u32(child, "fsl,adc-refp", &refp);232		fwnode_property_read_u32(child, "fsl,adc-refn", &refn);233 234		switch (refp) {235		case MX25_ADC_REFP_EXT:236		case MX25_ADC_REFP_XP:237		case MX25_ADC_REFP_YP:238			ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp);239			if (ret)240				return ret;241			priv->channel_vref_mv[reg] =242				regulator_get_voltage(priv->vref[refp]);243			/* Conversion from uV to mV */244			priv->channel_vref_mv[reg] /= 1000;245			break;246		case MX25_ADC_REFP_INT:247			priv->channel_vref_mv[reg] = 2500;248			break;249		default:250			return dev_err_probe(dev, -EINVAL,251					     "Invalid positive reference %d\n", refp);252		}253 254		/*255		 * Shift the read values to the correct positions within the256		 * register.257		 */258		refp = MX25_ADCQ_CFG_REFP(refp);259		refn = MX25_ADCQ_CFG_REFN(refn);260 261		if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp)262			return dev_err_probe(dev, -EINVAL,263					     "Invalid fsl,adc-refp property value\n");264 265		if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn)266			return dev_err_probe(dev, -EINVAL,267					     "Invalid fsl,adc-refn property value\n");268 269		regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),270				   MX25_ADCQ_CFG_REFP_MASK |271				   MX25_ADCQ_CFG_REFN_MASK,272				   refp | refn);273	}274	regmap_set_bits(priv->regs, MX25_ADCQ_CR,275			MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST);276 277	regmap_write(priv->regs, MX25_ADCQ_CR,278		     MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS);279 280	return 0;281}282 283static void mx25_gcq_reg_disable(void *reg)284{285	regulator_disable(reg);286}287 288/* Custom handling needed as this driver doesn't own the clock */289static void mx25_gcq_clk_disable(void *clk)290{291	clk_disable_unprepare(clk);292}293 294static int mx25_gcq_probe(struct platform_device *pdev)295{296	struct iio_dev *indio_dev;297	struct mx25_gcq_priv *priv;298	struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);299	struct device *dev = &pdev->dev;300	void __iomem *mem;301	int ret;302	int i;303 304	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));305	if (!indio_dev)306		return -ENOMEM;307 308	priv = iio_priv(indio_dev);309 310	mem = devm_platform_ioremap_resource(pdev, 0);311	if (IS_ERR(mem))312		return PTR_ERR(mem);313 314	priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);315	if (IS_ERR(priv->regs))316		return dev_err_probe(dev, PTR_ERR(priv->regs),317				     "Failed to initialize regmap\n");318 319	mutex_init(&priv->lock);320 321	init_completion(&priv->completed);322 323	ret = mx25_gcq_setup_cfgs(pdev, priv);324	if (ret)325		return ret;326 327	for (i = 0; i != 4; ++i) {328		if (!priv->vref[i])329			continue;330 331		ret = regulator_enable(priv->vref[i]);332		if (ret)333			return ret;334 335		ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable,336					       priv->vref[i]);337		if (ret)338			return ret;339	}340 341	priv->clk = tsadc->clk;342	ret = clk_prepare_enable(priv->clk);343	if (ret)344		return dev_err_probe(dev, ret, "Failed to enable clock\n");345 346	ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable,347				       priv->clk);348	if (ret)349		return ret;350 351	ret = platform_get_irq(pdev, 0);352	if (ret < 0)353		return ret;354 355	priv->irq = ret;356	ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name,357			       priv);358	if (ret)359		return dev_err_probe(dev, ret, "Failed requesting IRQ\n");360 361	indio_dev->channels = mx25_gcq_channels;362	indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);363	indio_dev->info = &mx25_gcq_iio_info;364	indio_dev->name = driver_name;365 366	ret = devm_iio_device_register(dev, indio_dev);367	if (ret)368		return dev_err_probe(dev, ret, "Failed to register iio device\n");369 370	return 0;371}372 373static const struct of_device_id mx25_gcq_ids[] = {374	{ .compatible = "fsl,imx25-gcq", },375	{ /* Sentinel */ }376};377MODULE_DEVICE_TABLE(of, mx25_gcq_ids);378 379static struct platform_driver mx25_gcq_driver = {380	.driver		= {381		.name	= "mx25-gcq",382		.of_match_table = mx25_gcq_ids,383	},384	.probe		= mx25_gcq_probe,385};386module_platform_driver(mx25_gcq_driver);387 388MODULE_DESCRIPTION("ADC driver for Freescale mx25");389MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");390MODULE_LICENSE("GPL v2");391