summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/ep93xx_adc.c
blob: 81c901507ad2432676bc0a9b6a6b811b26b677cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*
 * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs
 *
 * Copyright (C) 2015 Alexander Sverdlin
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * The driver uses polling to get the conversion status. According to EP93xx
 * datasheets, reading ADCResult register starts the conversion, but user is also
 * responsible for ensuring that delay between adjacent conversion triggers is
 * long enough so that maximum allowed conversion rate is not exceeded. This
 * basically renders IRQ mode unusable.
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/io.h>
#include <linux/irqflags.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>

/*
 * This code could benefit from real HR Timers, but jiffy granularity would
 * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait
 * in such case.
 *
 * HR Timers-based version loads CPU only up to 10% during back to back ADC
 * conversion, while busy wait-based version consumes whole CPU power.
 */
#ifdef CONFIG_HIGH_RES_TIMERS
#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax)
#else
#define ep93xx_adc_delay(usmin, usmax) udelay(usmin)
#endif

#define EP93XX_ADC_RESULT	0x08
#define   EP93XX_ADC_SDR	BIT(31)
#define EP93XX_ADC_SWITCH	0x18
#define EP93XX_ADC_SW_LOCK	0x20

struct ep93xx_adc_priv {
	struct clk *clk;
	void __iomem *base;
	int lastch;
	struct mutex lock;
};

#define EP93XX_ADC_CH(index, dname, swcfg) {			\
	.type = IIO_VOLTAGE,					\
	.indexed = 1,						\
	.channel = index,					\
	.address = swcfg,					\
	.datasheet_name = dname,				\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
				   BIT(IIO_CHAN_INFO_OFFSET),	\
}

/*
 * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets.
 * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is
 * not defined. So the last three are numbered randomly, let's say.
 */
static const struct iio_chan_spec ep93xx_adc_channels[8] = {
	EP93XX_ADC_CH(0, "YM",	0x608),
	EP93XX_ADC_CH(1, "SXP",	0x680),
	EP93XX_ADC_CH(2, "SXM",	0x640),
	EP93XX_ADC_CH(3, "SYP",	0x620),
	EP93XX_ADC_CH(4, "SYM",	0x610),
	EP93XX_ADC_CH(5, "XP",	0x601),
	EP93XX_ADC_CH(6, "XM",	0x602),
	EP93XX_ADC_CH(7, "YP",	0x604),
};

static int ep93xx_read_raw(struct iio_dev *iiodev,
			   struct iio_chan_spec const *channel, int *value,
			   int *shift, long mask)
{
	struct ep93xx_adc_priv *priv = iio_priv(iiodev);
	unsigned long timeout;
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		mutex_lock(&priv->lock);
		if (priv->lastch != channel->channel) {
			priv->lastch = channel->channel;
			/*
			 * Switch register is software-locked, unlocking must be
			 * immediately followed by write
			 */
			local_irq_disable();
			writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK);
			writel_relaxed(channel->address,
				       priv->base + EP93XX_ADC_SWITCH);
			local_irq_enable();
			/*
			 * Settling delay depends on module clock and could be
			 * 2ms or 500us
			 */
			ep93xx_adc_delay(2000, 2000);
		}
		/* Start the conversion, eventually discarding old result */
		readl_relaxed(priv->base + EP93XX_ADC_RESULT);
		/* Ensure maximum conversion rate is not exceeded */
		ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925),
				 DIV_ROUND_UP(1000000, 925));
		/* At this point conversion must be completed, but anyway... */
		ret = IIO_VAL_INT;
		timeout = jiffies + msecs_to_jiffies(1) + 1;
		while (1) {
			u32 t;

			t = readl_relaxed(priv->base + EP93XX_ADC_RESULT);
			if (t & EP93XX_ADC_SDR) {
				*value = sign_extend32(t, 15);
				break;
			}

			if (time_after(jiffies, timeout)) {
				dev_err(&iiodev->dev, "Conversion timeout\n");
				ret = -ETIMEDOUT;
				break;
			}

			cpu_relax();
		}
		mutex_unlock(&priv->lock);
		return ret;

	case IIO_CHAN_INFO_OFFSET:
		/* According to datasheet, range is -25000..25000 */
		*value = 25000;
		return IIO_VAL_INT;

	case IIO_CHAN_INFO_SCALE:
		/* Typical supply voltage is 3.3v */
		*value = (1ULL << 32) * 3300 / 50000;
		*shift = 32;
		return IIO_VAL_FRACTIONAL_LOG2;
	}

	return -EINVAL;
}

static const struct iio_info ep93xx_adc_info = {
	.read_raw = ep93xx_read_raw,
};

static int ep93xx_adc_probe(struct platform_device *pdev)
{
	int ret;
	struct iio_dev *iiodev;
	struct ep93xx_adc_priv *priv;
	struct clk *pclk;
	struct resource *res;

	iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
	if (!iiodev)
		return -ENOMEM;
	priv = iio_priv(iiodev);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "Cannot obtain memory resource\n");
		return -ENXIO;
	}
	priv->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(priv->base)) {
		dev_err(&pdev->dev, "Cannot map memory resource\n");
		return PTR_ERR(priv->base);
	}

	iiodev->dev.parent = &pdev->dev;
	iiodev->name = dev_name(&pdev->dev);
	iiodev->modes = INDIO_DIRECT_MODE;
	iiodev->info = &ep93xx_adc_info;
	iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels);
	iiodev->channels = ep93xx_adc_channels;

	priv->lastch = -1;
	mutex_init(&priv->lock);

	platform_set_drvdata(pdev, iiodev);

	priv->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(priv->clk)) {
		dev_err(&pdev->dev, "Cannot obtain clock\n");
		return PTR_ERR(priv->clk);
	}

	pclk = clk_get_parent(priv->clk);
	if (!pclk) {
		dev_warn(&pdev->dev, "Cannot obtain parent clock\n");
	} else {
		/*
		 * This is actually a place for improvement:
		 * EP93xx ADC supports two clock divisors -- 4 and 16,
		 * resulting in conversion rates 3750 and 925 samples per second
		 * with 500us or 2ms settling time respectively.
		 * One might find this interesting enough to be configurable.
		 */
		ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16);
		if (ret)
			dev_warn(&pdev->dev, "Cannot set clock rate\n");
		/*
		 * We can tolerate rate setting failure because the module should
		 * work in any case.
		 */
	}

	ret = clk_enable(priv->clk);
	if (ret) {
		dev_err(&pdev->dev, "Cannot enable clock\n");
		return ret;
	}

	ret = iio_device_register(iiodev);
	if (ret)
		clk_disable(priv->clk);

	return ret;
}

static int ep93xx_adc_remove(struct platform_device *pdev)
{
	struct iio_dev *iiodev = platform_get_drvdata(pdev);
	struct ep93xx_adc_priv *priv = iio_priv(iiodev);

	iio_device_unregister(iiodev);
	clk_disable(priv->clk);

	return 0;
}

static struct platform_driver ep93xx_adc_driver = {
	.driver = {
		.name = "ep93xx-adc",
	},
	.probe = ep93xx_adc_probe,
	.remove = ep93xx_adc_remove,
};
module_platform_driver(ep93xx_adc_driver);

MODULE_AUTHOR("Alexander Sverdlin <alexander.sverdlin@gmail.com>");
MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ep93xx-adc");
r white-space fixes. * tag 'dt-cleanup-6.9' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-dt: ARM: dts: sti: minor whitespace cleanup around '=' ARM: dts: da850: add MMD SDIO interrupts ARM: dts: marvell: dove-cubox: fix si5351 node names arm: dts: marvell: Fix maxium->maxim typo in brownstone dts Link: https://lore.kernel.org/r/20240218182656.32103-1-krzysztof.kozlowski@linaro.org Signed-off-by: Arnd Bergmann <arnd@arndb.de> 2024-02-27arm: dts: marvell: clearfog-gtr: add missing pinctrl for all used gpiosJosua Mayer Various control signals such as sfp module-absence, pci-e reset or led gpios were missing pinctrl nodes, leaving any u-boot choices in place. Since U-Boot is shared between multiple board variants, i.e. a388 clearfog pro / base, clearfog gtr l8 / s4, it is better to explicitly configure functions. Add explicit pinctrl entries for all gpios currently in use. Additionally the loss-of-signal gpio specified is invalid, in fact los only has a pull-up on the board but no gpio connection to the cpu. Remove this stray reference. Signed-off-by: Josua Mayer <josua@solid-run.com> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2024-02-27arm: dts: marvell: clearfog-gtr: sort pinctrl nodes alphabeticallyJosua Mayer Cosmetic change to increase future patches readability when adding new pinctrl nodes. Signed-off-by: Josua Mayer <josua@solid-run.com> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2024-02-27arm: dts: marvell: clearfog-gtr: add board-specific compatible stringsJosua Mayer Most arm board have a board-specific compatible string that allows e.g. userspace to match specific firmware variants or apply specific policies. Add board-specific properties to both variants of the Clearfog GTR: - solidrun,clearfog-gtr-l8 - solidrun,clearfog-gtr-s4 Introduction of a common parent (e.g. "solidrun,clearfog-gtr") is omitted for brevity. Since announcement of the two products no additional variants were added it is assumed that there will always be just two. Signed-off-by: Josua Mayer <josua@solid-run.com> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2024-02-27arm: dts: marvell: clearfog: add pro variant compatible in legacy dtsJosua Mayer Armada 388 Clearfog ("armada-388-clearfog.dts)" is a legacy filename for the Armada 388 Clearfog Pro ("armada-388-clearfog-pro.dts"). The "Pro" suffix was only used when the smaller version, the "Base" got released. The two names refer to exactly the same hardware, therefore they should share the same compatible strings. Copy "solidrun,clearfog-pro-a1" compatible from the -pro dts and add it to this legacy file. Signed-off-by: Josua Mayer <josua@solid-run.com> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2024-02-20arm: dts: Fix dtc interrupt_provider warningsRob Herring The dtc interrupt_provider warning is off by default. Fix all the warnings so it can be enabled. Signed-off-by: Rob Herring <robh@kernel.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Reviewed-by: Alexandre Torgue <alexandre.torgue@foss.st.com> Acked-by: Florian Fainelli <florian.fainelli@broadcom.com> #Broadcom Acked-by: Thierry Reding <treding@nvidia.com> Link: https://lore.kernel.org/r/20240213-arm-dt-cleanups-v1-2-f2dee1292525@kernel.org Signed-off-by: Arnd Bergmann <arnd@arndb.de> 2024-01-26ARM: dts: marvell: dove-cubox: fix si5351 node namesAlvin Šipraga Correct the device tree to conform with the bindings. The node name and index should be separated with an @. Suggested-by: Rob Herring <robh@kernel.org> Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk> Link: https://lore.kernel.org/r/20231004063712.3348978-3-alvin@pqrs.dk Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> 2024-01-26arm: dts: marvell: Fix maxium->maxim typo in brownstone dtsDuje Mihanović Fix an obvious spelling error in the PMIC compatible in the MMP2 Brownstone DTS file. Fixes: 58f1193e6210 ("mfd: max8925: Add dts") Cc: <stable@vger.kernel.org> Signed-off-by: Duje Mihanović <duje.mihanovic@skole.hr> Reported-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Closes: https://lore.kernel.org/linux-devicetree/1410884282-18041-1-git-send-email-k.kozlowski@samsung.com/ Reviewed-by: Andrew Lunn <andrew@lunn.ch> Link: https://lore.kernel.org/r/20240125-brownstone-typo-fix-v2-1-45bc48a0c81c@skole.hr [krzysztof: Just 10 years to take a patch, not bad! Rephrased commit msg] Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> 2023-12-15ARM: dts: marvell: make dts use gpio-fan matrix instead of arrayDavid Heidelberg No functional changes. Adjust to comply with dt-schema requirements and make possible to validate values. Acked-by: Simon Guinot <simon.guinot@sequanux.org> Signed-off-by: David Heidelberg <david@ixit.cz> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2023-12-15ARM: dts: marvell: Fix some common switch mistakesLinus Walleij Fix some errors in the Marvell MV88E6xxx switch descriptions: - The top node had no address size or cells. - switch0@0 is not OK, should be ethernet-switch@0. - The ports node should be named ethernet-ports - The ethernet-ports node should have port@0 etc children, no plural "ports" in the children. - Ports should be named ethernet-port@0 etc - PHYs should be named ethernet-phy@0 etc This serves as an example of fixes needed for introducing a schema for the bindings, but the patch can simply be applied. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2023-08-12ARM: dts: marvell: dove: drop incorrect reg in fixed regulatorsKrzysztof Kozlowski Fixed regulators are not in some bus and bindings do not allow a "reg" property. Move them out of "regulators" node to top-level. dove-cubox.dtb: regulator@1: Unevaluated properties are not allowed ('reg' was unexpected) Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2023-08-12ARM: dts: marvell: armada: drop incorrect reg in fixed regulatorsKrzysztof Kozlowski Fixed regulators are not in some bus and bindings do not allow a "reg" property. Move them out of "regulators" node to top-level. armada-370-dlink-dns327l.dtb: regulator@1: Unevaluated properties are not allowed ('reg' was unexpected) Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2023-07-13ARM: dts: marvell: add missing space before {Krzysztof Kozlowski Add missing whitespace between node name/label and opening {. Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> 2023-06-22ARM: mvebu: fix unit address on armada-390-db flashArnd Bergmann The unit address needs to be changed to match the reg property: arch/arm/boot/dts/marvell/armada-390-db.dts:84.10-106.4: Warning (spi_bus_reg): /soc/spi@10680/flash@1: SPI bus unit address format error, expected "0" Reported-by: Stephen Rothwell <sfr@canb.auug.org.au> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de> 2023-06-21ARM: dts: Move .dts files to vendor sub-directoriesRob Herring The arm dts directory has grown to 1559 boards which makes it a bit unwieldy to maintain and use. Past attempts stalled out due to plans to move .dts files out of the kernel tree. Doing that is no longer planned (any time soon at least), so let's go ahead and group .dts files by vendors. This move aligns arm with arm64 .dts file structure. There's no change to dtbs_install as the flat structure is maintained on install. The naming of vendor directories is roughly in this order of preference: - Matching original and current SoC vendor prefix/name (e.g. ti, qcom) - Current vendor prefix/name if still actively sold (SoCs which have been aquired) (e.g. nxp/imx) - Existing platform name for older platforms not sold/maintained by any company (e.g. gemini, nspire) The whole move was scripted with the exception of MAINTAINERS and a few makefile fixups. Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Michal Simek <michal.simek@amd.com> #Xilinx Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Acked-by: Neil Armstrong <neil.armstrong@linaro.org> Acked-by: Paul Barker <paul.barker@sancloud.com> Acked-by: Tony Lindgren <tony@atomide.com> Acked-by: Gregory CLEMENT <gregory.clement@bootlin.com> Acked-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Wei Xu <xuwei5@hisilicon.com> #hisilicon Acked-by: Geert Uytterhoeven <geert+renesas@glider.be> Acked-by: Nick Hawkins <nick.hawkins@hpe.com> Acked-by: Baruch Siach <baruch@tkos.co.il> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Andre Przywara <andre.przywara@arm.com> Acked-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com> Acked-by: Peter Rosin <peda@axentia.se> Acked-by: Jesper Nilsson <jesper.nilsson@axis.com> Acked-by: Sudeep Holla <sudeep.holla@arm.com> Acked-by: Florian Fainelli <f.fainelli@gmail.com> #broadcom Acked-by: Manivannan Sadhasivam <mani@kernel.org> Reviewed-by: Jisheng Zhang <jszhang@kernel.org> Acked-by: Patrice Chotard <patrice.chotard@foss.st.com> Acked-by: Romain Perier <romain.perier@gmail.com> Acked-by: Alexandre TORGUE <alexandre.torgue@st.com> Acked-by: Shawn Guo <shawnguo@kernel.org> Acked-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Acked-by: Enric Balletbo i Serra <eballetbo@gmail.com> Signed-off-by: Rob Herring <robh@kernel.org>