* [PATCH 0/4] Generic #pinctrl-cells and and pinctrl_parse_index_with_args
From: Tony Lindgren @ 2016-10-25 16:45 UTC (permalink / raw)
To: Linus Walleij
Cc: Jon Hunter, Mark Rutland, Rob Herring, Grygorii Strashko,
Nishanth Menon, linux-gpio, devicetree, linux-kernel, linux-omap
Hi all,
Here are some pinctrl changes to introduce #pinctrl-cells and a generic
parser pinctrl_parse_index_with_args that the drivers can optionally use.
Regards,
Tony
Tony Lindgren (4):
pinctrl: Introduce generic #pinctrl-cells and
pinctrl_parse_index_with_args
pinctrl: single: Use generic parser and #pinctrl-cells for
pinctrl-single,pins
pinctrl: single: Use generic parser and #pinctrl-cells for
pinctrl-single,bits
ARM: dts: Add #pinctrl-cells for pinctrl-single instances
.../bindings/pinctrl/pinctrl-bindings.txt | 42 +++++-
.../devicetree/bindings/pinctrl/pinctrl-single.txt | 3 +
arch/arm/boot/dts/am33xx.dtsi | 2 +
arch/arm/boot/dts/am3517.dtsi | 1 +
arch/arm/boot/dts/am4372.dtsi | 1 +
arch/arm/boot/dts/da850.dtsi | 1 +
arch/arm/boot/dts/dm814x.dtsi | 1 +
arch/arm/boot/dts/dm816x.dtsi | 2 +
arch/arm/boot/dts/dra7.dtsi | 1 +
arch/arm/boot/dts/hi3620.dtsi | 2 +
arch/arm/boot/dts/keystone-k2g.dtsi | 1 +
arch/arm/boot/dts/keystone-k2l.dtsi | 1 +
arch/arm/boot/dts/omap2420.dtsi | 2 +
arch/arm/boot/dts/omap2430.dtsi | 2 +
arch/arm/boot/dts/omap3.dtsi | 2 +
arch/arm/boot/dts/omap34xx.dtsi | 1 +
arch/arm/boot/dts/omap36xx.dtsi | 1 +
arch/arm/boot/dts/omap4.dtsi | 2 +
arch/arm/boot/dts/omap5.dtsi | 2 +
arch/arm/boot/dts/pxa3xx.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hi6220.dtsi | 3 +
drivers/pinctrl/devicetree.c | 151 ++++++++++++++++++++
drivers/pinctrl/devicetree.h | 24 ++++
drivers/pinctrl/pinctrl-single.c | 157 +++++++++++++++------
24 files changed, 363 insertions(+), 43 deletions(-)
--
2.9.3
^ permalink raw reply
* Re: [v2 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: Moritz Fischer @ 2016-10-25 16:34 UTC (permalink / raw)
To: Joel Holdsworth
Cc: atull, ijc+devicetree, galak, mark.rutland, pawel.moll, robh+dt,
devicetree, linux-kernel
In-Reply-To: <c971d8f4-dbb4-8d6e-f8eb-028a0a95e6fb@airwebreathe.org.uk>
On Mon, Oct 24, 2016 at 11:05:28PM -0600, Joel Holdsworth wrote:
> Only being able to load firmware from kernel C-code is rather a limitation -
> though I suppose the framework is quite early in development.
I agree this is a limitation. To expand a bit on what Alan said. For the
last year(?) or so we've been reviewing / reiterating patches to do that
using device tree overlays.
Rob was ok with the last round of bindings so it looks like it is moving
in the right direction.
The part that's missing in the whole spiel is a way to apply overlays
from userland (which in turn then trigger the reconfiguration of the
regions).
Pantelis has a patchset that allows doing this using a configfs
interface; however the devicetree maintainers weren't too happy with it.
At ELCE Pantelis and me talked to Frank about the patchset and Pantelis
agreed to resubmit the patchset with small changes.
TL;DR we'll eventually get there but it takes time and many rounds of review :)
Cheers,
Moritz
^ permalink raw reply
* Re: [v2 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: Joel Holdsworth @ 2016-10-25 16:31 UTC (permalink / raw)
To: atull
Cc: ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, mark.rutland-5wv7dgnIgG8,
moritz.fischer-+aYTwkv1SeIAvxtiuMwx3w, pawel.moll-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <alpine.DEB.2.02.1610250913530.14525@linuxheads99>
Hi Alan
> Yes! I have submitted patches for doing exactly that. About to
> submit v21. Here's patch 1 of v20:
>
> https://patchwork.kernel.org/patch/9379859/
That's really cool. I hope it gets accepted.
>> and/or if there were any patches for loading
>> firmware from userspace - something like this: "cat firmware.bin >
>> /dev/fpga0"?
>
> We discussed doing a char driver interface. That's where all this
> started. It met with lots of resistance.
>
Yeah I saw some slides about it. Personally I don't care too much about
the character device thing. It could be something in sysfs - anything
that allows the user to inject firmware firmware from user-space.
Joel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Configure STM32 ADC to use dma by default.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 3fcd941..a00f7a6 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -186,6 +186,8 @@
#io-channel-cells = <1>;
reg = <0x0>;
clocks = <&rcc 0 168>;
+ dmas = <&dma2 0 0 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
@@ -201,6 +203,8 @@
#io-channel-cells = <1>;
reg = <0x100>;
clocks = <&rcc 0 169>;
+ dmas = <&dma2 3 1 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
@@ -216,6 +220,8 @@
#io-channel-cells = <1>;
reg = <0x200>;
clocks = <&rcc 0 170>;
+ dmas = <&dma2 1 2 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
--
1.9.1
^ permalink raw reply related
* [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Enable analog to digital converter on stm32f429i-eval board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..0e8c900 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
serial0 = &usart1;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
green {
@@ -123,3 +137,19 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc3-slave@200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+
+ jadc3: adc3-injected@200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 08/10] ARM: dts: stm32f429: Add adc support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: jic23-DgEjT+Ai2ygdnm+yROfE0A, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
fabrice.gasnier-qxv4g6HH51o
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
---
arch/arm/boot/dts/stm32f429.dtsi | 62 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..3fcd941 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,62 @@
status = "disabled";
};
+ adc: adc@40012000 {
+ compatible = "st,stm32f4-adc";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc1: adc1-master@0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ status = "disabled";
+ };
+
+ jadc1: adc1-injected@0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ st,injected;
+ status = "disabled";
+ };
+
+ adc2: adc2-slave@100 {
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ status = "disabled";
+ };
+
+ jadc2: adc2-injected@100 {
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ st,injected;
+ status = "disabled";
+ };
+
+ adc3: adc3-slave@200 {
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ status = "disabled";
+ };
+
+ jadc3: adc3-injected@200 {
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ st,injected;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config@40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +388,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc@0 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc@40023810 {
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/configs/stm32_defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..5a5f50c 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -57,6 +57,8 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_DMADEVICES=y
CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32F4_ADC=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
--
1.9.1
^ permalink raw reply related
* [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add per channel "smpr" IIO extended attribute, to allow sampling
time configuration.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 75 +++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 25 +++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
3 files changed, 148 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 9b4b459..1681f75 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -38,6 +38,61 @@
#include "stm32-adc.h"
/**
+ * stm32_adc_conf_smp() - Configure sampling time for each channel
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ */
+static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *smp =
+ adc->common->data->adc_reginfo->smpr_regs;
+ struct stm32_adc_chan *stm32_chan;
+ const struct iio_chan_spec *chan;
+ u32 bit, val;
+ int i;
+
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ stm32_chan = to_stm32_chan(adc, chan);
+ i = chan->channel;
+
+ if (i >= adc->max_channels)
+ return -EINVAL;
+
+ val = stm32_adc_readl(adc, smp[i].reg);
+ val &= ~smp[i].mask;
+ val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
+ stm32_adc_writel(adc, smp[i].reg, val);
+ }
+
+ return 0;
+}
+
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ stm32_chan->smpr = smpr;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
+
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ return stm32_chan->smpr;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
+
+/**
* stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
* @indio_dev: IIO device
* @scan_mask: channels to be converted
@@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
return ret;
}
+ ret = stm32_adc_conf_smp(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure samp time\n");
+ goto err_dis;
+ }
+
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
if (ret) {
dev_err(&indio_dev->dev, "Failed to configure sequence\n");
@@ -938,6 +999,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
const struct stm32_adc_info *adc_info)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
struct device_node *node = indio_dev->dev.of_node;
struct property *prop;
const __be32 *cur;
@@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
int scan_index = 0, num_channels = 0;
u32 val;
+ if (!common->stm32_chans[adc->id]) {
+ /* Allocate extended attributes structure for an instance */
+ struct stm32_adc_chan *stm32_chans;
+
+ stm32_chans = devm_kcalloc(&indio_dev->dev,
+ adc_info->max_channels,
+ sizeof(*stm32_chans), GFP_KERNEL);
+ if (!stm32_chans)
+ return -ENOMEM;
+
+ common->stm32_chans[adc->id] = stm32_chans;
+ }
+
of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
num_channels++;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 6c9b70d..8cf1d5c 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
};
/**
+ * struct stm32_adc_chan - Extended specifications of stm32 adc channels
+ * @smpr: per channel sampling time selection
+ */
+struct stm32_adc_chan {
+ unsigned smpr:3;
+};
+
+/**
* struct stm32_adc_trig_info - ADC trigger info
* @extsel: trigger selection for regular or injected
* @name: name of the trigger, corresponding to its source
@@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
* @jdr: injected data registers offsets
* @sqr_regs: Regular sequence registers description
* @jsqr_reg: Injected sequence register description
+ * @smpr_regs: Sampling time registers description
* @trig_reginfo: regular trigger control registers description
* @jtrig_reginfo: injected trigger control registers description
*/
@@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
u32 jdr[4];
const struct stm32_adc_regs *sqr_regs;
const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_regs *smpr_regs;
const struct stm32_adc_trig_reginfo *trig_reginfo;
const struct stm32_adc_trig_reginfo *jtrig_reginfo;
};
@@ -328,6 +338,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @stm32_chans: stm32 channels extended specification data
* @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
@@ -341,11 +352,21 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct stm32_adc_chan *stm32_chans[STM32_ADC_ID_MAX];
struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
/* Helper routines */
+static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
+ const struct iio_chan_spec
+ *chan)
+{
+ struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
+
+ return &stm32_chans[chan->channel];
+}
+
static inline int stm32_adc_start_conv(struct stm32_adc *adc)
{
return adc->common->data->start_conv(adc);
@@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
/* STM32 common extended attributes */
extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr);
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
int stm32_adc_probe(struct platform_device *pdev);
int stm32_adc_remove(struct platform_device *pdev);
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index e033a68..e0e6211 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
};
/**
+ * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
+};
+
+/**
* stm32f4_sqr_regs - describe regular sequence registers
* - L: sequence len (register & bit field)
* - SQ1..SQ16: sequence entries (register & bit field)
@@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
},
.sqr_regs = stm32f4_sqr_regs,
.jsqr_reg = stm32f4_jsqr_reg,
+ .smpr_regs = stm32f4_smpr_regs,
.trig_reginfo = &stm32f4_adc_trig_reginfo,
.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
};
@@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+/* stm32f4_smpr_items : Channel-wise programmable sampling time */
+static const char * const stm32f4_smpr_items[] = {
+ [STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
+ [STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
+ [STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
+ [STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
+ [STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
+ [STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
+ [STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
+ [STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
+};
+
+static const struct iio_enum stm32f4_smpr = {
+ .items = stm32f4_smpr_items,
+ .num_items = ARRAY_SIZE(stm32f4_smpr_items),
+ .get = stm32_adc_get_smpr,
+ .set = stm32_adc_set_smpr,
+};
+
static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
+ IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
{
.name = "trigger_pol_available",
--
1.9.1
^ permalink raw reply related
* [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add trigger polarity extended attribute.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 41 ++++++++++++++++++++++++++++++++++++-
drivers/iio/adc/stm32/stm32-adc.h | 4 ++++
drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1a13450..9b4b459 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
/* trigger source */
extsel = trig_info[ret].extsel;
- exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+
+ /* default to rising edge if no polarity */
+ if (adc->exten == STM32_EXTEN_SWTRIG)
+ adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ exten = adc->exten;
}
spin_lock_irqsave(&adc->lock, flags);
@@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
return 0;
}
+static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ adc->exten = type;
+
+ return 0;
+}
+
+static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ return adc->exten;
+}
+
+static const char * const stm32_trig_pol_items[] = {
+ [STM32_EXTEN_SWTRIG] = "swtrig",
+ [STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
+ [STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
+ [STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
+};
+
+const struct iio_enum stm32_adc_trig_pol = {
+ .items = stm32_trig_pol_items,
+ .num_items = ARRAY_SIZE(stm32_trig_pol_items),
+ .get = stm32_adc_get_trig_pol,
+ .set = stm32_adc_set_trig_pol,
+};
+EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
+
/**
* stm32_adc_conv_irq_enable() - Unmask end of conversion irq
* @adc: stm32 adc instance
@@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->scan_type.realbits = adc->common->data->highres;
chan->scan_type.storagebits = STM32_STORAGEBITS;
chan->scan_type.shift = 0;
+ chan->ext_info = adc->common->data->ext_info;
}
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index fe3568b..6c9b70d 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
* @ext_triggers: Reference to trigger info for regular channels
* @jext_triggers: Reference to trigger info for injected channels
* @adc_reginfo: stm32 ADC registers description
+ * @ext_info: Extended channel info
* @highres: Max resolution
* @max_clock_rate: Max input clock rate
* @clk_sel: routine to select common clock and prescaler
@@ -251,6 +252,7 @@ struct stm32_adc_ops {
const struct stm32_adc_trig_info *ext_triggers;
const struct stm32_adc_trig_info *jext_triggers;
const struct stm32_adc_reginfo *adc_reginfo;
+ const struct iio_chan_spec_ext_info *ext_info;
int highres;
unsigned long max_clock_rate;
int (*clk_sel)(struct stm32_adc *adc);
@@ -273,6 +275,7 @@ struct stm32_adc_ops {
* @id: ADC instance number (e.g. adc 1, 2 or 3)
* @offset: ADC instance register offset in ADC block
* @max_channels: Max channels number for this ADC.
+ * @exten: External trigger config (enable/polarity)
* @extrig_list: External trigger list (for regular channel)
* @completion: end of single conversion completion
* @buffer: data buffer
@@ -295,6 +298,7 @@ struct stm32_adc {
int id;
int offset;
int max_channels;
+ enum stm32_adc_exten exten;
struct list_head extrig_list;
struct completion completion;
u16 *buffer;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 4d7a2a8..e033a68 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
+ {
+ .name = "trigger_pol_available",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = iio_enum_available_read,
+ .private = (uintptr_t)&stm32_adc_trig_pol,
+ },
+ {},
+};
+
static const struct stm32_adc_ops stm32f4_adc_ops = {
.adc_info = stm32f4_adc_info,
.ext_triggers = stm32f4_adc_ext_triggers,
.jext_triggers = stm32f4_adc_jext_triggers,
.adc_reginfo = &stm32f4_adc_reginfo,
+ .ext_info = stm32f4_adc_ext_info,
.highres = 12,
.max_clock_rate = 36000000,
.clk_sel = stm32f4_adc_clk_sel,
--
1.9.1
^ permalink raw reply related
* [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, lars, alexandre.torgue, pmeerw, linux, robh+dt,
mcoquelin.stm32, knaack.h, fabrice.gasnier, jic23
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
STM32 ADC may use EXTi signals routed internally as trigger source
for conversions. Configure them as interrupt to configure this path
in HW to adc.
Note: interrupt handler isn't required here, and corresponding interrupt
can be kept masked at exti controller level.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 3 +++
2 files changed, 48 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 25d0307..1a13450 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
return ret;
}
+static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
+{
+ /* Exti handler should not be invoqued, and is not used */
+ return IRQ_HANDLED;
+}
+
/**
* stm32_adc_validate_trigger() - validate trigger for stm32 adc
* @indio_dev: IIO device
@@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
}
}
+static int stm32_adc_exti_probe(struct stm32_adc_common *common)
+{
+ int i, irq, ret;
+
+ common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
+ GPIOD_IN);
+ if (!common->gpios)
+ return 0;
+
+ for (i = 0; i < common->gpios->ndescs; i++) {
+ irq = gpiod_to_irq(common->gpios->desc[i]);
+ if (irq < 0) {
+ dev_err(common->dev, "gpio %d to irq failed\n", i);
+ return irq;
+ }
+
+ ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
+ 0, dev_name(common->dev), common);
+ if (ret) {
+ dev_err(common->dev, "request IRQ %d failed\n", irq);
+ return ret;
+ }
+
+ /*
+ * gpios are configured as interrupts, so exti trigger path is
+ * configured in HW. But getting interrupts when starting
+ * conversions is unused here, so mask it in on exti
+ * controller by default.
+ */
+ disable_irq(irq);
+ }
+
+ return 0;
+}
+
int stm32_adc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *child;
@@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
goto err_clk_disable;
}
+ ret = stm32_adc_exti_probe(common);
+ if (ret)
+ goto err_clk_disable;
+
/* Parse adc child nodes to retrieve master/slave instances data */
for_each_available_child_of_node(np, child) {
ret = stm32_adc_register(common, child);
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 01a0dda..fe3568b 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -23,6 +23,7 @@
#define __STM32_ADC_H
#include <linux/dmaengine.h>
+#include <linux/gpio/consumer.h>
#include <linux/irq_work.h>
/*
@@ -323,6 +324,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
struct stm32_adc_common {
@@ -335,6 +337,7 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
--
1.9.1
^ permalink raw reply related
* [PATCH 03/10] iio: adc: stm32: add optional dma support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, lars, alexandre.torgue, pmeerw, linux, robh+dt,
mcoquelin.stm32, knaack.h, fabrice.gasnier, jic23
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add optional DMA support to STM32 ADC.
Use dma cyclic mode with at least two periods.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/Kconfig | 2 +
drivers/iio/adc/stm32/stm32-adc.c | 222 ++++++++++++++++++++++++++++++++----
drivers/iio/adc/stm32/stm32-adc.h | 15 +++
drivers/iio/adc/stm32/stm32f4-adc.c | 20 +++-
4 files changed, 235 insertions(+), 24 deletions(-)
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
index 245d037..5554ac8 100644
--- a/drivers/iio/adc/stm32/Kconfig
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -8,6 +8,7 @@ config STM32_ADC
select REGULATOR_FIXED_VOLTAGE
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select IRQ_WORK
help
Say yes here to build the driver for the STMicroelectronics
STM32 analog-to-digital converter (ADC).
@@ -18,6 +19,7 @@ config STM32_ADC
config STM32F4_ADC
tristate "STMicroelectronics STM32F4 adc"
depends on ARCH_STM32 || COMPILE_TEST
+ depends on HAS_DMA
depends on OF
select STM32_ADC
help
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1e0850d..25d0307 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
@@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
return ret;
}
+static int stm32_adc_residue(struct stm32_adc *adc)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ if (!adc->rx_buf)
+ return 0;
+
+ status = dmaengine_tx_status(adc->dma_chan,
+ adc->dma_chan->cookie,
+ &state);
+ if (status == DMA_IN_PROGRESS) {
+ /* Residue is size in bytes from end of buffer */
+ int i = adc->rx_buf_sz - state.residue;
+ int size;
+
+ /* Return available bytes */
+ if (i >= adc->bufi)
+ size = i - adc->bufi;
+ else
+ size = adc->rx_buf_sz - adc->bufi + i;
+
+ return size;
+ }
+
+ return 0;
+}
+
/**
* stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
*/
@@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
status &= mask;
- /* Regular data */
- if (status & reginfo->eoc) {
+ /* Regular data (when dma isn't used) */
+ if ((status & reginfo->eoc) && (!adc->rx_buf)) {
adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
if (iio_buffer_enabled(indio_dev)) {
adc->bufi++;
@@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
return indio != indio_dev ? -EINVAL : 0;
}
-static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
- bool state)
+static void stm32_adc_dma_irq_work(struct irq_work *work)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ /**
+ * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
+ * irq context, and cannot be called directly from dma callback,
+ * dma cb has to schedule this work instead.
+ */
+ iio_trigger_poll(indio_dev->trig);
+}
+
+static void stm32_adc_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
struct stm32_adc *adc = iio_priv(indio_dev);
- int ret;
- if (state) {
- /* Reset adc buffer index */
- adc->bufi = 0;
+ /* invoques iio_trigger_poll() from hard irq context */
+ irq_work_queue(&adc->work);
+}
+
+static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config config;
+ dma_cookie_t cookie;
+ int ret, size, watermark;
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ if (!adc->dma_chan) {
/* Allocate adc buffer */
adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (!adc->buffer)
return -ENOMEM;
+ return 0;
+ }
+
+ /*
+ * Allocate at least twice the buffer size for dma cyclic transfers, so
+ * we can work with at least two dma periods. There should be :
+ * - always one buffer (period) dma is working on
+ * - one buffer (period) driver can push with iio_trigger_poll().
+ */
+ size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
+ size = max(indio_dev->scan_bytes * 2, size);
+
+ adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
+ &adc->rx_dma_buf,
+ GFP_KERNEL);
+ if (!adc->rx_buf)
+ return -ENOMEM;
+ adc->rx_buf_sz = size;
+ watermark = indio_dev->buffer->bytes_per_datum
+ * indio_dev->buffer->watermark;
+ watermark = max(indio_dev->scan_bytes, watermark);
+ watermark = rounddown(watermark, indio_dev->scan_bytes);
+
+ dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
+ watermark);
+
+ /* Configure DMA channel to read data register */
+ memset(&config, 0, sizeof(config));
+ config.src_addr = (dma_addr_t)adc->common->phys_base;
+ config.src_addr += adc->offset + reginfo->dr;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ goto config_err;
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+ adc->rx_dma_buf,
+ size, watermark,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ desc->callback = stm32_adc_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ ret = dma_submit_error(cookie);
+ goto config_err;
+ }
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(adc->dma_chan);
+
+ return 0;
+
+config_err:
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
+ adc->rx_dma_buf);
+
+ return ret;
+}
+
+static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan) {
+ kfree(adc->buffer);
+ } else {
+ dmaengine_terminate_all(adc->dma_chan);
+ irq_work_sync(&adc->work);
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
+ adc->rx_buf, adc->rx_dma_buf);
+ adc->rx_buf = NULL;
+ }
+
+ adc->buffer = NULL;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "alloc failed\n");
+ return ret;
+ }
+
ret = stm32_adc_set_trig(indio_dev, trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
goto err_buffer_free;
}
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
ret = stm32_adc_start_conv(adc);
if (ret) {
@@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
return ret;
}
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
ret = stm32_adc_set_trig(indio_dev, NULL);
if (ret)
dev_warn(&indio_dev->dev, "Can't clear trigger\n");
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
}
return 0;
err_irq_trig_disable:
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
stm32_adc_set_trig(indio_dev, NULL);
err_buffer_free:
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
return ret;
}
@@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct stm32_adc *adc = iio_priv(indio_dev);
- dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
-
- /* reset buffer index */
- adc->bufi = 0;
- iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
- pf->timestamp);
+ if (!adc->dma_chan) {
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+ } else {
+ int residue = stm32_adc_residue(adc);
+
+ while (residue >= indio_dev->scan_bytes) {
+ adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ adc->buffer,
+ pf->timestamp);
+ residue -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->rx_buf_sz)
+ adc->bufi = 0;
+ }
+ }
iio_trigger_notify_done(indio_dev->trig);
/* re-enable eoc irq */
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
return IRQ_HANDLED;
}
@@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
if (ret)
goto err_clk_disable;
+ adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+ if (adc->dma_chan)
+ init_irq_work(&adc->work, stm32_adc_dma_irq_work);
+
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
&stm32_adc_trigger_handler,
@@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
iio_triggered_buffer_cleanup(indio_dev);
err_trig_unregister:
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
stm32_adc_trig_unregister(indio_dev);
err_clk_disable:
@@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
stm32_adc_trig_unregister(indio_dev);
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
if (adc->clk) {
clk_disable_unprepare(adc->clk);
clk_put(adc->clk);
@@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
common->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
+ common->phys_base = res->start;
common->data = match->data;
common->dev = &pdev->dev;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 0be603c..01a0dda 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -22,6 +22,9 @@
#ifndef __STM32_ADC_H
#define __STM32_ADC_H
+#include <linux/dmaengine.h>
+#include <linux/irq_work.h>
+
/*
* STM32 - ADC global register map
* ________________________________________________________
@@ -276,6 +279,11 @@ struct stm32_adc_ops {
* @num_conv: expected number of scan conversions
* @injected: use injected channels on this adc
* @lock: spinlock
+ * @work: irq work used to call trigger poll routine
+ * @dma_chan: dma channel
+ * @rx_buf: dma rx buffer cpu address
+ * @rx_dma_buf: dma rx buffer bus address
+ * @rx_buf_sz: dma rx buffer size
* @clk: optional adc clock, for this adc instance
* @calib: optional calibration data
* @en: emulates enabled state on some stm32 adc
@@ -293,6 +301,11 @@ struct stm32_adc {
int num_conv;
bool injected;
spinlock_t lock; /* interrupt lock */
+ struct irq_work work;
+ struct dma_chan *dma_chan;
+ u8 *rx_buf;
+ dma_addr_t rx_dma_buf;
+ int rx_buf_sz;
struct clk *clk;
void *calib;
bool en;
@@ -302,6 +315,7 @@ struct stm32_adc {
* struct stm32_adc_common - private data of ADC driver, common to all
* ADC instances (ADC block)
* @dev: device for this controller
+ * @phys_base: control registers base physical addr
* @base: control registers base cpu addr
* @irq: Common irq line for all adc instances
* @data: STM32 dependent data from compatible
@@ -313,6 +327,7 @@ struct stm32_adc {
*/
struct stm32_adc_common {
struct device *dev;
+ phys_addr_t phys_base;
void __iomem *base;
int irq;
const struct stm32_adc_ops *data;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 147fe9c..4d7a2a8 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
* @adc: stm32 adc instance
*
* Start single conversions for regular or injected channels.
+ * Also take care of normal or DMA mode. DMA is used in circular mode for
+ * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
+ * read doesn't use dma, but direct DR read.
*/
static int stm32f4_adc_start_conv(struct stm32_adc *adc)
{
- u32 trig_msk, start_msk;
+ u32 val, trig_msk, start_msk;
+ unsigned long flags;
dev_dbg(adc->common->dev, "%s %s\n", __func__,
adc->injected ? "injected" : "regular");
stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ if (!adc->injected) {
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= ~(STM32F4_DMA | STM32F4_DDS);
+ if (adc->rx_buf)
+ val |= STM32F4_DMA | STM32F4_DDS;
+ stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+ }
+
if (!stm32f4_adc_is_started(adc)) {
stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
STM32F4_EOCS | STM32F4_ADON);
@@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
}
+ if (!adc->injected)
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_DMA | STM32F4_DDS);
+
return 0;
}
--
1.9.1
^ permalink raw reply related
* [PATCH 02/10] iio: adc: Add stm32 support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 34 ++
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 999 ++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 442 ++++++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
7 files changed, 2056 insertions(+)
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..5c96a55 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -583,4 +583,6 @@ config XILINX_XADC
The driver can also be build as a module. If so, the module will be called
xilinx-xadc.
+source "drivers/iio/adc/stm32/Kconfig"
+
endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a9dbf3a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
new file mode 100644
index 0000000..245d037
--- /dev/null
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -0,0 +1,34 @@
+#
+# STM32 familly ADC drivers
+#
+
+config STM32_ADC
+ tristate
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build the driver for the STMicroelectronics
+ STM32 analog-to-digital converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc.
+
+config STM32F4_ADC
+ tristate "STMicroelectronics STM32F4 adc"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select STM32_ADC
+ help
+ Say yes here to build support for STMicroelectronics stm32f4 Analog
+ to Digital Converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32f4-adc.
+
+config STM32_ADC_DEBUG
+ bool "Enable debug for stm32 ADC drivers"
+ depends on STM32_ADC
+ help
+ Say "yes" to enable debug messages, on stm32 ADC drivers.
diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
new file mode 100644
index 0000000..83e8154
--- /dev/null
+++ b/drivers/iio/adc/stm32/Makefile
@@ -0,0 +1,4 @@
+# Core
+subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
new file mode 100644
index 0000000..1e0850d
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -0,0 +1,999 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include "stm32-adc.h"
+
+/**
+ * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ *
+ * Conversion sequence :
+ * Configure ADC scan sequence based on selected channels in scan_mask.
+ * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
+ * program sequence len.
+ *
+ * Note: This can be done only when ADC enabled and no conversion is ongoing.
+ */
+static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *sq;
+ const struct iio_chan_spec *chan;
+ u32 val, bit;
+ int sq_max, i = 0;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->jsqr_reg;
+ sq_max = STM32_ADC_MAX_JSQ;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->sqr_regs;
+ sq_max = STM32_ADC_MAX_SQ;
+ }
+
+ /* Build sequence for regular or injected channels */
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ /*
+ * Assign one channel per SQ/JSQ entry in regular/injected
+ * sequence, starting with SQ1/JSQ1.
+ */
+ i++;
+ if (i > sq_max)
+ return -EINVAL;
+
+ dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
+ __func__, chan->channel, adc->injected ? "JSQ" : "SQ",
+ i);
+
+ val = stm32_adc_readl(adc, sq[i].reg);
+ val &= ~sq[i].mask;
+ val |= (chan->channel << sq[i].shift) & sq[i].mask;
+ stm32_adc_writel(adc, sq[i].reg, val);
+ }
+
+ if (!i)
+ return -EINVAL;
+
+ /* Sequence len */
+ val = stm32_adc_readl(adc, sq[0].reg);
+ val &= ~sq[0].mask;
+ val |= ((i - 1) << sq[0].shift) & sq[0].mask;
+ stm32_adc_writel(adc, sq[0].reg, val);
+
+ return 0;
+}
+
+static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = stm32_adc_clk_sel(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Clock sel failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_enable(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to enable adc\n");
+ return ret;
+ }
+
+ ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure sequence\n");
+ goto err_dis;
+ }
+
+ return 0;
+
+err_dis:
+ stm32_adc_disable(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_get_trig_index() - Get trigger index
+ * @indio_dev: IIO device
+ * @trig: trigger
+ *
+ * Returns trigger index, if trig matches one of the triggers registered by
+ * stm32 adc driver, -EINVAL otherwise.
+ */
+static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *tr;
+ int i = 0;
+
+ list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
+ if (tr == trig)
+ return i;
+ i++;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_set_trig() - Set a regular or injected trigger
+ * @indio_dev: IIO device
+ * @trig: IIO trigger
+ *
+ * Set trigger source/polarity (e.g. SW, or HW with polarity) :
+ * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
+ * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
+ *
+ * Note: must be called when ADC is enabled
+ */
+static int stm32_adc_set_trig(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_trig_info *trig_info;
+ const struct stm32_adc_trig_reginfo *reginfo;
+ u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+ unsigned long flags;
+ int ret;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
+ trig_info = adc->common->data->jext_triggers;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->trig_reginfo;
+ trig_info = adc->common->data->ext_triggers;
+ }
+
+ if (trig) {
+ ret = stm32_adc_get_trig_index(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* trigger source */
+ extsel = trig_info[ret].extsel;
+ exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ }
+
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, reginfo->reg);
+ val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
+ val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
+ val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
+ stm32_adc_writel(adc, reginfo->reg, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Unmask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_set_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_conv_irq_disable() - Mask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Mask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_clr_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_single_conv() - perform a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @result: conversion result
+ *
+ * The function performs a single conversion on a given channel, by
+ * by:
+ * - creating scan mask with only one channel
+ * - using SW trigger
+ * - then start single conv
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned long *scan_mask;
+ long timeout;
+ u16 result;
+ int ret;
+
+ scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
+ GFP_KERNEL);
+ if (!scan_mask)
+ return -ENOMEM;
+
+ set_bit(chan->scan_index, scan_mask);
+
+ reinit_completion(&adc->completion);
+
+ adc->bufi = 0;
+ adc->num_conv = 1;
+ adc->buffer = &result;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ goto free;
+
+ /* No HW trigger: conversion can be launched in SW */
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set SW trigger\n");
+ goto adc_disable;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start single conv\n");
+ goto irq_disable;
+ }
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
+ ret = -EINTR;
+ } else {
+ *val = result & STM32_RESULT_MASK;
+ ret = IIO_VAL_INT;
+ }
+
+ if (stm32_adc_stop_conv(adc))
+ dev_err(&indio_dev->dev, "stop failed\n");
+
+irq_disable:
+ stm32_adc_conv_irq_disable(adc);
+
+adc_disable:
+ stm32_adc_disable(adc);
+
+free:
+ kfree(scan_mask);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ if (chan->type == IIO_VOLTAGE)
+ ret = stm32_adc_single_conv(indio_dev, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
+ */
+static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
+
+ if (adc->injected) {
+ mask = reginfo->jeoc;
+ clr_mask = mask;
+ } else {
+ mask = reginfo->eoc;
+ /* don't clear 'eoc' as it is cleared when reading 'dr' */
+ clr_mask = 0;
+ }
+
+ /* clear irq */
+ stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
+ status &= mask;
+
+ /* Regular data */
+ if (status & reginfo->eoc) {
+ adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
+ if (iio_buffer_enabled(indio_dev)) {
+ adc->bufi++;
+ if (adc->bufi >= adc->num_conv) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ }
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /* Injected data */
+ if (status & reginfo->jeoc) {
+ int i;
+
+ for (i = 0; i < adc->num_conv; i++) {
+ adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
+ adc->bufi++;
+ }
+
+ if (iio_buffer_enabled(indio_dev)) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /*
+ * In case end of conversion flags have been handled, this has been
+ * handled for this ADC instance
+ */
+ if (status)
+ return IRQ_HANDLED;
+
+ /* This adc instance didn't trigger this interrupt */
+ return IRQ_NONE;
+}
+
+/**
+ * stm32_adc_common_isr() - Common isr for the whole ADC block
+ *
+ * There is one IRQ for all ADCs in ADC block, check all instances.
+ */
+static irqreturn_t stm32_adc_common_isr(int irq, void *data)
+{
+ struct stm32_adc_common *common = data;
+ irqreturn_t ret = IRQ_NONE;
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ ret |= stm32_adc_isr(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_validate_trigger() - validate trigger for stm32 adc
+ * @indio_dev: IIO device
+ * @trig: new trigger
+ *
+ * Returns: 0 if trig matches one of the triggers registered by stm32 adc
+ * driver, -EINVAL otherwise.
+ */
+static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 bit;
+
+ adc->num_conv = 0;
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength)
+ adc->num_conv++;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ * echo [ADC reg offset] > direct_reg_access
+ * cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ * echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!readval)
+ stm32_adc_writel(adc, reg, writeval);
+ else
+ *readval = stm32_adc_readl(adc, reg);
+
+ return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+ .read_raw = stm32_adc_read_raw,
+ .validate_trigger = stm32_adc_validate_trigger,
+ .update_scan_mode = stm32_adc_update_scan_mode,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_disable(adc);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &stm32_adc_buffer_postdisable,
+};
+
+static int stm32_adc_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+ return indio != indio_dev ? -EINVAL : 0;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ /* Allocate adc buffer */
+ adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc->buffer)
+ return -ENOMEM;
+
+ ret = stm32_adc_set_trig(indio_dev, trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set trigger\n");
+ goto err_buffer_free;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start\n");
+ goto err_irq_trig_disable;
+ }
+ } else {
+ ret = stm32_adc_stop_conv(adc);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to stop\n");
+ return ret;
+ }
+
+ stm32_adc_conv_irq_disable(adc);
+
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret)
+ dev_warn(&indio_dev->dev, "Can't clear trigger\n");
+
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+ }
+
+ return 0;
+
+err_irq_trig_disable:
+ stm32_adc_conv_irq_disable(adc);
+ stm32_adc_set_trig(indio_dev, NULL);
+
+err_buffer_free:
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static const struct iio_trigger_ops stm32_adc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .validate_device = stm32_adc_validate_device,
+ .set_trigger_state = stm32_adc_set_trigger_state,
+};
+
+static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+ /* reset buffer index */
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ /* re-enable eoc irq */
+ stm32_adc_conv_irq_enable(adc);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *trig, *_t;
+
+ list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
+ iio_trigger_unregister(trig);
+ list_del(&trig->alloc_list);
+ }
+}
+
+static int stm32_adc_trig_register(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
+ const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
+ struct iio_trigger *trig;
+ int i, ret = 0;
+
+ if (adc->injected)
+ ext = common->data->jext_triggers;
+ else
+ ext = common->data->ext_triggers;
+
+ for (i = 0; ext && ext[i].name; i++) {
+ trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ if (!trig) {
+ dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ trig->dev.parent = common->dev;
+ trig->ops = &stm32_adc_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ dev_err(common->dev,
+ "trig %s_%s%d_%s register failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ goto err;
+ }
+
+ list_add_tail(&trig->alloc_list, &adc->extrig_list);
+ }
+
+ return 0;
+err:
+ stm32_adc_trig_unregister(indio_dev);
+
+ return ret;
+}
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ const struct stm32_adc_chan_spec *channel,
+ int scan_index)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_name = channel->name;
+ chan->extend_name = channel->name;
+ chan->scan_index = scan_index;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = adc->common->data->highres;
+ chan->scan_type.storagebits = STM32_STORAGEBITS;
+ chan->scan_type.shift = 0;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
+ const struct stm32_adc_info *adc_info)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device_node *node = indio_dev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ struct iio_chan_spec *channels;
+ int scan_index = 0, num_channels = 0;
+ u32 val;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
+ num_channels++;
+
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &adc_info->channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ adc->max_channels = adc_info->max_channels;
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_register(struct stm32_adc_common *common,
+ struct device_node *child)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int i, ret;
+ u32 reg;
+
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret != 0) {
+ dev_err(common->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; common->data->adc_info[i].channels; i++)
+ if (common->data->adc_info[i].reg == reg)
+ break;
+
+ if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
+ dev_err(common->dev, "bad adc reg offset\n");
+ return -ENOENT;
+ }
+
+ indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(common->dev, "iio device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->id = i;
+ adc->offset = reg;
+ adc->common = common;
+ INIT_LIST_HEAD(&adc->extrig_list);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ if (child->name)
+ indio_dev->name = child->name;
+ else
+ indio_dev->name = common->data->adc_info[i].name;
+ indio_dev->dev.parent = common->dev;
+ indio_dev->dev.of_node = child;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (of_property_read_bool(child, "st,injected")) {
+ dev_dbg(common->dev, "%s Configured to use injected\n",
+ indio_dev->name);
+ adc->injected = true;
+ }
+
+ adc->clk = of_clk_get(child, 0);
+ if (IS_ERR(adc->clk)) {
+ adc->clk = NULL;
+ dev_dbg(common->dev, "No child clk found\n");
+ } else {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0)
+ goto err_clk_put;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
+ if (ret < 0) {
+ dev_err(common->dev, "iio channels init failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_trig_register(indio_dev);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_adc_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret) {
+ dev_err(common->dev, "buffer setup failed\n");
+ goto err_trig_unregister;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(common->dev, "iio dev register failed\n");
+ goto err_buffer_cleanup;
+ }
+
+ list_add_tail(&adc->adc_list, &common->adc_list);
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+err_trig_unregister:
+ stm32_adc_trig_unregister(indio_dev);
+
+err_clk_disable:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+err_clk_put:
+ if (adc->clk)
+ clk_put(adc->clk);
+
+ return ret;
+}
+
+static void stm32_adc_unregister(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ stm32_adc_trig_unregister(indio_dev);
+ if (adc->clk) {
+ clk_disable_unprepare(adc->clk);
+ clk_put(adc->clk);
+ }
+}
+
+int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node, *child;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct stm32_adc_common *common;
+ struct stm32_adc *adc;
+ struct resource *res;
+ int ret;
+
+ match = of_match_device(dev->driver->of_match_table, &pdev->dev);
+ if (!match || !match->data) {
+ dev_err(&pdev->dev, "compatible data not provided\n");
+ return -EINVAL;
+ }
+
+ common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
+ if (!common)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ common->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(common->base))
+ return PTR_ERR(common->base);
+
+ common->data = match->data;
+ common->dev = &pdev->dev;
+ platform_set_drvdata(pdev, common);
+ mutex_init(&common->lock);
+ INIT_LIST_HEAD(&common->adc_list);
+
+ common->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(common->vref)) {
+ ret = PTR_ERR(common->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ common->vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
+
+ common->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(common->aclk)) {
+ ret = PTR_ERR(common->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(common->aclk);
+ if (ret < 0) {
+ dev_err(common->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ common->irq = platform_get_irq(pdev, 0);
+ if (common->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ ret = common->irq;
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(&pdev->dev, common->irq, stm32_adc_common_isr,
+ 0, pdev->name, common);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ goto err_clk_disable;
+ }
+
+ /* Parse adc child nodes to retrieve master/slave instances data */
+ for_each_available_child_of_node(np, child) {
+ ret = stm32_adc_register(common, child);
+ if (ret)
+ goto err_unregister;
+ }
+
+ dev_info(&pdev->dev, "registered\n");
+
+ return 0;
+
+err_unregister:
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+
+err_clk_disable:
+ clk_disable_unprepare(common->aclk);
+
+err_regulator_disable:
+ regulator_disable(common->vref);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_probe);
+
+int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+ clk_disable_unprepare(common->aclk);
+ regulator_disable(common->vref);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_remove);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
new file mode 100644
index 0000000..0be603c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -0,0 +1,442 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+#define STM32_ADCX_COMN_OFFSET 0x300
+#define STM32_ADC_ID_MAX 3
+#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
+#define STM32_ADC_MAX_JSQ 4 /* JSQ1..JSQ4 */
+
+/* STM32 value masks */
+#define STM32_RESULT_MASK GENMASK(15, 0)
+#define STM32_STORAGEBITS 16
+
+/* External trigger enable for regular or injected channels (exten/jexten) */
+enum stm32_adc_exten {
+ STM32_EXTEN_SWTRIG,
+ STM32_EXTEN_HWTRIG_RISING_EDGE,
+ STM32_EXTEN_HWTRIG_FALLING_EDGE,
+ STM32_EXTEN_HWTRIG_BOTH_EDGES,
+};
+
+enum stm32_adc_extsel {
+ STM32_EXT0,
+ STM32_EXT1,
+ STM32_EXT2,
+ STM32_EXT3,
+ STM32_EXT4,
+ STM32_EXT5,
+ STM32_EXT6,
+ STM32_EXT7,
+ STM32_EXT8,
+ STM32_EXT9,
+ STM32_EXT10,
+ STM32_EXT11,
+ STM32_EXT12,
+ STM32_EXT13,
+ STM32_EXT14,
+ STM32_EXT15,
+ STM32_EXT16,
+ STM32_EXT17,
+ STM32_EXT18,
+ STM32_EXT19,
+ STM32_EXT20,
+ STM32_EXT21,
+ STM32_EXT22,
+ STM32_EXT23,
+ STM32_EXT24,
+ STM32_EXT25,
+ STM32_EXT26,
+ STM32_EXT27,
+ STM32_EXT28,
+ STM32_EXT29,
+ STM32_EXT30,
+ STM32_EXT31,
+};
+
+enum stm32_adc_jextsel {
+ STM32_JEXT0,
+ STM32_JEXT1,
+ STM32_JEXT2,
+ STM32_JEXT3,
+ STM32_JEXT4,
+ STM32_JEXT5,
+ STM32_JEXT6,
+ STM32_JEXT7,
+ STM32_JEXT8,
+ STM32_JEXT9,
+ STM32_JEXT10,
+ STM32_JEXT11,
+ STM32_JEXT12,
+ STM32_JEXT13,
+ STM32_JEXT14,
+ STM32_JEXT15,
+ STM32_JEXT16,
+ STM32_JEXT17,
+ STM32_JEXT18,
+ STM32_JEXT19,
+ STM32_JEXT20,
+ STM32_JEXT21,
+ STM32_JEXT22,
+ STM32_JEXT23,
+ STM32_JEXT24,
+ STM32_JEXT25,
+ STM32_JEXT26,
+ STM32_JEXT27,
+ STM32_JEXT28,
+ STM32_JEXT29,
+ STM32_JEXT30,
+ STM32_JEXT31,
+};
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type: IIO channel type
+ * @channel: channel number (single ended)
+ * @name: channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @extsel: trigger selection for regular or injected
+ * @name: name of the trigger, corresponding to its source
+ */
+struct stm32_adc_trig_info {
+ u32 extsel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @name: default name for this instance (like "adc1")
+ * @reg: reg offset for this instance (e.g. 0x0 for adc1...)
+ * @channels: Reference to stm32 channels spec
+ * @max_channels: Number of single ended channels
+ */
+struct stm32_adc_info {
+ const char *name;
+ u32 reg;
+ const struct stm32_adc_chan_spec *channels;
+ int max_channels;
+};
+
+/**
+ * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
+ * @reg: register offset
+ * @mask: bitfield mask
+ * @shift: left shift
+ */
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+/**
+ * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
+ * @reg: trigger control register offset (exten/jexten)
+ * @exten_mask: external trigger en/polarity mask in @reg
+ * @exten_shift: external trigger en/polarity shift in @reg
+ * @extsel_mask: external trigger source mask in @reg
+ * @extsel_shift: external trigger source shift in @reg
+ */
+struct stm32_adc_trig_reginfo {
+ u32 reg;
+ u32 exten_mask;
+ u32 exten_shift;
+ u32 extsel_mask;
+ u32 extsel_shift;
+};
+
+/**
+ * struct stm32_adc_reginfo - stm32 ADC registers description
+ * @isr: interrupt status register offset
+ * @eoc: end of conversion mask in @isr
+ * @jeoc: end of injected conversion sequence mask in @isr
+ * @ier: interrupt enable register offset
+ * @eocie: end of conversion interrupt enable mask in @ier
+ * @jeocie: end of injected conversion sequence interrupt en mask
+ * @dr: data register offset
+ * @jdr: injected data registers offsets
+ * @sqr_regs: Regular sequence registers description
+ * @jsqr_reg: Injected sequence register description
+ * @trig_reginfo: regular trigger control registers description
+ * @jtrig_reginfo: injected trigger control registers description
+ */
+struct stm32_adc_reginfo {
+ u32 isr;
+ u32 eoc;
+ u32 jeoc;
+ u32 ier;
+ u32 eocie;
+ u32 jeocie;
+ u32 dr;
+ u32 jdr[4];
+ const struct stm32_adc_regs *sqr_regs;
+ const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_trig_reginfo *trig_reginfo;
+ const struct stm32_adc_trig_reginfo *jtrig_reginfo;
+};
+
+struct stm32_adc;
+
+/**
+ * struct stm32_adc_ops - stm32 ADC, compatible dependent data
+ * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
+ *
+ * @adc_info: Array spec for stm32 adc master/slaves instances
+ * @ext_triggers: Reference to trigger info for regular channels
+ * @jext_triggers: Reference to trigger info for injected channels
+ * @adc_reginfo: stm32 ADC registers description
+ * @highres: Max resolution
+ * @max_clock_rate: Max input clock rate
+ * @clk_sel: routine to select common clock and prescaler
+ * @start_conv: routine to start conversions
+ * @stop_conv: routine to stop conversions
+ * @is_started: routine to get adc 'started' state
+ * @regular_started routine to check regular conversions status
+ * @injected_started routine to check injected conversions status
+ * @enable: optional routine to enable stm32 adc
+ * @disable: optional routine to disable stm32 adc
+ * @is_enabled reports enabled state
+ */
+struct stm32_adc_ops {
+ const struct stm32_adc_info *adc_info;
+ const struct stm32_adc_trig_info *ext_triggers;
+ const struct stm32_adc_trig_info *jext_triggers;
+ const struct stm32_adc_reginfo *adc_reginfo;
+ int highres;
+ unsigned long max_clock_rate;
+ int (*clk_sel)(struct stm32_adc *adc);
+ int (*start_conv)(struct stm32_adc *adc);
+ int (*stop_conv)(struct stm32_adc *adc);
+ bool (*is_started)(struct stm32_adc *adc);
+ bool (*regular_started)(struct stm32_adc *adc);
+ bool (*injected_started)(struct stm32_adc *adc);
+ int (*enable)(struct stm32_adc *adc);
+ void (*disable)(struct stm32_adc *adc);
+ bool (*is_enabled)(struct stm32_adc *adc);
+};
+
+struct stm32_adc_common;
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @adc_list: current ADC entry in common ADC list
+ * @id: ADC instance number (e.g. adc 1, 2 or 3)
+ * @offset: ADC instance register offset in ADC block
+ * @max_channels: Max channels number for this ADC.
+ * @extrig_list: External trigger list (for regular channel)
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @bufi: data buffer index
+ * @num_conv: expected number of scan conversions
+ * @injected: use injected channels on this adc
+ * @lock: spinlock
+ * @clk: optional adc clock, for this adc instance
+ * @calib: optional calibration data
+ * @en: emulates enabled state on some stm32 adc
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ struct list_head adc_list;
+ int id;
+ int offset;
+ int max_channels;
+ struct list_head extrig_list;
+ struct completion completion;
+ u16 *buffer;
+ int bufi;
+ int num_conv;
+ bool injected;
+ spinlock_t lock; /* interrupt lock */
+ struct clk *clk;
+ void *calib;
+ bool en;
+};
+
+/**
+ * struct stm32_adc_common - private data of ADC driver, common to all
+ * ADC instances (ADC block)
+ * @dev: device for this controller
+ * @base: control registers base cpu addr
+ * @irq: Common irq line for all adc instances
+ * @data: STM32 dependent data from compatible
+ * @adc_list: list of all stm32 ADC in this ADC block
+ * @aclk: common clock for the analog circuitry
+ * @vref: regulator reference
+ * @vref_mv: vref voltage (mv)
+ * @lock: mutex
+ */
+struct stm32_adc_common {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ const struct stm32_adc_ops *data;
+ struct list_head adc_list;
+ struct clk *aclk;
+ struct regulator *vref;
+ int vref_mv;
+ struct mutex lock; /* read_raw lock */
+};
+
+/* Helper routines */
+static inline int stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->start_conv(adc);
+}
+
+static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->stop_conv(adc);
+}
+
+static inline bool stm32_adc_is_started(struct stm32_adc *adc)
+{
+ return adc->common->data->is_started(adc);
+}
+
+static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
+{
+ return adc->common->data->regular_started(adc);
+}
+
+static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
+{
+ return adc->common->data->injected_started(adc);
+}
+
+static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
+{
+ return adc->common->data->clk_sel(adc);
+}
+
+static inline int stm32_adc_enable(struct stm32_adc *adc)
+{
+ if (adc->common->data->enable)
+ return adc->common->data->enable(adc);
+
+ adc->en = true;
+
+ return 0;
+}
+
+static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
+{
+ if (adc->common->data->is_enabled)
+ return adc->common->data->is_enabled(adc);
+ else
+ return adc->en;
+}
+
+static inline void stm32_adc_disable(struct stm32_adc *adc)
+{
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32_adc_is_started(adc))
+ return;
+
+ if (adc->common->data->disable)
+ adc->common->data->disable(adc);
+ else
+ adc->en = false;
+}
+
+/* STM32 ADC registers access routines */
+static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
+{
+ u32 val = readl_relaxed(com->base + reg);
+
+ return val;
+}
+
+static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
+ u32 reg, u32 val)
+{
+ writel_relaxed(val, com->base + reg);
+}
+
+static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
+
+ return val;
+}
+
+#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+ readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+ cond, sleep_us, timeout_us)
+
+static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/* STM32 common extended attributes */
+extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_probe(struct platform_device *pdev);
+int stm32_adc_remove(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
new file mode 100644
index 0000000..147fe9c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -0,0 +1,574 @@
+/*
+ * This file is part of STM32F4 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/platform_device.h>
+#include "stm32-adc.h"
+
+/*
+ * STM32F4 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADCX_SR 0x00
+#define STM32F4_ADCX_CR1 0x04
+#define STM32F4_ADCX_CR2 0x08
+#define STM32F4_ADCX_SMPR1 0x0C
+#define STM32F4_ADCX_SMPR2 0x10
+#define STM32F4_ADCX_HTR 0x24
+#define STM32F4_ADCX_LTR 0x28
+#define STM32F4_ADCX_SQR1 0x2C
+#define STM32F4_ADCX_SQR2 0x30
+#define STM32F4_ADCX_SQR3 0x34
+#define STM32F4_ADCX_JSQR 0x38
+#define STM32F4_ADCX_JDR1 0x3C
+#define STM32F4_ADCX_JDR2 0x40
+#define STM32F4_ADCX_JDR3 0x44
+#define STM32F4_ADCX_JDR4 0x48
+#define STM32F4_ADCX_DR 0x4C
+
+/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32F4_ADC_CDR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32F4_ADCX_SR - bit fields */
+#define STM32F4_OVR BIT(5)
+#define STM32F4_STRT BIT(4)
+#define STM32F4_JSTRT BIT(3)
+#define STM32F4_JEOC BIT(2)
+#define STM32F4_EOC BIT(1)
+#define STM32F4_AWD BIT(0)
+
+/* STM32F4_ADCX_CR1 - bit fields */
+#define STM32F4_OVRIE BIT(26)
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_AWDEN BIT(23)
+#define STM32F4_JAWDEN BIT(22)
+#define STM32F4_DISCNUM_SHIFT 13
+#define STM32F4_DISCNUM_MASK GENMASK(15, 13)
+#define STM32F4_JDISCEN BIT(12)
+#define STM32F4_DISCEN BIT(11)
+#define STM32F4_JAUTO BIT(10)
+#define STM32F4_AWDSGL BIT(9)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_JEOCIE BIT(7)
+#define STM32F4_AWDIE BIT(6)
+#define STM32F4_EOCIE BIT(5)
+#define STM32F4_AWDCH_SHIFT 0
+#define STM32F4_AWDCH_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_JSWSTART BIT(22)
+#define STM32F4_JEXTEN_SHIFT 20
+#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
+#define STM32F4_JEXTSEL_SHIFT 16
+#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
+#define STM32F4_ALIGN BIT(11)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_CONT BIT(1)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADCX_SMPR1 - bit fields */
+#define STM32F4_SMP18_SHIFT 24
+#define STM32F4_SMP18_MASK GENMASK(26, 24)
+#define STM32F4_SMP17_SHIFT 21
+#define STM32F4_SMP17_MASK GENMASK(23, 21)
+#define STM32F4_SMP16_SHIFT 18
+#define STM32F4_SMP16_MASK GENMASK(20, 18)
+#define STM32F4_SMP15_SHIFT 15
+#define STM32F4_SMP15_MASK GENMASK(17, 15)
+#define STM32F4_SMP14_SHIFT 12
+#define STM32F4_SMP14_MASK GENMASK(14, 12)
+#define STM32F4_SMP13_SHIFT 9
+#define STM32F4_SMP13_MASK GENMASK(11, 9)
+#define STM32F4_SMP12_SHIFT 6
+#define STM32F4_SMP12_MASK GENMASK(8, 6)
+#define STM32F4_SMP11_SHIFT 3
+#define STM32F4_SMP11_MASK GENMASK(5, 3)
+#define STM32F4_SMP10_SHIFT 0
+#define STM32F4_SMP10_MASK GENMASK(2, 0)
+
+/* STM32F4_ADCX_SMPR2 - bit fields */
+#define STM32F4_SMP9_SHIFT 27
+#define STM32F4_SMP9_MASK GENMASK(29, 27)
+#define STM32F4_SMP8_SHIFT 24
+#define STM32F4_SMP8_MASK GENMASK(26, 24)
+#define STM32F4_SMP7_SHIFT 21
+#define STM32F4_SMP7_MASK GENMASK(23, 21)
+#define STM32F4_SMP6_SHIFT 18
+#define STM32F4_SMP6_MASK GENMASK(20, 18)
+#define STM32F4_SMP5_SHIFT 15
+#define STM32F4_SMP5_MASK GENMASK(17, 15)
+#define STM32F4_SMP4_SHIFT 12
+#define STM32F4_SMP4_MASK GENMASK(14, 12)
+#define STM32F4_SMP3_SHIFT 9
+#define STM32F4_SMP3_MASK GENMASK(11, 9)
+#define STM32F4_SMP2_SHIFT 6
+#define STM32F4_SMP2_MASK GENMASK(8, 6)
+#define STM32F4_SMP1_SHIFT 3
+#define STM32F4_SMP1_MASK GENMASK(5, 3)
+#define STM32F4_SMP0_SHIFT 0
+#define STM32F4_SMP0_MASK GENMASK(2, 0)
+enum stm32f4_adc_smpr {
+ STM32F4_SMPR_3_CK_CYCLES,
+ STM32F4_SMPR_15_CK_CYCLES,
+ STM32F4_SMPR_28_CK_CYCLES,
+ STM32F4_SMPR_56_CK_CYCLES,
+ STM32F4_SMPR_84_CK_CYCLES,
+ STM32F4_SMPR_112_CK_CYCLES,
+ STM32F4_SMPR_144_CK_CYCLES,
+ STM32F4_SMPR_480_CK_CYCLES,
+};
+
+/* STM32F4_ADCX_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+#define STM32F4_SQ16_SHIFT 15
+#define STM32F4_SQ16_MASK GENMASK(19, 15)
+#define STM32F4_SQ15_SHIFT 10
+#define STM32F4_SQ15_MASK GENMASK(14, 10)
+#define STM32F4_SQ14_SHIFT 5
+#define STM32F4_SQ14_MASK GENMASK(9, 5)
+#define STM32F4_SQ13_SHIFT 0
+#define STM32F4_SQ13_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR2 - bit fields */
+#define STM32F4_SQ12_SHIFT 25
+#define STM32F4_SQ12_MASK GENMASK(29, 25)
+#define STM32F4_SQ11_SHIFT 20
+#define STM32F4_SQ11_MASK GENMASK(24, 20)
+#define STM32F4_SQ10_SHIFT 15
+#define STM32F4_SQ10_MASK GENMASK(19, 15)
+#define STM32F4_SQ9_SHIFT 10
+#define STM32F4_SQ9_MASK GENMASK(14, 10)
+#define STM32F4_SQ8_SHIFT 5
+#define STM32F4_SQ8_MASK GENMASK(9, 5)
+#define STM32F4_SQ7_SHIFT 0
+#define STM32F4_SQ7_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR3 - bit fields */
+#define STM32F4_SQ6_SHIFT 25
+#define STM32F4_SQ6_MASK GENMASK(29, 25)
+#define STM32F4_SQ5_SHIFT 20
+#define STM32F4_SQ5_MASK GENMASK(24, 20)
+#define STM32F4_SQ4_SHIFT 15
+#define STM32F4_SQ4_MASK GENMASK(19, 15)
+#define STM32F4_SQ3_SHIFT 10
+#define STM32F4_SQ3_MASK GENMASK(14, 10)
+#define STM32F4_SQ2_SHIFT 5
+#define STM32F4_SQ2_MASK GENMASK(9, 5)
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_JSQR - bit fields */
+#define STM32F4_JL_SHIFT 20
+#define STM32F4_JL_MASK GENMASK(21, 20)
+#define STM32F4_JSQ4_SHIFT 15
+#define STM32F4_JSQ4_MASK GENMASK(19, 15)
+#define STM32F4_JSQ3_SHIFT 10
+#define STM32F4_JSQ3_MASK GENMASK(14, 10)
+#define STM32F4_JSQ2_SHIFT 5
+#define STM32F4_JSQ2_MASK GENMASK(9, 5)
+#define STM32F4_JSQ1_SHIFT 0
+#define STM32F4_JSQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/*
+ * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
+ * Define here all inputs for all ADC instances
+ */
+static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
+ /* master ADC1 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+ /* internal analog sources available on input 16 to 18 */
+ { IIO_VOLTAGE, 16, "in16" },
+ { IIO_VOLTAGE, 17, "in17" },
+ { IIO_VOLTAGE, 18, "in18" },
+};
+
+static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
+ /* slave ADC2 / ADC3 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+};
+
+/* Triggers for regular channels */
+static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
+ { STM32_EXT0, "TIM1_CH1" },
+ { STM32_EXT1, "TIM1_CH2" },
+ { STM32_EXT2, "TIM1_CH3" },
+ { STM32_EXT3, "TIM2_CH2" },
+ { STM32_EXT4, "TIM2_CH3" },
+ { STM32_EXT5, "TIM2_CH4" },
+ { STM32_EXT6, "TIM2_TRGO" },
+ { STM32_EXT7, "TIM3_CH1" },
+ { STM32_EXT8, "TIM3_TRGO" },
+ { STM32_EXT9, "TIM4_CH4" },
+ { STM32_EXT10, "TIM5_CH1" },
+ { STM32_EXT11, "TIM5_CH2" },
+ { STM32_EXT12, "TIM5_CH3" },
+ { STM32_EXT13, "TIM8_CH1" },
+ { STM32_EXT14, "TIM8_TRGO" },
+ { STM32_EXT15, "EXTI_11" },
+ {},
+};
+
+/* Triggers for injected channels */
+static const struct stm32_adc_trig_info stm32f4_adc_jext_triggers[] = {
+ { STM32_JEXT0, "TIM1_CH4" },
+ { STM32_JEXT1, "TIM1_TRGO" },
+ { STM32_JEXT2, "TIM2_CH1" },
+ { STM32_JEXT3, "TIM2_TRGO" },
+ { STM32_JEXT4, "TIM3_CH2" },
+ { STM32_JEXT5, "TIM3_CH4" },
+ { STM32_JEXT6, "TIM4_CH1" },
+ { STM32_JEXT7, "TIM4_CH2" },
+ { STM32_JEXT8, "TIM4_CH3" },
+ { STM32_JEXT9, "TIM4_TRGO" },
+ { STM32_JEXT10, "TIM5_CH4" },
+ { STM32_JEXT11, "TIM5_TRGO" },
+ { STM32_JEXT12, "TIM8_CH2" },
+ { STM32_JEXT13, "TIM8_CH3" },
+ { STM32_JEXT14, "TIM8_CH4" },
+ { STM32_JEXT15, "EXTI_15" },
+ {},
+};
+
+static const struct stm32_adc_info stm32f4_adc_info[] = {
+ {
+ .name = "adc1-master",
+ .reg = 0x0,
+ .channels = stm32f4_adc1_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
+ },
+ {
+ .name = "adc2-slave",
+ .reg = 0x100,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {
+ .name = "adc3-slave",
+ .reg = 0x200,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {},
+};
+
+/**
+ * stm32f4_sqr_regs - describe regular sequence registers
+ * - L: sequence len (register & bit field)
+ * - SQ1..SQ16: sequence entries (register & bit field)
+ */
+static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
+ /* L: len bit field description to be kept as first element */
+ { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
+ /* SQ1..SQ16 registers & bit fields */
+ { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
+};
+
+/**
+ * stm32f4_jsqr_reg - describe injected sequence register:
+ * - JL: injected sequence len
+ * - JSQ4..SQ1: sequence entries
+ * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
+ * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
+ * When JL == 1, ADC converts JSQ3, JSQ4
+ * When JL == 0, ADC converts JSQ4
+ */
+static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
+ /* JL: len bit field description to be kept as first element */
+ {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
+ /* JSQ4..JSQ1 registers & bit fields */
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_EXTEN_MASK,
+ .exten_shift = STM32F4_EXTEN_SHIFT,
+ .extsel_mask = STM32F4_EXTSEL_MASK,
+ .extsel_shift = STM32F4_EXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_JEXTEN_MASK,
+ .exten_shift = STM32F4_JEXTEN_SHIFT,
+ .extsel_mask = STM32F4_JEXTSEL_MASK,
+ .extsel_shift = STM32F4_JEXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
+ .isr = STM32F4_ADCX_SR,
+ .eoc = STM32F4_EOC,
+ .jeoc = STM32F4_JEOC,
+ .ier = STM32F4_ADCX_CR1,
+ .eocie = STM32F4_EOCIE,
+ .jeocie = STM32F4_JEOCIE,
+ .dr = STM32F4_ADCX_DR,
+ .jdr = {
+ STM32F4_ADCX_JDR1,
+ STM32F4_ADCX_JDR2,
+ STM32F4_ADCX_JDR3,
+ STM32F4_ADCX_JDR4,
+ },
+ .sqr_regs = stm32f4_sqr_regs,
+ .jsqr_reg = stm32f4_jsqr_reg,
+ .trig_reginfo = &stm32f4_adc_trig_reginfo,
+ .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
+};
+
+static bool stm32f4_adc_is_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
+
+ return !!val;
+}
+
+/**
+ * stm32f4_adc_start_conv() - Start regular or injected conversions
+ * @adc: stm32 adc instance
+ *
+ * Start single conversions for regular or injected channels.
+ */
+static int stm32f4_adc_start_conv(struct stm32_adc *adc)
+{
+ u32 trig_msk, start_msk;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+
+ if (!stm32f4_adc_is_started(adc)) {
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+ }
+
+ if (adc->injected) {
+ trig_msk = STM32F4_JEXTEN_MASK;
+ start_msk = STM32F4_JSWSTART;
+ } else {
+ trig_msk = STM32F4_EXTEN_MASK;
+ start_msk = STM32F4_SWSTART;
+ }
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
+
+ return 0;
+}
+
+static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
+{
+ u32 val;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ /* First disable trigger for either regular or injected channels */
+ if (adc->injected) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
+ } else {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
+ }
+
+ /* Disable adc when all triggered conversion have been disabled */
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
+ if (!val) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
+ }
+
+ return 0;
+}
+
+/* ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
+ * @adc: stm32 adc instance
+ * Select clock prescaler used for analog conversions.
+ */
+static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
+{
+ struct stm32_adc_common *common = adc->common;
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ /* Common prescaler is set only once, when 1st ADC instance starts */
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ if (stm32f4_adc_is_started(adc))
+ return 0;
+
+ rate = clk_get_rate(common->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <=
+ common->data->max_clock_rate)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
+
+ dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+static const struct stm32_adc_ops stm32f4_adc_ops = {
+ .adc_info = stm32f4_adc_info,
+ .ext_triggers = stm32f4_adc_ext_triggers,
+ .jext_triggers = stm32f4_adc_jext_triggers,
+ .adc_reginfo = &stm32f4_adc_reginfo,
+ .highres = 12,
+ .max_clock_rate = 36000000,
+ .clk_sel = stm32f4_adc_clk_sel,
+ .start_conv = stm32f4_adc_start_conv,
+ .stop_conv = stm32f4_adc_stop_conv,
+ .is_started = stm32f4_adc_is_started,
+ .regular_started = stm32f4_adc_regular_started,
+ .injected_started = stm32f4_adc_injected_started,
+};
+
+static const struct of_device_id stm32f4_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
+
+static struct platform_driver stm32f4_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32f4-adc",
+ .of_match_table = stm32f4_adc_of_match,
+ },
+};
+
+module_platform_driver(stm32f4_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds documentation of device tree bindings for the STM32 ADC.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..a9a8b3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,78 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+ interrupt regular conversion sequence (either triggered in SW or HW).
+ Regular sequence is resumed, in case it has been interrupted.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+ inX ADC pins in mode of operation for analog input on external pin.
+- gpios: Array of gpios that may be configured as EXTi trigger sources.
+
+Example:
+ adc: adc@40012000 {
+ compatible = "st,stm32f4-adc";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ gpios = <&gpioa 11 0>,
+ <&gpioa 15 0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc1: adc1-master@0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Optional properties:
+- clocks: Input clock private to this ADC instance.
+- st,injected: Use injected conversion sequence on an ADC, rather than regular.
+- dmas: Phandle to dma channel for this ADC instance, only for regular
+ conversions. See ../../dma/dma.txt for details.
+- dma-names: Must be "rx" when dmas property is being used.
--
1.9.1
^ permalink raw reply related
* [PATCH 00/10] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: jic23-DgEjT+Ai2ygdnm+yROfE0A, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
fabrice.gasnier-qxv4g6HH51o
This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter. It has up to 19 multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.
This driver has been developed and tested on STM32F429 eval board.
It consist of a core driver, to ease support for other STM32 family
ADCs, and a specific driver for STM32F4 ADC.
It allows to use direct or triggered buffer modes with triggers.
Optional support for DMA and few extended attributes is included.
Fabrice Gasnier (10):
Documentation: dt-bindings: Document STM32 ADC DT bindings
iio: adc: Add stm32 support
iio: adc: stm32: add optional dma support
iio: adc: stm32: add optional support for exti gpios
iio: adc: stm32: add trigger polarity ext attr
iio: adc: stm32: add ext attrs to configure sampling time
ARM: configs: stm32: enable IIO ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
ARM: dts: stm32f429: Add dma support to adc
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++
arch/arm/boot/dts/stm32429i-eval.dts | 30 +
arch/arm/boot/dts/stm32f429.dtsi | 68 +
arch/arm/configs/stm32_defconfig | 2 +
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 36 +
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 1334 ++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 489 +++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 652 ++++++++++
11 files changed, 2696 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Lina Iyer @ 2016-10-25 16:24 UTC (permalink / raw)
To: Sudeep Holla
Cc: ulf.hansson, khilman, rjw, linux-pm, linux-arm-kernel, andy.gross,
sboyd, linux-arm-msm, brendan.jackman, lorenzo.pieralisi,
Juri.Lelli, devicetree, Rob Herring
In-Reply-To: <383a62dd-b4d7-f764-baf1-ff73691b3cbc@arm.com>
On Tue, Oct 25 2016 at 09:59 -0600, Sudeep Holla wrote:
>
>
>On 25/10/16 16:26, Lina Iyer wrote:
>>Update domain-idle-state binding to use "domain-idle-state" compatible
>>from Documentation/devicetree/bindings/arm/idle-states.txt.
>>
>>Cc: <devicetree@vger.kernel.org>
>>Cc: Rob Herring <robh@kernel.org>
>>Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
>>Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>---
>> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
>> 1 file changed, 5 insertions(+), 4 deletions(-)
>>
>>diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>>index e165036..6fb53a3 100644
>>--- a/Documentation/devicetree/bindings/power/power_domain.txt
>>+++ b/Documentation/devicetree/bindings/power/power_domain.txt
>>@@ -30,8 +30,9 @@ Optional properties:
>> available in the next section.
>>
>> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
>>- generic domain power state. The idle state definitions are
>>- compatible with arm,idle-state specified in [1].
>>+ generic domain power state. The idle state definitions must be
>>+ compatible with "domain-idle-state"
>
>I would reword the below a bit different so that it's flexible to be
>reused without "arm,idle-state".
>
>>as well as
>>+ "arm,idle-state" as defined in [1].
>
>'Idle states that are "arm,idle-state" compatible are generally
>"domain-idle-state" compatible as well if it's a PM domain.'
>
I believe we should have both compatible strings. Per [1], any CPU that
follows the idle state compatible *must* have "arm,idle-state" as a
compatible. Since we are re-using the same compatible, its only correct
that we retain what is already spec'd up in [1] and in addition provide
this new compatible.
Thanks,
Lina
>or something like that in line with what's in patch 2/4.
>
>That would give us the scope of reuse of "domain-idle-state" in device
>for future. Also it aligns with your patch 4/4.
>
>Otherwise, it looks good.
>
>--
>Regards,
>Sudeep
^ permalink raw reply
* Re: [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: David Lechner @ 2016-10-25 16:24 UTC (permalink / raw)
To: Axel Haslam
Cc: Greg KH, Johan Hovold, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Sekhar Nori, Alan Stern, Kevin Hilman, Sergei Shtylyov,
manjunath.goudar-QSEj5FYQhm4dnm+yROfE0A, Mark Brown,
Alexandre Bailon, linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAKXjFTM8RwsAut8cD0n0foxueSjR4VB4k4WJizXo9SyaW3viOA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 10/25/2016 11:21 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 6:12 PM, David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org> wrote:
>> On 10/25/2016 02:39 AM, Axel Haslam wrote:
>>>
>>> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>>> wrote:
>>>>
>>>> On 10/24/2016 11:46 AM, ahaslam-rdvid1DuHRBWk0Htik3J/w@public.gmane.org wrote:
>>>>>
>>>>>
>>>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>>>> -#error "This file is DA8xx bus glue. Define
>>>>> CONFIG_ARCH_DAVINCI_DA8XX."
>>>>> -#endif
>>>>> +#include "ohci.h"
>>>>> +
>>>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>>>> +
>>>>> +static const char hcd_name[] = "ohci-da8xx";
>>>>
>>>>
>>>>
>>>> why static const char instead of #define? This is only used one time in a
>>>> pr_info, so it seems kind of pointless anyway.
>>>
>>>
>>> Other drivers are using static const for the same variable.
>>> i think static const is preferred over #define because #define doet give a
>>> type.
>>> If you dont mind ill keep it static const.
>>>
>>
>> If this string was used in this file more than one place, I would agree with
>> you, but currently it is only used as the argument of a pr_info(). The
>> string "ohci-da8xx" could just be included in the fmt string instead of
>> using "%s".
>
> I think the purpose was to use it in the .name of the platform_driver
> structure, too. only that not everybody is doing that, i looked at some bad
> examples :(
>
> would you agree to keep it if we use it in .name too?
>
> -Axel
>
>>
Yes, that will make more sense. ;-)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: Axel Haslam @ 2016-10-25 16:21 UTC (permalink / raw)
To: David Lechner
Cc: Greg KH, Johan Hovold, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Sekhar Nori, Alan Stern, Kevin Hilman, Sergei Shtylyov,
manjunath.goudar-QSEj5FYQhm4dnm+yROfE0A, Mark Brown,
Alexandre Bailon, linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <21019d11-3b01-3e0e-60bc-c4873cf20237-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
On Tue, Oct 25, 2016 at 6:12 PM, David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org> wrote:
> On 10/25/2016 02:39 AM, Axel Haslam wrote:
>>
>> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>> wrote:
>>>
>>> On 10/24/2016 11:46 AM, ahaslam-rdvid1DuHRBWk0Htik3J/w@public.gmane.org wrote:
>>>>
>>>>
>>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>>> -#error "This file is DA8xx bus glue. Define
>>>> CONFIG_ARCH_DAVINCI_DA8XX."
>>>> -#endif
>>>> +#include "ohci.h"
>>>> +
>>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>>> +
>>>> +static const char hcd_name[] = "ohci-da8xx";
>>>
>>>
>>>
>>> why static const char instead of #define? This is only used one time in a
>>> pr_info, so it seems kind of pointless anyway.
>>
>>
>> Other drivers are using static const for the same variable.
>> i think static const is preferred over #define because #define doet give a
>> type.
>> If you dont mind ill keep it static const.
>>
>
> If this string was used in this file more than one place, I would agree with
> you, but currently it is only used as the argument of a pr_info(). The
> string "ohci-da8xx" could just be included in the fmt string instead of
> using "%s".
I think the purpose was to use it in the .name of the platform_driver
structure, too. only that not everybody is doing that, i looked at some bad
examples :(
would you agree to keep it if we use it in .name too?
-Axel
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH] ARM: DT: stm32: move dma translation to board files
From: Alexandre TORGUE @ 2016-10-25 16:16 UTC (permalink / raw)
To: Maxime Coquelin, arnd, robh+dt, mark.rutland, linux,
peter.griffin, lee.jones
Cc: devicetree, linux-arm-kernel
stm32f469-disco and stm32f429-eval boards use SDRAM start address remapping
(to @0) to boost performances. A DMA translation through "dma-ranges"
property was needed for other masters than the M4 CPU.
stm32f429-disco doesn't use remapping so doesn't need this DMA translation.
This patches moves this DMA translation definition from stm32f429 soc file
to board files.
Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 13c7cd2..a763c15 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -82,6 +82,10 @@
};
};
+ soc {
+ dma-ranges = <0xc0000000 0x0 0x10000000>;
+ };
+
usbotg_hs_phy: usbphy {
#phy-cells = <0>;
compatible = "usb-nop-xceiv";
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 0596d60..3a1cfdd 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -59,8 +59,6 @@
};
soc {
- dma-ranges = <0xc0000000 0x0 0x10000000>;
-
timer2: timer@40000000 {
compatible = "st,stm32-timer";
reg = <0x40000000 0x400>;
diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
index 9e73656..c2213c0 100644
--- a/arch/arm/boot/dts/stm32f469-disco.dts
+++ b/arch/arm/boot/dts/stm32f469-disco.dts
@@ -64,6 +64,10 @@
aliases {
serial0 = &usart3;
};
+
+ soc {
+ dma-ranges = <0xc0000000 0x0 0x10000000>;
+ };
};
&clk_hse {
--
1.9.1
^ permalink raw reply related
* Re: [PATCH 1/2 v6] iio: gyro: Add MPU-3050 device tree bindings
From: Jonathan Cameron @ 2016-10-25 16:13 UTC (permalink / raw)
To: Linus Walleij, linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Peter Rosin
In-Reply-To: <1477404954-3942-1-git-send-email-linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
On 25/10/16 15:15, Linus Walleij wrote:
> This adds device tree bindings for the MPU-3050 gyroscope. Since it
> is the first set of bindings for a gyroscope, the folder for it
> is also created.
>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to largely ignore this patch ;)
Thanks
Jonathan
> ---
> ChangeLog v5->v6:
> - No changes, just reposting.
> ChangeLog v4->v5:
> - No changes, just reposting.
> ChangeLog v3->v4:
> - No changes, just reposting. If we agree on these bindings and since
> the dependent I2C bindings are merged, this patch should be merge
> material now I guess.
> ChangeLog v2->v3:
> - Augmented device tree bindings to reflect Peter Rosins now merged
> I2C gate bindings.
> ChangeLog v1->v2:
> - Not yet convered to use i2c-gate as I'm waiting for that
> patch set to stabilize.
> ---
> .../bindings/iio/gyroscope/invensense,mpu3050.txt | 46 ++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
> new file mode 100644
> index 000000000000..b0d3b59966bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
> @@ -0,0 +1,46 @@
> +Invensense MPU-3050 Gyroscope device tree bindings
> +
> +Required properties:
> + - compatible : should be "invensense,mpu3050"
> + - reg : the I2C address of the sensor
> +
> +Optional properties:
> + - interrupt-parent : should be the phandle for the interrupt controller
> + - interrupts : interrupt mapping for the trigger interrupt from the
> + internal oscillator. The following IRQ modes are supported:
> + IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
> + IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
> + for the desired interrupt type.
> + - vdd-supply : supply regulator for the main power voltage.
> + - vlogic-supply : supply regulator for the signal voltage.
> + - mount-matrix : see iio/mount-matrix.txt
> +
> +Optional subnodes:
> + - The MPU-3050 will pass through and forward the I2C signals from the
> + incoming I2C bus, alternatively drive traffic to a slave device (usually
> + an accelerometer) on its own initiative. Therefore is supports a subnode
> + i2c gate node. For details see: i2c/i2c-gate.txt
> +
> +Example:
> +
> +mpu3050@68 {
> + compatible = "invensense,mpu3050";
> + reg = <0x68>;
> + interrupt-parent = <&foo>;
> + interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
> + vdd-supply = <&bar>;
> + vlogic-supply = <&baz>;
> +
> + /* External I2C interface */
> + i2c-gate {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + fnord@18 {
> + compatible = "fnord";
> + reg = <0x18>;
> + interrupt-parent = <&foo>;
> + interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
> + };
> + };
> +};
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: David Lechner @ 2016-10-25 16:12 UTC (permalink / raw)
To: Axel Haslam
Cc: Greg KH, Johan Hovold, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Sekhar Nori, Alan Stern, Kevin Hilman, Sergei Shtylyov,
manjunath.goudar-QSEj5FYQhm4dnm+yROfE0A, Mark Brown,
Alexandre Bailon, linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAKXjFTPvEXPRyq0HZiF7mcHWFaFoH7MjFqEKnTyuSCUOB=U7Aw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 10/25/2016 02:39 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org> wrote:
>> On 10/24/2016 11:46 AM, ahaslam-rdvid1DuHRBWk0Htik3J/w@public.gmane.org wrote:
>>>
>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>> -#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX."
>>> -#endif
>>> +#include "ohci.h"
>>> +
>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>> +
>>> +static const char hcd_name[] = "ohci-da8xx";
>>
>>
>> why static const char instead of #define? This is only used one time in a
>> pr_info, so it seems kind of pointless anyway.
>
> Other drivers are using static const for the same variable.
> i think static const is preferred over #define because #define doet give a type.
> If you dont mind ill keep it static const.
>
If this string was used in this file more than one place, I would agree
with you, but currently it is only used as the argument of a pr_info().
The string "ohci-da8xx" could just be included in the fmt string instead
of using "%s".
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v8] mtd: nand: add tango NAND flash controller support
From: Marc Gonzalez @ 2016-10-25 16:10 UTC (permalink / raw)
To: linux-mtd, Boris Brezillon, Richard Weinberger
Cc: DT, Rob Herring, Mark Rutland, Mason, Sebastian Frias
In-Reply-To: <580F5B37.5090100-y1yR0Z3OICC7zZZRDBGcUA@public.gmane.org>
This driver supports the NAND Flash controller embedded in recent
Tango chips, such as SMP8758 and SMP8759.
Signed-off-by: Marc Gonzalez <marc_gonzalez-y1yR0Z3OICC7zZZRDBGcUA@public.gmane.org>
---
drivers/mtd/nand/Kconfig | 7 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/tango_nand.c | 654 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 662 insertions(+)
create mode 100644 drivers/mtd/nand/tango_nand.c
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 7b7a887b4709..84ce3fe6e5e2 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -205,6 +205,13 @@ config MTD_NAND_S3C2410_CLKSTOP
when the is NAND chip selected or released, but will save
approximately 5mA of power when there is nothing happening.
+config MTD_NAND_TANGO
+ tristate "NAND Flash support for Tango chips"
+ depends on ARCH_TANGO || COMPILE_TEST
+ depends on HAS_DMA
+ help
+ Enables the NAND Flash controller on Tango chips.
+
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
depends on HAS_IOMEM
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index cafde6f3d957..4904ad3614fb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
+obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c
new file mode 100644
index 000000000000..74e39a92771c
--- /dev/null
+++ b/drivers/mtd/nand/tango_nand.c
@@ -0,0 +1,654 @@
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+/* Offsets relative to chip->base */
+#define PBUS_CMD 0
+#define PBUS_ADDR 4
+#define PBUS_DATA 8
+
+/* Offsets relative to reg_base */
+#define NFC_STATUS 0x00
+#define NFC_FLASH_CMD 0x04
+#define NFC_DEVICE_CFG 0x08
+#define NFC_TIMING1 0x0c
+#define NFC_TIMING2 0x10
+#define NFC_XFER_CFG 0x14
+#define NFC_PKT_0_CFG 0x18
+#define NFC_PKT_N_CFG 0x1c
+#define NFC_BB_CFG 0x20
+#define NFC_ADDR_PAGE 0x24
+#define NFC_ADDR_OFFSET 0x28
+#define NFC_XFER_STATUS 0x2c
+
+/* NFC_STATUS values */
+#define CMD_READY BIT(31)
+
+/* NFC_FLASH_CMD values */
+#define NFC_READ 1
+#define NFC_WRITE 2
+
+/* NFC_XFER_STATUS values */
+#define PAGE_IS_EMPTY BIT(16)
+
+/* Offsets relative to mem_base */
+#define METADATA 0x000
+#define ERROR_REPORT 0x1c0
+
+/*
+ * Error reports are split in two bytes:
+ * byte 0 for the first packet in the page (PKT_0)
+ * byte 1 for other packets in the page (PKT_N, for N > 0)
+ * ERR_COUNT_PKT_N is the max error count over all but the first packet.
+ */
+#define DECODE_OK_PKT_0(v) (v & BIT(7))
+#define DECODE_OK_PKT_N(v) (v & BIT(15))
+#define ERR_COUNT_PKT_0(v) ((v >> 0) & 0x3f)
+#define ERR_COUNT_PKT_N(v) ((v >> 8) & 0x3f)
+
+/* Offsets relative to pbus_base */
+#define PBUS_CS_CTRL 0x83c
+#define PBUS_PAD_MODE 0x8f0
+
+/* PBUS_CS_CTRL values */
+#define PBUS_IORDY BIT(31)
+
+/*
+ * PBUS_PAD_MODE values
+ * In raw mode, the driver communicates directly with the NAND chips.
+ * In NFC mode, the NAND Flash controller manages the communication.
+ * We use NFC mode for read and write; raw mode for everything else.
+ */
+#define MODE_RAW 0
+#define MODE_NFC BIT(31)
+
+#define METADATA_SIZE 4
+#define BBM_SIZE 6
+#define FIELD_ORDER 15
+
+#define MAX_CS 4
+
+struct tango_nfc {
+ struct nand_hw_control hw;
+ void __iomem *reg_base;
+ void __iomem *mem_base;
+ void __iomem *pbus_base;
+ struct tango_chip *chips[MAX_CS];
+ struct dma_chan *chan;
+ int freq_kHz;
+};
+
+#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw)
+
+struct tango_chip {
+ struct nand_chip nand_chip;
+ void __iomem *base;
+ u32 timing1;
+ u32 timing2;
+ u32 xfer_cfg;
+ u32 pkt_0_cfg;
+ u32 pkt_n_cfg;
+ u32 bb_cfg;
+};
+
+#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip)
+
+#define XFER_CFG(cs, page_count, steps, metadata_size) \
+ ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size))
+
+#define PKT_CFG(size, strength) ((size) << 16 | (strength))
+
+#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size))
+
+#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
+
+static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ if (ctrl & NAND_CLE)
+ writeb_relaxed(dat, tchip->base + PBUS_CMD);
+
+ if (ctrl & NAND_ALE)
+ writeb_relaxed(dat, tchip->base + PBUS_ADDR);
+}
+
+static int tango_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+
+ return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
+}
+
+static uint8_t tango_read_byte(struct mtd_info *mtd)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ return readb_relaxed(tchip->base + PBUS_DATA);
+}
+
+static void tango_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ ioread8_rep(tchip->base + PBUS_DATA, buf, len);
+}
+
+static void tango_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
+}
+
+static void tango_select_chip(struct mtd_info *mtd, int idx)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ struct tango_chip *tchip = to_tango_chip(chip);
+
+ if (idx < 0)
+ return; /* No "chip unselect" function */
+
+ writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
+ writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
+ writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
+ writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG);
+ writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG);
+ writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
+}
+
+/*
+ * The controller does not check for bitflips in erased pages,
+ * therefore software must check instead.
+ */
+static int check_erased_page(struct nand_chip *chip, u8 *buf)
+{
+ u8 *meta = chip->oob_poi + BBM_SIZE;
+ u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int i, res, meta_len, bitflips = 0;
+
+ for (i = 0; i < chip->ecc.steps; ++i) {
+ meta_len = i ? 0 : METADATA_SIZE;
+ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
+ meta, meta_len, chip->ecc.strength);
+ if (res < 0)
+ chip->mtd.ecc_stats.failed++;
+
+ bitflips = max(res, bitflips);
+ buf += pkt_size;
+ ecc += ecc_size;
+ }
+
+ return bitflips;
+}
+
+static int decode_error_report(struct tango_nfc *nfc)
+{
+ u32 status, res;
+
+ status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS);
+ if (status & PAGE_IS_EMPTY)
+ return 0;
+
+ res = readl_relaxed(nfc->mem_base + ERROR_REPORT);
+
+ if (DECODE_OK_PKT_0(res) && DECODE_OK_PKT_N(res))
+ return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res));
+
+ return -EBADMSG;
+}
+
+static void tango_dma_callback(void *arg)
+{
+ complete(arg);
+}
+
+static int do_dma(struct tango_nfc *nfc, int dir, int cmd,
+ const void *buf, int len, int page)
+{
+ void __iomem *addr = nfc->reg_base + NFC_STATUS;
+ struct dma_chan *chan = nfc->chan;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist sg;
+ struct completion tx_done;
+ int err = -EIO;
+ u32 res, val;
+
+ sg_init_one(&sg, buf, len);
+ if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
+ return -EIO;
+
+ desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, DMA_PREP_INTERRUPT);
+ if (!desc)
+ goto dma_unmap;
+
+ desc->callback = tango_dma_callback;
+ desc->callback_param = &tx_done;
+ init_completion(&tx_done);
+
+ writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE);
+
+ writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE);
+ writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET);
+ writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD);
+
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+
+ res = wait_for_completion_timeout(&tx_done, HZ);
+ if (res > 0)
+ err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000);
+
+ writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
+
+dma_unmap:
+ dma_unmap_sg(chan->device->dev, &sg, 1, dir);
+
+ return err;
+}
+
+static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ int err, res, len = mtd->writesize;
+
+ if (oob_required)
+ chip->ecc.read_oob(mtd, chip, page);
+
+ err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page);
+ if (err)
+ return err;
+
+ res = decode_error_report(nfc);
+ if (res < 0) {
+ chip->ecc.read_oob_raw(mtd, chip, page);
+ res = check_erased_page(chip, buf);
+ }
+
+ return res;
+}
+
+static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ int err, len = mtd->writesize;
+
+ /* Calling tango_write_oob() would send PAGEPROG twice */
+ if (oob_required)
+ return -ENOTSUPP;
+
+ writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
+ err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
+{
+ *pos += len;
+
+ if (*buf == NULL) {
+ /* skip over "len" bytes */
+ chip->cmdfunc(&chip->mtd, NAND_CMD_RNDOUT, *pos, -1);
+ } else {
+ tango_read_buf(&chip->mtd, *buf, len);
+ *buf += len;
+ }
+}
+
+static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
+{
+ *pos += len;
+
+ if (*buf == NULL) {
+ /* skip over "len" bytes */
+ chip->cmdfunc(&chip->mtd, NAND_CMD_SEQIN, *pos, -1);
+ } else {
+ tango_write_buf(&chip->mtd, *buf, len);
+ *buf += len;
+ }
+}
+
+/*
+ * Physical page layout (not drawn to scale)
+ *
+ * NB: Bad Block Marker area splits PKT_N in two (N1, N2).
+ *
+ * +---+-----------------+-------+-----+-----------+-----+----+-------+
+ * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N |
+ * +---+-----------------+-------+-----+-----------+-----+----+-------+
+ *
+ * Logical page layout:
+ *
+ * +-----+---+-------+-----+-------+
+ * oob = | BBM | M | ECC_0 | ... | ECC_N |
+ * +-----+---+-------+-----+-------+
+ *
+ * +-----------------+-----+-----------------+
+ * buf = | PKT_0 | ... | PKT_N |
+ * +-----------------+-----+-----------------+
+ */
+static int raw_read(struct nand_chip *chip, u8 *buf, u8 *oob)
+{
+ u8 *oob_orig = oob;
+ const int page_size = chip->mtd.writesize;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int pos = 0; /* position within physical page */
+ int rem = page_size; /* bytes remaining until BBM area */
+
+ if (oob != NULL)
+ oob += BBM_SIZE;
+
+ aux_read(chip, &oob, METADATA_SIZE, &pos);
+
+ while (rem > pkt_size) {
+ aux_read(chip, &buf, pkt_size, &pos);
+ aux_read(chip, &oob, ecc_size, &pos);
+ rem = page_size - pos;
+ }
+
+ aux_read(chip, &buf, rem, &pos);
+ aux_read(chip, &oob_orig, BBM_SIZE, &pos);
+ aux_read(chip, &buf, pkt_size - rem, &pos);
+ aux_read(chip, &oob, ecc_size, &pos);
+
+ return 0;
+}
+
+static int raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
+{
+ const u8 *oob_orig = oob;
+ const int page_size = chip->mtd.writesize;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int pos = 0; /* position within physical page */
+ int rem = page_size; /* bytes remaining until BBM area */
+
+ if (oob != NULL)
+ oob += BBM_SIZE;
+
+ aux_write(chip, &oob, METADATA_SIZE, &pos);
+
+ while (rem > pkt_size) {
+ aux_write(chip, &buf, pkt_size, &pos);
+ aux_write(chip, &oob, ecc_size, &pos);
+ rem = page_size - pos;
+ }
+
+ aux_write(chip, &buf, rem, &pos);
+ aux_write(chip, &oob_orig, BBM_SIZE, &pos);
+ aux_write(chip, &buf, pkt_size - rem, &pos);
+ aux_write(chip, &oob, ecc_size, &pos);
+
+ return 0;
+}
+
+static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ return raw_read(chip, buf, chip->oob_poi);
+}
+
+static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ return raw_write(chip, buf, chip->oob_poi);
+}
+
+static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ return raw_read(chip, NULL, chip->oob_poi);
+}
+
+static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
+ raw_write(chip, NULL, chip->oob_poi);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ chip->waitfunc(mtd, chip);
+ return 0;
+}
+
+static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (idx >= ecc->steps)
+ return -ERANGE;
+
+ res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx;
+ res->length = ecc->bytes;
+
+ return 0;
+}
+
+static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
+{
+ return -ERANGE; /* no free space in spare area */
+}
+
+static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = {
+ .ecc = oob_ecc,
+ .free = oob_free,
+};
+
+static u32 to_ticks(int kHz, int ps)
+{
+ return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC);
+}
+
+static int tango_set_timings(struct mtd_info *mtd,
+ const struct nand_data_interface *conf, bool check_only)
+{
+ const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ struct tango_chip *tchip = to_tango_chip(chip);
+ u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr;
+ int kHz = nfc->freq_kHz;
+
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ if (check_only)
+ return 0;
+
+ Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max);
+ Textw = to_ticks(kHz, sdr->tWB_max);
+ Twc = to_ticks(kHz, sdr->tWC_min);
+ Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min);
+
+ Tacc = to_ticks(kHz, sdr->tREA_max);
+ Thold = to_ticks(kHz, sdr->tREH_min);
+ Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min);
+ Textr = to_ticks(kHz, sdr->tRHZ_max);
+
+ tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw);
+ tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr);
+
+ return 0;
+}
+
+static int chip_init(struct device *dev, struct device_node *np)
+{
+ u32 cs;
+ int err, res;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct tango_chip *tchip;
+ struct nand_ecc_ctrl *ecc;
+ struct tango_nfc *nfc = dev_get_drvdata(dev);
+
+ tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL);
+ if (!tchip)
+ return -ENOMEM;
+
+ res = of_property_count_u32_elems(np, "reg");
+ if (res < 0)
+ return res;
+
+ if (res != 1)
+ return -ENOTSUPP; /* Multi-CS chips are not supported */
+
+ err = of_property_read_u32_index(np, "reg", 0, &cs);
+ if (err)
+ return err;
+
+ if (cs >= MAX_CS)
+ return -EINVAL;
+
+ chip = &tchip->nand_chip;
+ ecc = &chip->ecc;
+ mtd = &chip->mtd;
+
+ chip->read_byte = tango_read_byte;
+ chip->write_buf = tango_write_buf;
+ chip->read_buf = tango_read_buf;
+ chip->select_chip = tango_select_chip;
+ chip->cmd_ctrl = tango_cmd_ctrl;
+ chip->dev_ready = tango_dev_ready;
+ chip->setup_data_interface = tango_set_timings;
+ chip->options = NAND_USE_BOUNCE_BUFFER
+ | NAND_NO_SUBPAGE_WRITE
+ | NAND_WAIT_TCCS;
+ chip->controller = &nfc->hw;
+ tchip->base = nfc->pbus_base + (cs * 256);
+
+ nand_set_flash_node(chip, np);
+ mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops);
+ mtd->dev.parent = dev;
+
+ err = nand_scan_ident(mtd, 1, NULL);
+ if (err)
+ return err;
+
+ ecc->mode = NAND_ECC_HW;
+ ecc->algo = NAND_ECC_BCH;
+ ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
+
+ ecc->read_page_raw = tango_read_page_raw;
+ ecc->write_page_raw = tango_write_page_raw;
+ ecc->read_page = tango_read_page;
+ ecc->write_page = tango_write_page;
+ ecc->read_oob = tango_read_oob;
+ ecc->write_oob = tango_write_oob;
+
+ err = nand_scan_tail(mtd);
+ if (err)
+ return err;
+
+ tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE);
+ tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength);
+ tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength);
+ tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE);
+
+ err = mtd_device_register(mtd, NULL, 0);
+ if (err)
+ return err;
+
+ nfc->chips[cs] = tchip;
+
+ return 0;
+}
+
+static int tango_nand_remove(struct platform_device *pdev)
+{
+ int cs;
+ struct tango_nfc *nfc = platform_get_drvdata(pdev);
+
+ dma_release_channel(nfc->chan);
+
+ for (cs = 0; cs < MAX_CS; ++cs) {
+ if (nfc->chips[cs] != NULL)
+ nand_release(&nfc->chips[cs]->nand_chip.mtd);
+ }
+
+ return 0;
+}
+
+static int tango_nand_probe(struct platform_device *pdev)
+{
+ int err;
+ struct clk *clk;
+ struct resource *res;
+ struct tango_nfc *nfc;
+ struct device_node *np;
+
+ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->reg_base))
+ return PTR_ERR(nfc->reg_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ nfc->mem_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->mem_base))
+ return PTR_ERR(nfc->mem_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->pbus_base))
+ return PTR_ERR(nfc->pbus_base);
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ nfc->chan = dma_request_chan(&pdev->dev, "nfc_sbox");
+ if (IS_ERR(nfc->chan))
+ return PTR_ERR(nfc->chan);
+
+ platform_set_drvdata(pdev, nfc);
+ nand_hw_control_init(&nfc->hw);
+ nfc->freq_kHz = clk_get_rate(clk) / 1000;
+
+ for_each_child_of_node(pdev->dev.of_node, np) {
+ err = chip_init(&pdev->dev, np);
+ if (err) {
+ tango_nand_remove(pdev);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id tango_nand_ids[] = {
+ { .compatible = "sigma,smp8758-nand" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver tango_nand_driver = {
+ .probe = tango_nand_probe,
+ .remove = tango_nand_remove,
+ .driver = {
+ .name = "tango-nand",
+ .of_match_table = tango_nand_ids,
+ },
+};
+
+module_platform_driver(tango_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sigma Designs");
+MODULE_DESCRIPTION("Tango4 NAND Flash controller driver");
--
2.9.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH/RFT v2 07/17] ARM: davinci: da8xx: Enable the usb20 "per" clk on phy_clk_enable
From: David Lechner @ 2016-10-25 16:05 UTC (permalink / raw)
To: Sekhar Nori, ahaslam-rdvid1DuHRBWk0Htik3J/w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
johan-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz,
khilman-rdvid1DuHRBWk0Htik3J/w, sshtylyov-hkdhdckH98+B+jHODAdFcQ,
manjunath.goudar-QSEj5FYQhm4dnm+yROfE0A,
broonie-DgEjT+Ai2ygdnm+yROfE0A, abailon-rdvid1DuHRBWk0Htik3J/w
Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <833b6b60-4504-dac7-ccbb-bc08c985ad01-l0cyMroinI0@public.gmane.org>
On 10/25/2016 05:12 AM, Sekhar Nori wrote:
> On Monday 24 October 2016 10:16 PM, ahaslam-rdvid1DuHRBWk0Htik3J/w@public.gmane.org wrote:
>> diff --git a/arch/arm/mach-davinci/usb-da8xx.c b/arch/arm/mach-davinci/usb-da8xx.c
>> index 9e41a7f..982e105 100644
>> --- a/arch/arm/mach-davinci/usb-da8xx.c
>> +++ b/arch/arm/mach-davinci/usb-da8xx.c
>> @@ -53,11 +53,19 @@ int __init da8xx_register_usb_refclkin(int rate)
>>
>> static void usb20_phy_clk_enable(struct clk *clk)
>> {
>> + struct clk *usb20_clk;
>> u32 val;
>> u32 timeout = 500000; /* 500 msec */
>>
>> val = readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG));
>>
>> + usb20_clk = clk_get(NULL, "usb20");
>
> We should not be using a NULL device pointer here. Can you pass the musb
> device pointer available in the same file? Also, da850_clks[] in da850.c
> needs to be fixed to add the matching device name.
This clock can be used for usb 1.1 PHY even when musb is not being used,
so I don't think we can depend on having a musb device here.
Also, in a previous review, it was decided that the usb clocks should
*not* be added to da850_clks[] [1]. Instead, they are dynamically
registered elsewhere.
[1]: http://www.gossamer-threads.com/lists/linux/kernel/2396533
>
>> + if (IS_ERR(usb20_clk)) {
>> + pr_err("could not get usb20 clk\n");
>> + return;
>> + }
>
> Thanks,
> Sekhar
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Sudeep Holla @ 2016-10-25 15:59 UTC (permalink / raw)
To: Lina Iyer, ulf.hansson, khilman, rjw, linux-pm, linux-arm-kernel
Cc: Sudeep Holla, andy.gross, sboyd, linux-arm-msm, brendan.jackman,
lorenzo.pieralisi, Juri.Lelli, devicetree, Rob Herring
In-Reply-To: <1477409199-52182-4-git-send-email-lina.iyer@linaro.org>
On 25/10/16 16:26, Lina Iyer wrote:
> Update domain-idle-state binding to use "domain-idle-state" compatible
> from Documentation/devicetree/bindings/arm/idle-states.txt.
>
> Cc: <devicetree@vger.kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
> 1 file changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> index e165036..6fb53a3 100644
> --- a/Documentation/devicetree/bindings/power/power_domain.txt
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -30,8 +30,9 @@ Optional properties:
> available in the next section.
>
> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
> - generic domain power state. The idle state definitions are
> - compatible with arm,idle-state specified in [1].
> + generic domain power state. The idle state definitions must be
> + compatible with "domain-idle-state"
I would reword the below a bit different so that it's flexible to be
reused without "arm,idle-state".
> as well as
> + "arm,idle-state" as defined in [1].
'Idle states that are "arm,idle-state" compatible are generally
"domain-idle-state" compatible as well if it's a PM domain.'
or something like that in line with what's in patch 2/4.
That would give us the scope of reuse of "domain-idle-state" in device
for future. Also it aligns with your patch 4/4.
Otherwise, it looks good.
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v4 4/5] mm: make processing of movable_node arch-specific
From: Reza Arbab @ 2016-10-25 15:55 UTC (permalink / raw)
To: Balbir Singh
Cc: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
Rob Herring, Frank Rowand, Andrew Morton, Bharata B Rao,
Nathan Fontenot, Stewart Smith, Alistair Popple, Aneesh Kumar K.V,
Tang Chen, linux-kernel, linuxppc-dev, devicetree, linux-mm
In-Reply-To: <dcfc8ace-e59e-6b4b-0f2f-4eff9f08f3c1@gmail.com>
On Tue, Oct 25, 2016 at 11:15:40PM +1100, Balbir Singh wrote:
>After the ack, I realized there were some more checks needed, IOW
>questions for you :)
Hey! No takebacks!
The short answer is that neither of these is a concern.
Longer; if you use "movable_node", x86 can identify these nodes at boot.
They call memblock_mark_hotplug() while parsing the SRAT. Then, when the
zones are initialized, those markings are used to determine ZONE_MOVABLE.
We have no analog of this SRAT information, so our movable nodes can
only be created post boot, by hotplugging and explicitly onlining with
online_movable.
>1. Have you checked to see if our memblock allocations spill
>over to probably hotpluggable nodes?
Since our nodes don't exist at boot, we don't have that short window
before the zones are drawn where the node has normal memory, and a
kernel allocation might occur within.
>2. Shouldn't we be marking nodes discovered as movable via
>memblock_mark_hotplug()?
Again, this early boot marking mechanism only applies to movable_node.
--
Reza Arbab
^ permalink raw reply
* Re: [PATCH/RFT v2 02/17] ARM: davinci: da8xx: Add CFGCHIP syscon platform declaration.
From: David Lechner @ 2016-10-25 15:53 UTC (permalink / raw)
To: Sekhar Nori, Axel Haslam
Cc: Greg KH, Johan Hovold, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Alan Stern,
Kevin Hilman, Sergei Shtylyov,
manjunath.goudar-QSEj5FYQhm4dnm+yROfE0A, Mark Brown,
Alexandre Bailon, linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <758f206c-d0bd-6f70-da1e-42c88d6dd1f0-l0cyMroinI0@public.gmane.org>
Hi Sekhar,
On 10/25/2016 05:17 AM, Sekhar Nori wrote:
> On Tuesday 25 October 2016 03:07 PM, Axel Haslam wrote:
>> Hi Sekar,
>>
>> On Tue, Oct 25, 2016 at 10:10 AM, Sekhar Nori <nsekhar-l0cyMroinI0@public.gmane.org> wrote:
>>> On Monday 24 October 2016 10:16 PM, ahaslam-rdvid1DuHRBWk0Htik3J/w@public.gmane.org wrote:
>>>> From: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>>>>
>>>> The CFGCHIP registers are used by a number of devices, so using a syscon
>>>> device to share them. The first consumer of this will by the phy-da8xx-usb
>>>> driver.
>>>>
>>>> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>>>> [Axel: minor fix: change id to -1]
>>>
>>> Can you please clarify this change? There could be other syscon devices
>>> on the chip for other common registers. Why use the singular device-id?
>>>
>>
>> in the case of non DT boot, the phy driver is looking for "syscon" :
>>
>> d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon");
>>
>> if we register the syscon driver with id = 0, the actual name of the syscon
>> device will be "syscon.0" and the phy driver will fail to probe, because
>> the strncmp match in the syscon driver (syscon_match_pdevname)
>> will fail.
>>
>> should i change the phy driver instead?
>
> Yes, please. Forcing only one syscon region for the whole chip will be
> too restrictive, I am pretty sure.
>
> Thanks,
> Sekhar
>
In the previous review, you requested that this be changed to -1 [1].
If we change it back to 0, it will also require reverting a patch to the
phy driver that has already been merged[2].
[1]: http://www.gossamer-threads.com/lists/linux/kernel/2435807?page=last
[2]: http://www.gossamer-threads.com/lists/linux/kernel/2518804
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox