* [PATCH] iio: stm32: fix adc/trigger link error
From: Jonathan Cameron @ 2017-12-02 11:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <da83af87-6c70-8b00-b602-342a9274f81f@st.com>
On Mon, 27 Nov 2017 10:09:47 +0100
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
> On 09/25/2017 07:26 PM, Jonathan Cameron wrote:
> > On Mon, 25 Sep 2017 11:16:04 +0100
> > Jonathan Cameron <Jonathan.Cameron@huawei.com> wrote:
> >
> >> On Mon, 25 Sep 2017 11:54:06 +0200
> >> Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
> >>
> >>> On 09/10/2017 06:00 PM, Jonathan Cameron wrote:
> >>>> On Wed, 6 Sep 2017 18:16:33 +0200
> >>>> Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
> >>>>
> >>>>> On 09/06/2017 02:56 PM, Arnd Bergmann wrote:
> >>>>>> The ADC driver can trigger on either the timer or the lptim
> >>>>>> trigger, but it only uses a Kconfig 'select' statement
> >>>>>> to ensure that the first of the two is present. When the lptim
> >>>>>> trigger is enabled as a loadable module, and the adc driver
> >>>>>> is built-in, we now get a link error:
> >>>>>>
> >>>>>> drivers/iio/adc/stm32-adc.o: In function `stm32_adc_get_trig_extsel':
> >>>>>> stm32-adc.c:(.text+0x4e0): undefined reference to `is_stm32_lptim_trigger'
> >>>>>>
> >>>>>> We could use a second 'select' statement and always have both
> >>>>>> trigger drivers enabled when the adc driver is, but it seems that
> >>>>>> the lptimer trigger was intentionally left optional, so it seems
> >>>>>> better to keep it that way.
> >>>>> Hi Arnd,
> >>>>>
> >>>>> This is correct, not all stm32 have this hardware.
> >>>>>>
> >>>>>> This adds a hack to use 'IS_REACHABLE()' rather than 'IS_ENABLED()',
> >>>>>> which avoids the link error, but instead leads to the lptimer trigger
> >>>>>> not being used in the broken configuration. I've added a runtime
> >>>>>> warning for this case to help users figure out what they did wrong
> >>>>>> if this should ever be done by accident.
> >>>>>>
> >>>>>> Fixes: f0b638a7f6db ("iio: adc: stm32: add support for lptimer triggers")
> >>>>>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> >>>>> Tested-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> >>>>
> >>>> This may take me a few weeks to pick up. I don't want to
> >>>> pull the mfd immutable branch in just to have this fix, so this will
> >>>> get sorted after rc1 is out.
> >>>>
> >>>> As ever, poke me if it looks like I've forgotten about it.
> >>>
> >>> Hi Jonathan,
> >>>
> >>> Di you apply this one?
> >>
> >> I think I still need to rebase my branch. Greg took my fixes pull this morning
> >> so I can fast forward that branch next time I'm on the right PC and then pick
> >> this up.
> >>
> >> Thanks for the reminder.
> >
> > Applied to the fixes-togreg branch of iio.git.
>
> Hi Jonathan,
>
> Sorry to raise this again. Maybe I missed something, but I can't find
> this fix on recent tag (4.15-rc1). Do you know if this patch from Arnd
> is on its way in ?
Hi Fabrice,
You are absolutely correct to raise this (and please do so if similar
seems to have happened in the future).
I dropped this one somehow - now applied to my fixes-togreg branch of iio.git
and definitely pushed out.
Sorry about that. Will hopefully get a pull request out in the next
day or so.
Thanks,
Jonathan
>
> Please kindly let me know.
> BR,
> Fabrice
> >
> > Thanks,
> >
> > Jonathan
> >>
> >> Jonathan
> >>
> >>>
> >>> Thanks,
> >>> Best Regards,
> >>> Fabrice
> >>>> Sorry for the delay!
> >>>>
> >>>> Jonathan
> >>>>
> >>>>>
> >>>>> Many thanks for the fix,
> >>>>> Best Regards,
> >>>>> Fabrice
> >>>>>> ---
> >>>>>> include/linux/iio/timer/stm32-lptim-trigger.h | 5 ++++-
> >>>>>> 1 file changed, 4 insertions(+), 1 deletion(-)
> >>>>>>
> >>>>>> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
> >>>>>> index 34d59bfdce2d..464458d20b16 100644
> >>>>>> --- a/include/linux/iio/timer/stm32-lptim-trigger.h
> >>>>>> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
> >>>>>> @@ -16,11 +16,14 @@
> >>>>>> #define LPTIM2_OUT "lptim2_out"
> >>>>>> #define LPTIM3_OUT "lptim3_out"
> >>>>>>
> >>>>>> -#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> >>>>>> +#if IS_REACHABLE(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> >>>>>> bool is_stm32_lptim_trigger(struct iio_trigger *trig);
> >>>>>> #else
> >>>>>> static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> >>>>>> {
> >>>>>> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> >>>>>> + pr_warn_once("stm32 lptim_trigger not linked in\n");
> >>>>>> +#endif
> >>>>>> return false;
> >>>>>> }
> >>>>>> #endif
> >>>>>>
> >>>>> --
> >>>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> >>>>> the body of a message to majordomo at vger.kernel.org
> >>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >>>>
> >>>
> >>> _______________________________________________
> >>> linux-arm-kernel mailing list
> >>> linux-arm-kernel at lists.infradead.org
> >>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> >>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> >> the body of a message to majordomo at vger.kernel.org
> >> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH] pinctrl: gemini: Support drive strength setting
From: Linus Walleij @ 2017-12-02 11:23 UTC (permalink / raw)
To: linux-arm-kernel
The Gemini pin controller can set drive strength for a few
select groups of pins (not individually). Implement this
for GMAC0 and 1 (ethernet ports), IDE and PCI.
Cc: devicetree at vger.kernel.org
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
The DT binding part is just using generic bindings, so should be
pretty uncontroversial.
---
.../bindings/pinctrl/cortina,gemini-pinctrl.txt | 3 +
drivers/pinctrl/pinctrl-gemini.c | 81 ++++++++++++++++++++++
2 files changed, 84 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt
index d857b67fab72..4346ff2dd8e6 100644
--- a/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt
@@ -17,6 +17,9 @@ and generic pin config nodes.
Supported configurations:
- skew-delay is supported on the Ethernet pins
+- drive-strength with 4, 8, 12 or 16 mA as argument is supported for
+ entire groups on the groups "idegrp", "gmii_gmac0_grp", "gmii_gmac1_grp"
+ and "pcigrp".
Example:
diff --git a/drivers/pinctrl/pinctrl-gemini.c b/drivers/pinctrl/pinctrl-gemini.c
index c11b8f14d841..56db4e77b220 100644
--- a/drivers/pinctrl/pinctrl-gemini.c
+++ b/drivers/pinctrl/pinctrl-gemini.c
@@ -67,6 +67,9 @@ struct gemini_pmx {
* elements in .pins so we can iterate over that array
* @mask: bits to clear to enable this when doing pin muxing
* @value: bits to set to enable this when doing pin muxing
+ * @driving_mask: bitmask for the IO Pad driving register for this
+ * group, if it supports altering the driving strength of
+ * its lines.
*/
struct gemini_pin_group {
const char *name;
@@ -74,12 +77,14 @@ struct gemini_pin_group {
const unsigned int num_pins;
u32 mask;
u32 value;
+ u32 driving_mask;
};
/* Some straight-forward control registers */
#define GLOBAL_WORD_ID 0x00
#define GLOBAL_STATUS 0x04
#define GLOBAL_STATUS_FLPIN BIT(20)
+#define GLOBAL_IODRIVE 0x10
#define GLOBAL_GMAC_CTRL_SKEW 0x1c
#define GLOBAL_GMAC0_DATA_SKEW 0x20
#define GLOBAL_GMAC1_DATA_SKEW 0x24
@@ -738,6 +743,7 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
/* Conflict with all flash usage */
.value = IDE_PADS_ENABLE | NAND_PADS_DISABLE |
PFLASH_PADS_DISABLE | SFLASH_PADS_DISABLE,
+ .driving_mask = GENMASK(21, 20),
},
{
.name = "satagrp",
@@ -753,6 +759,7 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
.name = "gmii_gmac0_grp",
.pins = gmii_gmac0_3512_pins,
.num_pins = ARRAY_SIZE(gmii_gmac0_3512_pins),
+ .driving_mask = GENMASK(17, 16),
},
{
.name = "gmii_gmac1_grp",
@@ -760,6 +767,7 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
.num_pins = ARRAY_SIZE(gmii_gmac1_3512_pins),
/* Bring out RGMII on the GMAC1 pins */
.value = GEMINI_GMAC_IOSEL_GMAC0_GMAC1_RGMII,
+ .driving_mask = GENMASK(19, 18),
},
{
.name = "pcigrp",
@@ -767,6 +775,7 @@ static const struct gemini_pin_group gemini_3512_pin_groups[] = {
.num_pins = ARRAY_SIZE(pci_3512_pins),
/* Conflict only with GPIO2 */
.value = PCI_PADS_ENABLE | PCI_CLK_PAD_ENABLE,
+ .driving_mask = GENMASK(23, 22),
},
{
.name = "lpcgrp",
@@ -1671,6 +1680,7 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
/* Conflict with all flash usage */
.value = IDE_PADS_ENABLE | NAND_PADS_DISABLE |
PFLASH_PADS_DISABLE | SFLASH_PADS_DISABLE,
+ .driving_mask = GENMASK(21, 20),
},
{
.name = "satagrp",
@@ -1686,6 +1696,7 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
.name = "gmii_gmac0_grp",
.pins = gmii_gmac0_3516_pins,
.num_pins = ARRAY_SIZE(gmii_gmac0_3516_pins),
+ .driving_mask = GENMASK(17, 16),
},
{
.name = "gmii_gmac1_grp",
@@ -1693,6 +1704,7 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
.num_pins = ARRAY_SIZE(gmii_gmac1_3516_pins),
/* Bring out RGMII on the GMAC1 pins */
.value = GEMINI_GMAC_IOSEL_GMAC0_GMAC1_RGMII,
+ .driving_mask = GENMASK(19, 18),
},
{
.name = "pcigrp",
@@ -1700,6 +1712,7 @@ static const struct gemini_pin_group gemini_3516_pin_groups[] = {
.num_pins = ARRAY_SIZE(pci_3516_pins),
/* Conflict only with GPIO2 */
.value = PCI_PADS_ENABLE | PCI_CLK_PAD_ENABLE,
+ .driving_mask = GENMASK(23, 22),
},
{
.name = "lpcgrp",
@@ -2393,9 +2406,77 @@ static int gemini_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
return ret;
}
+static int gemini_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned selector,
+ unsigned long *configs,
+ unsigned num_configs)
+{
+ struct gemini_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct gemini_pin_group *grp = NULL;
+ enum pin_config_param param;
+ u32 arg;
+ u32 val;
+ int i;
+
+ if (pmx->is_3512)
+ grp = &gemini_3512_pin_groups[selector];
+ if (pmx->is_3516)
+ grp = &gemini_3516_pin_groups[selector];
+
+ /* First figure out if this group supports configs */
+ if (!grp->driving_mask) {
+ dev_err(pmx->dev, "pin config group \"%s\" does "
+ "not support drive strength setting\n",
+ grp->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ switch (arg) {
+ case 4:
+ val = 0;
+ break;
+ case 8:
+ val = 1;
+ break;
+ case 12:
+ val = 2;
+ break;
+ case 16:
+ val = 3;
+ break;
+ default:
+ dev_err(pmx->dev,
+ "invalid drive strength %d mA\n",
+ arg);
+ return -ENOTSUPP;
+ }
+ val <<= (ffs(grp->driving_mask) - 1);
+ regmap_update_bits(pmx->map, GLOBAL_IODRIVE,
+ grp->driving_mask,
+ val);
+ dev_info(pmx->dev,
+ "set group %s to %d mA drive strength mask %08x val %08x\n",
+ grp->name, arg, grp->driving_mask, val);
+ break;
+ default:
+ dev_err(pmx->dev, "invalid config param %04x\n", param);
+ return -ENOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
static const struct pinconf_ops gemini_pinconf_ops = {
.pin_config_get = gemini_pinconf_get,
.pin_config_set = gemini_pinconf_set,
+ .pin_config_group_set = gemini_pinconf_group_set,
.is_generic = true,
};
--
2.14.3
^ permalink raw reply related
* [PATCH v4 0/4] fix the clock setting for SAR ADC
From: Jonathan Cameron @ 2017-12-02 11:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <15745fd0-1af9-c7c0-8769-063f218a3d28@amlogic.com>
On Tue, 28 Nov 2017 21:11:29 +0800
Yixun Lan <yixun.lan@amlogic.com> wrote:
> On 11/11/17 08:37, Jonathan Cameron wrote:
> > On Tue, 7 Nov 2017 22:36:00 +0100
> > Martin Blumenstingl <martin.blumenstingl@googlemail.com> wrote:
> >
> >> Hi Yixun,
> >>
> >> On Tue, Nov 7, 2017 at 3:09 PM, Yixun Lan <yixun.lan@amlogic.com> wrote:
> >>> patch [1/4]:
> >>> Fix wrong SARADC/SANA clock gate bit in Meson-GXBB/GXL,
> >>> the published datasheets[4] also has wrong description about this.
> >>> This patch should be explicitly merged *before* other patches.
> >>>
> >>> patch [2-4/4]:
> >>> Drop the "sana" clock from SAR ADC module,
> >> I agree with Jerome that patch 2/4 should be applied last.
> >
> > Let me know when I should take this.
> >
> > Thanks,
> >
> > Jonathan
> >
> >> when I wrote the driver I couldn't get it to work on my GXBB board
> >> (which unfortunately has died since then) because the clocks were
> >> disabled (they weren't enabled by the bootloader). people who are
> >> using an old .dtb would get the same problem again until the clock
> >> driver is merged
> >>
> >>> From the hardware perspective, the SAR ADC module doesn't
> >>> require "sana" clock to wrok. This should apply to all SoC,
> >>> including meson6,8, GXBB, GXL..
> >> thank you for clarifying this!
> >>
> >>> Note: the whole patchset series has been tested at GXL-P212 board,
> >>> we haven't got any meson6,8 board to test, so I would appreciate
> >>> if someone (Martin?) could help to confirm it works there.
> >> I can test this on a Meson8b and a Meson8m2 board on the weekend -
> >> I'll let you know about the results
> >>
> >>
> >> Regards
> >> Martin
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> >> the body of a message to majordomo at vger.kernel.org
> >> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
> >
> > _______________________________________________
> > linux-amlogic mailing list
> > linux-amlogic at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-amlogic
> >
> > .
> >
>
> Hi Jonathan Cameron & Kevin Hilman:
>
> since the clk part patch has been applied[1], can we proceed with others?
>
> to Jonathan:
> I think the iio part(patch [2/4]) should be merged via your iio tree?
> see [2]
I've applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it. It also makes sense for me to
take the dts binding update as it goes with the driver change.
Hence I've just left patch 4 for Kevin. Doesn't matter if this crosses
with Kevin also taking patch 3 though as it'll get sorted out automatically
by git later anyway.
Thanks
Jonathan
>
> to Kevin:
> could you take the DTS part? see [3], and the patch [4/4] has a rework
> version, see [4], to separate dt64 vs dt32 device tree.
>
>
> [1]
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005472.html
>
> [2]
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005264.html
>
> [3]
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005265.html
>
> [4]
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005304.html
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005305.html
> http://lists.infradead.org/pipermail/linux-amlogic/2017-November/005306.html
>
> Yixun
>
^ permalink raw reply
* [PATCH] pinctrl: stm32: Fix copyright
From: Linus Walleij @ 2017-12-02 12:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171129142532.23468-1-benjamin.gaignard@st.com>
On Wed, Nov 29, 2017 at 3:25 PM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Uniformize STMicroelectronics copyrights header
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
Patch applied.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] [media] vcodec: mediatek: Add MODULE_LICENSE to mtk_vcodec_util.c
From: Daniel Axtens @ 2017-12-02 12:53 UTC (permalink / raw)
To: linux-arm-kernel
This fixes the following warning in an allmodconfig build:
WARNING: modpost: missing MODULE_LICENSE() in drivers/media/platform/mtk-vcodec/mtk-vcodec-common.o
This matches the license at the top of the file.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
index 46768c056193..572efbbce7a9 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
@@ -115,3 +115,5 @@ struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev)
return ctx;
}
EXPORT_SYMBOL(mtk_vcodec_get_curr_ctx);
+
+MODULE_LICENSE("GPL v2");
--
2.11.0
^ permalink raw reply related
* [PATCH 0/5] crypto: arm64 - disable NEON across scatterwalk API calls
From: Peter Zijlstra @ 2017-12-02 13:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu-dG0Zc35NZZ85U36K2TXCpN+wJTYOyjkhxRjXbdiJTAQ@mail.gmail.com>
On Sat, Dec 02, 2017 at 09:11:46AM +0000, Ard Biesheuvel wrote:
> On 2 December 2017 at 09:01, Peter Zijlstra <peterz@infradead.org> wrote:
> > On Fri, Dec 01, 2017 at 09:19:22PM +0000, Ard Biesheuvel wrote:
> >> Note that the remaining crypto drivers simply operate on fixed buffers, so
> >> while the RT crowd may still feel the need to disable those (and the ones
> >> below as well, perhaps), they don't call back into the crypto layer like
> >> the ones updated by this series, and so there's no room for improvement
> >> there AFAICT.
> >
> > Do these other drivers process all the blocks fed to them in one go
> > under a single NEON section, or do they do a single fixed block per
> > NEON invocation?
>
> They consume the entire input in a single go, yes. But making it more
> granular than that is going to hurt performance, unless we introduce
> some kind of kernel_neon_yield(), which does a end+begin but only if
> the task is being scheduled out.
A little something like this:
https://lkml.kernel.org/r/20171201113235.6tmkwtov5cg2locv at hirez.programming.kicks-ass.net
> For example, the SHA256 keeps 256 bytes of round constants in NEON
> registers, and reloading those from memory for each 64 byte block of
> input is going to be noticeable. The same applies to the AES code
> (although the numbers are slightly different)
Quite. We could augment the above function with a return value that says
if we actually did a end/begin and registers were clobbered.
^ permalink raw reply
* [PATCH 0/5] crypto: arm64 - disable NEON across scatterwalk API calls
From: Peter Zijlstra @ 2017-12-02 13:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_DMVYX8y-8JJwyxy2rF0+6ofSWyjwpvwuF042ievBb7w@mail.gmail.com>
On Sat, Dec 02, 2017 at 11:15:14AM +0000, Ard Biesheuvel wrote:
> On 2 December 2017 at 09:11, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> > They consume the entire input in a single go, yes. But making it more
> > granular than that is going to hurt performance, unless we introduce
> > some kind of kernel_neon_yield(), which does a end+begin but only if
> > the task is being scheduled out.
> >
> > For example, the SHA256 keeps 256 bytes of round constants in NEON
> > registers, and reloading those from memory for each 64 byte block of
> > input is going to be noticeable. The same applies to the AES code
> > (although the numbers are slightly different)
>
> Something like below should do the trick I think (apologies for the
> patch soup). I.e., check TIF_NEED_RESCHED at a point where only very
> few NEON registers are live, and preserve/restore the live registers
> across calls to kernel_neon_end + kernel_neon_begin. Would that work
> for RT?
Probably yes. The important point is that preempt latencies (and thus by
extension NEON regions) are bounded and preferably small.
Unbounded stuff (like depends on the amount of data fed) are a complete
no-no for RT since then you cannot make predictions on how long things
will take.
^ permalink raw reply
* [PATCH v6 04/13] IIO: inkern: API for manipulating channel attributes
From: Jonathan Cameron @ 2017-12-02 14:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-5-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:11 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Extend the inkern API with functions for reading and writing
> attribute of iio channels.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V5 to V6 update:
> Replace include type.h with iio.h to fix build warning
Except don't do that. Consumers should not be able to see the internals
of iio devices.. Hence instead, move the required enum to types.h please.
One other passing comment inline (not one I'm expecting you to do anything
about!). Otherwise this is fine with me.
Thanks,
Jonathan
>
> drivers/iio/inkern.c | 18 +++++++++++++-----
> include/linux/iio/consumer.h | 28 +++++++++++++++++++++++++++-
> 2 files changed, 40 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
> index 069defc..f2e7824 100644
> --- a/drivers/iio/inkern.c
> +++ b/drivers/iio/inkern.c
> @@ -664,9 +664,8 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
> }
> EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
>
> -static int iio_read_channel_attribute(struct iio_channel *chan,
> - int *val, int *val2,
> - enum iio_chan_info_enum attribute)
> +int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
> + enum iio_chan_info_enum attribute)
> {
> int ret;
>
> @@ -682,6 +681,8 @@ static int iio_read_channel_attribute(struct iio_channel *chan,
>
> return ret;
> }
> +EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
> +
>
> int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
> {
> @@ -850,7 +851,8 @@ static int iio_channel_write(struct iio_channel *chan, int val, int val2,
> chan->channel, val, val2, info);
> }
>
> -int iio_write_channel_raw(struct iio_channel *chan, int val)
> +int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
> + enum iio_chan_info_enum attribute)
> {
> int ret;
>
> @@ -860,12 +862,18 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
> goto err_unlock;
> }
>
> - ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW);
> + ret = iio_channel_write(chan, val, val2, attribute);
> err_unlock:
> mutex_unlock(&chan->indio_dev->info_exist_lock);
>
> return ret;
> }
> +EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
> +
> +int iio_write_channel_raw(struct iio_channel *chan, int val)
> +{
> + return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW);
> +}
> EXPORT_SYMBOL_GPL(iio_write_channel_raw);
>
> unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index 5e347a9..70658b1 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -11,7 +11,7 @@
> #define _IIO_INKERN_CONSUMER_H_
>
> #include <linux/types.h>
> -#include <linux/iio/types.h>
> +#include <linux/iio/iio.h>
Hmm. Not keen on this include as it just exposed the internals
to IIO consumers which we really don't want to do.
This is to allow access to the enum iio_chan_info_enum?
Move it to types.h please.
>
> struct iio_dev;
> struct iio_chan_spec;
> @@ -216,6 +216,32 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val);
> int iio_read_channel_processed(struct iio_channel *chan, int *val);
>
> /**
> + * iio_write_channel_attribute() - Write values to the device attribute.
> + * @chan: The channel being queried.
> + * @val: Value being written.
> + * @val2: Value being written.val2 use depends on attribute type.
> + * @attribute: info attribute to be read.
> + *
> + * Returns an error code or 0.
> + */
> +int iio_write_channel_attribute(struct iio_channel *chan, int val,
> + int val2, enum iio_chan_info_enum attribute);
> +
> +/**
> + * iio_read_channel_attribute() - Read values from the device attribute.
> + * @chan: The channel being queried.
> + * @val: Value being written.
> + * @val2: Value being written.Val2 use depends on attribute type.
This pretty much highlights that we are still missing a function to allow us
to query what form write data should be presented in...
Here we have tightly coupled hardware so we know the answer, but in general
we don't.
Still can be a follow up patch when someone needs it.
> + * @attribute: info attribute to be written.
> + *
> + * Returns an error code if failed. Else returns a description of what is in val
> + * and val2, such as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val
> + * + val2/1e6
> + */
> +int iio_read_channel_attribute(struct iio_channel *chan, int *val,
> + int *val2, enum iio_chan_info_enum attribute);
> +
> +/**
> * iio_write_channel_raw() - write to a given channel
> * @chan: The channel being queried.
> * @val: Value being written.
^ permalink raw reply
* [PATCH v6 01/13] iio: Add hardware consumer buffer support
From: Jonathan Cameron @ 2017-12-02 14:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-2-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:08 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> From: Lars-Peter Clausen <lars@metafoo.de>
>
> Hardware consumer interface can be used when one IIO device has
> a direct connection to another device in hardware.
Given I'm not totally sure who will do the immutable branch I'm going
to starting adding tags to make it clear what I'm happy with.
(might rip them off again if I end up handling the final merge)
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> drivers/iio/buffer/Kconfig | 10 ++
> drivers/iio/buffer/Makefile | 1 +
> drivers/iio/buffer/industrialio-hw-consumer.c | 182 ++++++++++++++++++++++++++
> include/linux/iio/hw-consumer.h | 20 +++
> 4 files changed, 213 insertions(+)
> create mode 100644 drivers/iio/buffer/industrialio-hw-consumer.c
> create mode 100644 include/linux/iio/hw-consumer.h
>
> diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig
> index 4ffd3db..338774c 100644
> --- a/drivers/iio/buffer/Kconfig
> +++ b/drivers/iio/buffer/Kconfig
> @@ -29,6 +29,16 @@ config IIO_BUFFER_DMAENGINE
>
> Should be selected by drivers that want to use this functionality.
>
> +config IIO_BUFFER_HW_CONSUMER
> + tristate "Industrial I/O HW buffering"
> + help
> + Provides a way to bonding when an IIO device has a direct connection
> + to another device in hardware. In this case buffers for data transfers
> + are handled by hardware.
> +
> + Should be selected by drivers that want to use the generic Hw consumer
> + interface.
> +
> config IIO_KFIFO_BUF
> tristate "Industrial I/O buffering based on kfifo"
> help
> diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile
> index 85beaae..324a36b 100644
> --- a/drivers/iio/buffer/Makefile
> +++ b/drivers/iio/buffer/Makefile
> @@ -6,5 +6,6 @@
> obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
> obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o
> obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o
> +obj-$(CONFIG_IIO_BUFFER_HW_CONSUMER) += industrialio-hw-consumer.o
> obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
> obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
> diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c
> new file mode 100644
> index 0000000..0253be1
> --- /dev/null
> +++ b/drivers/iio/buffer/industrialio-hw-consumer.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright 2017 Analog Devices Inc.
> + * Author: Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/hw-consumer.h>
> +#include <linux/iio/buffer_impl.h>
> +
> +/**
> + * struct iio_hw_consumer - IIO hw consumer block
> + * @buffers: hardware buffers list head.
> + * @channels: IIO provider channels.
> + */
> +struct iio_hw_consumer {
> + struct list_head buffers;
> + struct iio_channel *channels;
> +};
> +
> +struct hw_consumer_buffer {
> + struct list_head head;
> + struct iio_dev *indio_dev;
> + struct iio_buffer buffer;
> + long scan_mask[];
> +};
> +
> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
> + struct iio_buffer *buffer)
> +{
> + return container_of(buffer, struct hw_consumer_buffer, buffer);
> +}
> +
> +static void iio_hw_buf_release(struct iio_buffer *buffer)
> +{
> + struct hw_consumer_buffer *hw_buf =
> + iio_buffer_to_hw_consumer_buffer(buffer);
> + kfree(hw_buf);
> +}
> +
> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
> + .release = &iio_hw_buf_release,
> + .modes = INDIO_BUFFER_HARDWARE,
> +};
> +
> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
> + struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
> +{
> + size_t mask_size = BITS_TO_LONGS(indio_dev->masklength) * sizeof(long);
> + struct hw_consumer_buffer *buf;
> +
> + list_for_each_entry(buf, &hwc->buffers, head) {
> + if (buf->indio_dev == indio_dev)
> + return buf;
> + }
> +
> + buf = kzalloc(sizeof(*buf) + mask_size, GFP_KERNEL);
> + if (!buf)
> + return NULL;
> +
> + buf->buffer.access = &iio_hw_buf_access;
> + buf->indio_dev = indio_dev;
> + buf->buffer.scan_mask = buf->scan_mask;
> +
> + iio_buffer_init(&buf->buffer);
> + list_add_tail(&buf->head, &hwc->buffers);
> +
> + return buf;
> +}
> +
> +/**
> + * iio_hw_consumer_alloc() - Allocate IIO hardware consumer
> + * @dev: Pointer to consumer device.
> + *
> + * Returns a valid iio_hw_consumer on success or a ERR_PTR() on failure.
> + */
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
> +{
> + struct hw_consumer_buffer *buf;
> + struct iio_hw_consumer *hwc;
> + struct iio_channel *chan;
> + int ret;
> +
> + hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
> + if (!hwc)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&hwc->buffers);
> +
> + hwc->channels = iio_channel_get_all(dev);
> + if (IS_ERR(hwc->channels)) {
> + ret = PTR_ERR(hwc->channels);
> + goto err_free_hwc;
> + }
> +
> + chan = &hwc->channels[0];
> + while (chan->indio_dev) {
> + buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
> + if (!buf) {
> + ret = -ENOMEM;
> + goto err_put_buffers;
> + }
> + set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
> + chan++;
> + }
> +
> + return hwc;
> +
> +err_put_buffers:
> + list_for_each_entry(buf, &hwc->buffers, head)
> + iio_buffer_put(&buf->buffer);
> + iio_channel_release_all(hwc->channels);
> +err_free_hwc:
> + kfree(hwc);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
> +
> +/**
> + * iio_hw_consumer_free() - Free IIO hardware consumer
> + * @hwc: hw consumer to free.
> + */
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> +{
> + struct hw_consumer_buffer *buf, *n;
> +
> + iio_channel_release_all(hwc->channels);
> + list_for_each_entry_safe(buf, n, &hwc->buffers, head)
> + iio_buffer_put(&buf->buffer);
> + kfree(hwc);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
> +
> +/**
> + * iio_hw_consumer_enable() - Enable IIO hardware consumer
> + * @hwc: iio_hw_consumer to enable.
> + *
> + * Returns 0 on success.
> + */
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
> +{
> + struct hw_consumer_buffer *buf;
> + int ret;
> +
> + list_for_each_entry(buf, &hwc->buffers, head) {
> + ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
> + if (ret)
> + goto err_disable_buffers;
> + }
> +
> + return 0;
> +
> +err_disable_buffers:
> + list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
> + iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
> +
> +/**
> + * iio_hw_consumer_disable() - Disable IIO hardware consumer
> + * @hwc: iio_hw_consumer to disable.
> + */
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
> +{
> + struct hw_consumer_buffer *buf;
> +
> + list_for_each_entry(buf, &hwc->buffers, head)
> + iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Hardware consumer buffer the IIO framework");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/hw-consumer.h b/include/linux/iio/hw-consumer.h
> new file mode 100644
> index 0000000..f16791b
> --- /dev/null
> +++ b/include/linux/iio/hw-consumer.h
> @@ -0,0 +1,20 @@
> +/*
> + * Industrial I/O in kernel hardware consumer interface
> + *
> + * Copyright 2017 Analog Devices Inc.
> + * Author: Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#ifndef LINUX_IIO_HW_CONSUMER_H
> +#define LINUX_IIO_HW_CONSUMER_H
> +
> +struct iio_hw_consumer;
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
> +
> +#endif
^ permalink raw reply
* [PATCH v6 02/13] docs: driver-api: add iio hw consumer section
From: Jonathan Cameron @ 2017-12-02 14:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-3-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:09 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> This adds a section about the Hardware consumer
> API of the IIO subsystem to the driver API
> documentation.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> Documentation/driver-api/iio/hw-consumer.rst | 51 ++++++++++++++++++++++++++++
> Documentation/driver-api/iio/index.rst | 1 +
> 2 files changed, 52 insertions(+)
> create mode 100644 Documentation/driver-api/iio/hw-consumer.rst
>
> diff --git a/Documentation/driver-api/iio/hw-consumer.rst b/Documentation/driver-api/iio/hw-consumer.rst
> new file mode 100644
> index 0000000..8facce6
> --- /dev/null
> +++ b/Documentation/driver-api/iio/hw-consumer.rst
> @@ -0,0 +1,51 @@
> +===========
> +HW consumer
> +===========
> +An IIO device can be directly connected to another device in hardware. in this
> +case the buffers between IIO provider and IIO consumer are handled by hardware.
> +The Industrial I/O HW consumer offers a way to bond these IIO devices without
> +software buffer for data. The implementation can be found under
> +:file:`drivers/iio/buffer/hw-consumer.c`
> +
> +
> +* struct :c:type:`iio_hw_consumer` ? Hardware consumer structure
> +* :c:func:`iio_hw_consumer_alloc` ? Allocate IIO hardware consumer
> +* :c:func:`iio_hw_consumer_free` ? Free IIO hardware consumer
> +* :c:func:`iio_hw_consumer_enable` ? Enable IIO hardware consumer
> +* :c:func:`iio_hw_consumer_disable` ? Disable IIO hardware consumer
> +
> +
> +HW consumer setup
> +=================
> +
> +As standard IIO device the implementation is based on IIO provider/consumer.
> +A typical IIO HW consumer setup looks like this::
> +
> + static struct iio_hw_consumer *hwc;
> +
> + static const struct iio_info adc_info = {
> + .read_raw = adc_read_raw,
> + };
> +
> + static int adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> + {
> + ret = iio_hw_consumer_enable(hwc);
> +
> + /* Acquire data */
> +
> + ret = iio_hw_consumer_disable(hwc);
> + }
> +
> + static int adc_probe(struct platform_device *pdev)
> + {
> + hwc = devm_iio_hw_consumer_alloc(&iio->dev);
> + }
> +
> +More details
> +============
> +.. kernel-doc:: include/linux/iio/hw-consumer.h
> +.. kernel-doc:: drivers/iio/buffer/industrialio-hw-consumer.c
> + :export:
> +
> diff --git a/Documentation/driver-api/iio/index.rst b/Documentation/driver-api/iio/index.rst
> index e5c3922..7fba341 100644
> --- a/Documentation/driver-api/iio/index.rst
> +++ b/Documentation/driver-api/iio/index.rst
> @@ -15,3 +15,4 @@ Contents:
> buffers
> triggers
> triggered-buffers
> + hw-consumer
^ permalink raw reply
* [PATCH v6 03/13] IIO: hw_consumer: add devm_iio_hw_consumer_alloc
From: Jonathan Cameron @ 2017-12-02 14:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-4-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:10 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add devm_iio_hw_consumer_alloc function that calls iio_hw_consumer_free
> when the device is unbound from the bus.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> drivers/iio/buffer/industrialio-hw-consumer.c | 66 +++++++++++++++++++++++++++
> include/linux/iio/hw-consumer.h | 2 +
> 2 files changed, 68 insertions(+)
>
> diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c
> index 0253be1..e980a79 100644
> --- a/drivers/iio/buffer/industrialio-hw-consumer.c
> +++ b/drivers/iio/buffer/industrialio-hw-consumer.c
> @@ -138,6 +138,72 @@ void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> }
> EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
>
> +static void devm_iio_hw_consumer_release(struct device *dev, void *res)
> +{
> + iio_hw_consumer_free(*(struct iio_hw_consumer **)res);
> +}
> +
> +static int devm_iio_hw_consumer_match(struct device *dev, void *res, void *data)
> +{
> + struct iio_hw_consumer **r = res;
> +
> + if (!r || !*r) {
> + WARN_ON(!r || !*r);
> + return 0;
> + }
> + return *r == data;
> +}
> +
> +/**
> + * devm_iio_hw_consumer_alloc - Resource-managed iio_hw_consumer_alloc()
> + * @dev: Pointer to consumer device.
> + *
> + * Managed iio_hw_consumer_alloc. iio_hw_consumer allocated with this function
> + * is automatically freed on driver detach.
> + *
> + * If an iio_hw_consumer allocated with this function needs to be freed
> + * separately, devm_iio_hw_consumer_free() must be used.
> + *
> + * returns pointer to allocated iio_hw_consumer on success, NULL on failure.
> + */
> +struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev)
> +{
> + struct iio_hw_consumer **ptr, *iio_hwc;
> +
> + ptr = devres_alloc(devm_iio_hw_consumer_release, sizeof(*ptr),
> + GFP_KERNEL);
> + if (!ptr)
> + return NULL;
> +
> + iio_hwc = iio_hw_consumer_alloc(dev);
> + if (IS_ERR(iio_hwc)) {
> + devres_free(ptr);
> + } else {
> + *ptr = iio_hwc;
> + devres_add(dev, ptr);
> + }
> +
> + return iio_hwc;
> +}
> +EXPORT_SYMBOL_GPL(devm_iio_hw_consumer_alloc);
> +
> +/**
> + * devm_iio_hw_consumer_free - Resource-managed iio_hw_consumer_free()
> + * @dev: Pointer to consumer device.
> + * @hwc: iio_hw_consumer to free.
> + *
> + * Free iio_hw_consumer allocated with devm_iio_hw_consumer_alloc().
> + */
> +void devm_iio_hw_consumer_free(struct device *dev, struct iio_hw_consumer *hwc)
> +{
> + int rc;
> +
> + rc = devres_release(dev, devm_iio_hw_consumer_release,
> + devm_iio_hw_consumer_match, hwc);
> + WARN_ON(rc);
> +}
> +EXPORT_SYMBOL_GPL(devm_iio_hw_consumer_free);
> +
> /**
> * iio_hw_consumer_enable() - Enable IIO hardware consumer
> * @hwc: iio_hw_consumer to enable.
> diff --git a/include/linux/iio/hw-consumer.h b/include/linux/iio/hw-consumer.h
> index f16791b..90ecfce 100644
> --- a/include/linux/iio/hw-consumer.h
> +++ b/include/linux/iio/hw-consumer.h
> @@ -14,6 +14,8 @@ struct iio_hw_consumer;
>
> struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev);
> +void devm_iio_hw_consumer_free(struct device *dev, struct iio_hw_consumer *hwc);
> int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
>
^ permalink raw reply
* [PATCH v6 05/13] IIO: Add DT bindings for sigma delta adc modulator
From: Jonathan Cameron @ 2017-12-02 14:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-6-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:12 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add documentation of device tree bindings to support
> sigma delta modulator in IIO framework.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> new file mode 100644
> index 0000000..e9ebb8a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for sigma delta modulator
> +
> +Required properties:
> +- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
> + as a generic SD modulator if modulator not specified in compatible list.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> + ads1202: adc at 0 {
> + compatible = "sd-modulator";
> + #io-channel-cells = <1>;
> + };
^ permalink raw reply
* [PATCH v6 06/13] IIO: ADC: add sigma delta modulator support
From: Jonathan Cameron @ 2017-12-02 14:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-7-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:13 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add generic driver to support sigma delta modulators.
> Typically, this device is hardware connected to
> an IIO device in charge of the conversion. Devices are
> bonded through the hardware consumer API.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
I love this little one ;) Smallest IIO driver yet!
Doesn't technically 'do' anything but that's not the point.
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> drivers/iio/adc/Kconfig | 12 ++++++
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/sd_adc_modulator.c | 81 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 94 insertions(+)
> create mode 100644 drivers/iio/adc/sd_adc_modulator.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 5762565..c5db62f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -626,6 +626,18 @@ config SPEAR_ADC
> To compile this driver as a module, choose M here: the
> module will be called spear_adc.
>
> +config SD_ADC_MODULATOR
> + tristate "Generic sigma delta modulator"
> + depends on OF
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Select this option to enables sigma delta modulator. This driver can
> + support generic sigma delta modulators.
> +
> + This driver can also be built as a module. If so, the module
> + will be called sd_adc_modulator.
> +
> config STM32_ADC_CORE
> tristate "STMicroelectronics STM32 adc core"
> depends on ARCH_STM32 || COMPILE_TEST
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9874e05..d800325 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -81,3 +81,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o
> obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
> xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
> obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
> +obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
> diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
> new file mode 100644
> index 0000000..08bd7b6
> --- /dev/null
> +++ b/drivers/iio/adc/sd_adc_modulator.c
> @@ -0,0 +1,81 @@
> +/*
> + * Generic sigma delta modulator driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@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/iio/iio.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +
> +static const struct iio_info iio_sd_mod_iio_info;
> +
> +static const struct iio_chan_spec iio_sd_mod_ch = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 1,
> + .shift = 0,
> + },
> +};
> +
> +static int iio_sd_mod_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct iio_dev *iio;
> +
> + iio = devm_iio_device_alloc(dev, 0);
> + if (!iio)
> + return -ENOMEM;
> +
> + iio->dev.parent = dev;
> + iio->dev.of_node = dev->of_node;
> + iio->name = dev_name(dev);
> + iio->info = &iio_sd_mod_iio_info;
> + iio->modes = INDIO_BUFFER_HARDWARE;
> +
> + iio->num_channels = 1;
> + iio->channels = &iio_sd_mod_ch;
> +
> + platform_set_drvdata(pdev, iio);
> +
> + return devm_iio_device_register(&pdev->dev, iio);
> +}
> +
> +static const struct of_device_id sd_adc_of_match[] = {
> + { .compatible = "sd-modulator" },
> + { .compatible = "ads1201" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, sd_adc_of_match);
> +
> +static struct platform_driver iio_sd_mod_adc = {
> + .driver = {
> + .name = "iio_sd_adc_mod",
> + .of_match_table = of_match_ptr(sd_adc_of_match),
> + },
> + .probe = iio_sd_mod_probe,
> +};
> +
> +module_platform_driver(iio_sd_mod_adc);
> +
> +MODULE_DESCRIPTION("Basic sigma delta modulator");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
^ permalink raw reply
* [PATCH v6 07/13] IIO: add DT bindings for stm32 DFSDM filter
From: Jonathan Cameron @ 2017-12-02 14:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-8-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:14 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add bindings that describes STM32 Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> .../bindings/iio/adc/st,stm32-dfsdm-adc.txt | 127 +++++++++++++++++++++
> 1 file changed, 127 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> new file mode 100644
> index 0000000..4a42d76
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,127 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicated to
> +interface external sigma delta modulators to STM32 micro controllers.
> +It is mainly targeted for:
> +- Sigma delta modulators (motor control, metering...)
> +- PDM microphones (audio digital microphone)
> +
> +It features up to 8 serial digital interfaces (SPI or Manchester) and
> +up to 4 filters on stm32h7.
> +
> +Each child node match with a filter instance.
> +
> +Contents of a STM32 DFSDM root node:
> +------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32h7-dfsdm".
> +- reg: Offset and length of the DFSDM block register set.
> +- clocks: IP and serial interfaces clocking. Should be set according
> + to rcc clock ID and "clock-names".
> +- clock-names: Input clock name "dfsdm" must be defined,
> + "audio" is optional. If defined CLKOUT is based on the audio
> + clock, else "dfsdm" is used.
> +- #interrupt-cells = <1>;
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +
> +Optional properties:
> +- spi-max-frequency: Requested only for SPI master mode.
> + SPI clock OUT frequency (Hz). This clock must be set according
> + to "clock" property. Frequency must be a multiple of the rcc
> + clock frequency. If not, SPI CLKOUT frequency will not be
> + accurate.
> +
> +Contents of a STM32 DFSDM child nodes:
> +--------------------------------------
> +
> +Required properties:
> +- compatible: Must be:
> + "st,stm32-dfsdm-adc" for sigma delta ADCs
> + "st,stm32-dfsdm-dmic" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance used.
> +- interrupts: IRQ lines connected to each DFSDM filter instance.
> +- st,adc-channels: List of single-ended channels muxed for this ADC.
> + valid values:
> + "st,stm32h7-dfsdm" compatibility: 0 to 7.
> +- st,adc-channel-names: List of single-ended channel names.
> +- st,filter-order: SinC filter order from 0 to 5.
> + 0: FastSinC
> + [1-5]: order 1 to 5.
> + For audio purpose it is recommended to use order 3 to 5.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- io-channels: From common IIO binding. Used to pipe external sigma delta
> + modulator or internal ADC output to DFSDM channel.
> + This is not required for "st,stm32-dfsdm-pdm" compatibility as
> + PDM microphone is binded in Audio DT node.
> +
> +Required properties for "st,stm32-dfsdm-pdm" compatibility:
> +- #sound-dai-cells: Must be set to 0.
> +- dma: DMA controller phandle and DMA request line associated to the
> + filter instance (specified by the field "reg")
> +- dma-names: Must be "rx"
> +
> +Optional properties:
> +- st,adc-channel-types: Single-ended channel input type.
> + - "SPI_R": SPI with data on rising edge (default)
> + - "SPI_F": SPI with data on falling edge
> + - "MANCH_R": manchester codec, rising edge = logic 0
> + - "MANCH_F": manchester codec, falling edge = logic 1
> +- st,adc-channel-clk-src: Conversion clock source.
> + - "CLKIN": external SPI clock (CLKIN x)
> + - "CLKOUT": internal SPI clock (CLKOUT) (default)
> + - "CLKOUT_F": internal SPI clock divided by 2 (falling edge).
> + - "CLKOUT_R": internal SPI clock divided by 2 (rising edge).
> +
> +- st,adc-alt-channel: Must be defined if two sigma delta modulator are
> + connected on same SPI input.
> + If not set, channel n is connected to SPI input n.
> + If set, channel n is connected to SPI input n + 1.
> +
> +- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
> + Used for multi microphones synchronization.
> +
> +Example of a sigma delta adc connected on DFSDM SPI port 0
> +and a pdm microphone connected on DFSDM SPI port 1:
> +
> + ads1202: simple_sd_adc at 0 {
> + compatible = "ads1202";
> + #io-channel-cells = <1>;
> + };
> +
> + dfsdm: dfsdm at 40017000 {
> + compatible = "st,stm32h7-dfsdm";
> + reg = <0x40017000 0x400>;
> + clocks = <&rcc DFSDM1_CK>;
> + clock-names = "dfsdm";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dfsdm_adc0: dfsdm-adc at 0 {
> + compatible = "st,stm32-dfsdm-adc";
> + #io-channel-cells = <1>;
> + reg = <0>;
> + interrupts = <110>;
> + st,adc-channels = <0>;
> + st,adc-channel-names = "sd_adc0";
> + st,adc-channel-types = "SPI_F";
> + st,adc-channel-clk-src = "CLKOUT";
> + io-channels = <&ads1202 0>;
> + st,filter-order = <3>;
> + };
> + dfsdm_pdm1: dfsdm-pdm at 1 {
> + compatible = "st,stm32-dfsdm-dmic";
> + reg = <1>;
> + interrupts = <111>;
> + dmas = <&dmamux1 102 0x400 0x00>;
> + dma-names = "rx";
> + st,adc-channels = <1>;
> + st,adc-channel-names = "dmic1";
> + st,adc-channel-types = "SPI_R";
> + st,adc-channel-clk-src = "CLKOUT";
> + st,filter-order = <5>;
> + };
> + }
^ permalink raw reply
* [PATCH v6 09/13] IIO: ADC: add STM32 DFSDM sigma delta ADC support
From: Jonathan Cameron @ 2017-12-02 14:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-10-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:16 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add DFSDM driver to handle sigma delta ADC.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
A couple of really small nitpicks if you are respinning.
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> drivers/iio/adc/Kconfig | 13 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/stm32-dfsdm-adc.c | 741 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 755 insertions(+)
> create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index b729ae0..98ca30b 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -677,6 +677,19 @@ config STM32_DFSDM_CORE
> This driver can also be built as a module. If so, the module
> will be called stm32-dfsdm-core.
>
> +config STM32_DFSDM_ADC
> + tristate "STMicroelectronics STM32 dfsdm adc"
> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
> + select STM32_DFSDM_CORE
> + select REGMAP_MMIO
> + select IIO_BUFFER_HW_CONSUMER
> + help
> + Select this option to support ADCSigma delta modulator for
> + STMicroelectronics STM32 digital filter for sigma delta converter.
> +
> + This driver can also be built as a module. If so, the module
> + will be called stm32-dfsdm-adc.
> +
> config STX104
> tristate "Apex Embedded Systems STX104 driver"
> depends on PC104 && X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index b52d0a0..c4f5d15 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
> obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..f9419ab
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,741 @@
> +/*
> + * This file is the ADC part of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPL V2.0.
> + *
> + * 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/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/hw-consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +/* Conversion timeout */
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +/* Oversampling attribute default */
> +#define DFSDM_DEFAULT_OVERSAMPLING 100
> +
> +/* Oversampling max values */
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +/* Max sample resolutions */
> +#define DFSDM_MAX_RES BIT(31)
> +#define DFSDM_DATA_RES BIT(23)
> +
> +enum sd_converter_type {
> + DFSDM_AUDIO,
> + DFSDM_IIO,
> +};
> +
> +struct stm32_dfsdm_dev_data {
> + int type;
> + int (*init)(struct iio_dev *indio_dev);
> + unsigned int num_channels;
> + const struct regmap_config *regmap_cfg;
> +};
> +
> +struct stm32_dfsdm_adc {
> + struct stm32_dfsdm *dfsdm;
> + const struct stm32_dfsdm_dev_data *dev_data;
> + unsigned int fl_id;
> + unsigned int ch_id;
> +
> + /* ADC specific */
> + unsigned int oversamp;
> + struct iio_hw_consumer *hwc;
> + struct completion completion;
> + u32 *buffer;
> +
> +};
> +
> +struct stm32_dfsdm_str2field {
> + const char *name;
> + unsigned int val;
> +};
> +
> +/* DFSDM channel serial interface type */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
> + { "SPI_R", 0 }, /* SPI with data on rising edge */
> + { "SPI_F", 1 }, /* SPI with data on falling edge */
> + { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
> + { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
> + {},
> +};
> +
> +/* DFSDM channel clock source */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
> + /* External SPI clock (CLKIN x) */
> + { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
> + /* Internal SPI clock (CLKOUT) */
> + { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
> + /* Internal SPI clock divided by 2 (falling edge) */
> + { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
> + /* Internal SPI clock divided by 2 (falling edge) */
> + { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
> + {},
> +};
> +
> +static int stm32_dfsdm_str2val(const char *str,
> + const struct stm32_dfsdm_str2field *list)
> +{
> + const struct stm32_dfsdm_str2field *p = list;
> +
> + for (p = list; p && p->name; p++)
> + if (!strcmp(p->name, str))
> + return p->val;
> +
> + return -EINVAL;
> +}
> +
> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
> + unsigned int fast, unsigned int oversamp)
> +{
> + unsigned int i, d, fosr, iosr;
> + u64 res;
> + s64 delta;
> + unsigned int m = 1; /* multiplication factor */
> + unsigned int p = fl->ford; /* filter order (ford) */
> +
> + pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
> + /*
> + * This function tries to compute filter oversampling and integrator
> + * oversampling, base on oversampling ratio requested by user.
> + *
> + * Decimation d depends on the filter order and the oversampling ratios.
> + * ford: filter order
> + * fosr: filter over sampling ratio
> + * iosr: integrator over sampling ratio
> + */
> + if (fl->ford == DFSDM_FASTSINC_ORDER) {
> + m = 2;
> + p = 2;
> + }
> +
> + /*
> + * Look for filter and integrator oversampling ratios which allows
> + * to reach 24 bits data output resolution.
> + * Leave as soon as if exact resolution if reached.
> + * Otherwise the higher resolution below 32 bits is kept.
> + */
> + for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
> + for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
> + if (fast)
> + d = fosr * iosr;
> + else if (fl->ford == DFSDM_FASTSINC_ORDER)
> + d = fosr * (iosr + 3) + 2;
> + else
> + d = fosr * (iosr - 1 + p) + p;
> +
> + if (d > oversamp)
> + break;
> + else if (d != oversamp)
> + continue;
> + /*
> + * Check resolution (limited to signed 32 bits)
> + * res <= 2^31
> + * Sincx filters:
> + * res = m * fosr^p x iosr (with m=1, p=ford)
> + * FastSinc filter
> + * res = m * fosr^p x iosr (with m=2, p=2)
> + */
> + res = fosr;
> + for (i = p - 1; i > 0; i--) {
> + res = res * (u64)fosr;
> + if (res > DFSDM_MAX_RES)
> + break;
> + }
> + if (res > DFSDM_MAX_RES)
> + continue;
> + res = res * (u64)m * (u64)iosr;
> + if (res > DFSDM_MAX_RES)
> + continue;
> +
> + delta = res - DFSDM_DATA_RES;
> +
> + if (res >= fl->res) {
> + fl->res = res;
> + fl->fosr = fosr;
> + fl->iosr = iosr;
> + fl->fast = fast;
> + pr_debug("%s: fosr = %d, iosr = %d\n",
> + __func__, fl->fosr, fl->iosr);
> + }
> +
> + if (!delta)
> + return 0;
> + }
> + }
> +
> + if (!fl->fosr)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
> + unsigned int ch_id)
> +{
> + return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> + DFSDM_CHCFGR1_CHEN_MASK,
> + DFSDM_CHCFGR1_CHEN(1));
> +}
> +
> +static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm,
> + unsigned int ch_id)
> +{
> + regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> + DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0));
> +}
> +
> +static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> + struct stm32_dfsdm_channel *ch)
> +{
> + unsigned int id = ch->id;
> + struct regmap *regmap = dfsdm->regmap;
> + int ret;
> +
> + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> + DFSDM_CHCFGR1_SITP_MASK,
> + DFSDM_CHCFGR1_SITP(ch->type));
> + if (ret < 0)
> + return ret;
> + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> + DFSDM_CHCFGR1_SPICKSEL_MASK,
> + DFSDM_CHCFGR1_SPICKSEL(ch->src));
> + if (ret < 0)
> + return ret;
> + return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> + DFSDM_CHCFGR1_CHINSEL_MASK,
> + DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
> +}
> +
> +static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
> + unsigned int fl_id)
> +{
> + int ret;
> +
> + /* Enable filter */
> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
> + if (ret < 0)
> + return ret;
> +
> + /* Start conversion */
> + return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> + DFSDM_CR1_RSWSTART_MASK,
> + DFSDM_CR1_RSWSTART(1));
> +}
> +
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> + /* Disable conversion */
> + regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> +}
> +
> +static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
> + unsigned int fl_id, unsigned int ch_id)
> +{
> + struct regmap *regmap = dfsdm->regmap;
> + struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
> + int ret;
> +
> + /* Average integrator oversampling */
> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> + DFSDM_FCR_IOSR(fl->iosr - 1));
> + if (ret)
> + return ret;
> +
> + /* Filter order and Oversampling */
> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
> + DFSDM_FCR_FOSR(fl->fosr - 1));
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
> + DFSDM_FCR_FORD(fl->ford));
> + if (ret)
> + return ret;
> +
> + /* No scan mode supported for the moment */
> + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
> + DFSDM_CR1_RCH(ch_id));
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> + DFSDM_CR1_RSYNC_MASK,
> + DFSDM_CR1_RSYNC(fl->sync_mode));
> +}
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> + struct iio_dev *indio_dev,
> + struct iio_chan_spec *ch)
> +{
> + struct stm32_dfsdm_channel *df_ch;
> + const char *of_str;
> + int chan_idx = ch->scan_index;
> + int ret, val;
> +
> + ret = of_property_read_u32_index(indio_dev->dev.of_node,
> + "st,adc-channels", chan_idx,
> + &ch->channel);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev,
> + " Error parsing 'st,adc-channels' for idx %d\n",
> + chan_idx);
> + return ret;
> + }
> + if (ch->channel >= dfsdm->num_chs) {
> + dev_err(&indio_dev->dev,
> + " Error bad channel number %d (max = %d)\n",
> + ch->channel, dfsdm->num_chs);
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_string_index(indio_dev->dev.of_node,
> + "st,adc-channel-names", chan_idx,
> + &ch->datasheet_name);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev,
> + " Error parsing 'st,adc-channel-names' for idx %d\n",
> + chan_idx);
> + return ret;
> + }
> +
> + df_ch = &dfsdm->ch_list[ch->channel];
> + df_ch->id = ch->channel;
> +
> + ret = of_property_read_string_index(indio_dev->dev.of_node,
> + "st,adc-channel-types", chan_idx,
> + &of_str);
> + if (!ret) {
> + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
> + if (val < 0)
> + return val;
> + } else {
> + val = 0;
> + }
> + df_ch->type = val;
> +
> + ret = of_property_read_string_index(indio_dev->dev.of_node,
> + "st,adc-channel-clk-src", chan_idx,
> + &of_str);
> + if (!ret) {
> + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
> + if (val < 0)
> + return val;
> + } else {
> + val = 0;
> + }
> + df_ch->src = val;
> +
> + ret = of_property_read_u32_index(indio_dev->dev.of_node,
> + "st,adc-alt-channel", chan_idx,
> + &df_ch->alt_si);
> + if (ret < 0)
> + df_ch->alt_si = 0;
> +
> + return 0;
> +}
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, bool dma)
> +{
> + struct regmap *regmap = adc->dfsdm->regmap;
> + int ret;
> +
> + ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
> + if (ret < 0)
> + return ret;
> +
> + ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
> + adc->ch_id);
> + if (ret < 0)
> + goto stop_channels;
> +
> + ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> + if (ret < 0)
> + goto stop_channels;
> +
> + return 0;
> +
> +stop_channels:
> + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> + DFSDM_CR1_RDMAEN_MASK, 0);
> +
> + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> + DFSDM_CR1_RCONT_MASK, 0);
> + stm32_dfsdm_stop_channel(adc->dfsdm, adc->fl_id);
> +
> + return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
> +{
> + struct regmap *regmap = adc->dfsdm->regmap;
> +
> + stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +
> + /* Clean conversion options */
> + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> + DFSDM_CR1_RDMAEN_MASK, 0);
> +
> + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> + DFSDM_CR1_RCONT_MASK, 0);
> +
> + stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan, int *res)
> +{
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + long timeout;
> + int ret;
> +
> + reinit_completion(&adc->completion);
> +
> + adc->buffer = res;
> +
> + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
> + if (ret < 0)
> + goto stop_dfsdm;
> +
> + ret = stm32_dfsdm_start_conv(adc, false);
> + if (ret < 0) {
> + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> + goto stop_dfsdm;
> + }
> +
> + timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> + DFSDM_TIMEOUT);
> +
> + /* Mask IRQ for regular conversion achievement*/
> + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> + if (timeout == 0)
> + ret = -ETIMEDOUT;
> + else if (timeout < 0)
> + ret = timeout;
> + else
> + ret = IIO_VAL_INT;
> +
> + stm32_dfsdm_stop_conv(adc);
> +
> +stop_dfsdm:
> + stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> + return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> + int ret = -EINVAL;
> +
> + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
> + ret = stm32_dfsdm_set_osrs(fl, 0, val);
> + if (!ret)
> + adc->oversamp = val;
> + }
> +
> + return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = iio_hw_consumer_enable(adc->hwc);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev,
> + "%s: IIO enable failed (channel %d)\n",
> + __func__, chan->channel);
> + return ret;
> + }
> + ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> + iio_hw_consumer_disable(adc->hwc);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev,
> + "%s: Conversion failed (channel %d)\n",
> + __func__, chan->channel);
> + return ret;
> + }
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + *val = adc->oversamp;
> +
> + return IIO_VAL_INT;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_adc = {
> + .read_raw = stm32_dfsdm_read_raw,
> + .write_raw = stm32_dfsdm_write_raw,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> + struct stm32_dfsdm_adc *adc = arg;
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> + struct regmap *regmap = adc->dfsdm->regmap;
> + unsigned int status, int_en;
> +
> + regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
> + regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en);
> +
> + if (status & DFSDM_ISR_REOCF_MASK) {
> + /* Read the data register clean the IRQ status */
> + regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
> + complete(&adc->completion);
> + }
> +
> + if (status & DFSDM_ISR_ROVRF_MASK) {
> + if (int_en & DFSDM_CR2_ROVRIE_MASK)
> + dev_warn(&indio_dev->dev, "Overrun detected\n");
> + regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
> + DFSDM_ICR_CLRROVRF_MASK,
> + DFSDM_ICR_CLRROVRF_MASK);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> + struct iio_chan_spec *ch)
> +{
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + int ret;
> +
> + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
> + if (ret < 0)
> + return ret;
> +
> + ch->type = IIO_VOLTAGE;
> + ch->indexed = 1;
> +
> + /*
> + * IIO_CHAN_INFO_RAW: used to compute regular conversion
> + * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
> + */
> + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> + ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> +
> + ch->scan_type.sign = 'u';
> + ch->scan_type.realbits = 24;
> + ch->scan_type.storagebits = 32;
> + adc->ch_id = ch->channel;
> +
> + return stm32_dfsdm_chan_configure(adc->dfsdm,
> + &adc->dfsdm->ch_list[ch->channel]);
> +}
> +
> +static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
> +{
> + struct iio_chan_spec *ch;
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + int num_ch;
> + int ret, chan_idx;
> +
> + adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
> + ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0,
> + adc->oversamp);
> + if (ret < 0)
> + return ret;
> +
> + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
> + "st,adc-channels");
> + if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
> + dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
> + return num_ch < 0 ? num_ch : -EINVAL;
> + }
> +
> + /* Bind to SD modulator IIO device */
> + adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
> + if (IS_ERR(adc->hwc))
> + return -EPROBE_DEFER;
> +
> + ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
> + GFP_KERNEL);
> + if (!ch)
> + return -ENOMEM;
> +
> + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> + ch->scan_index = chan_idx;
> + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev, "Channels init failed\n");
> + return ret;
> + }
> + }
> +
> + indio_dev->num_channels = num_ch;
> + indio_dev->channels = ch;
> +
> + init_completion(&adc->completion);
> +
> + return 0;
> +}
> +
> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = {
> + .type = DFSDM_IIO,
> + .init = stm32_dfsdm_adc_init,
> +};
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> + { .compatible = "st,stm32-dfsdm-adc",
Slightly odd alignment, I'd drop .compatible onto a new line as well..
> + .data = &stm32h7_dfsdm_adc_data,
> + },
> + {}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_dfsdm_adc *adc;
> + struct device_node *np = dev->of_node;
> + const struct stm32_dfsdm_dev_data *dev_data;
> + struct iio_dev *iio;
> + const struct of_device_id *of_id;
> + char *name;
> + int ret, irq, val;
> +
> + of_id = of_match_node(stm32_dfsdm_adc_match, np);
> + if (!of_id->data) {
> + dev_err(&pdev->dev, "Data associated to device is missing\n");
> + return -EINVAL;
> + }
> +
> + dev_data = (const struct stm32_dfsdm_dev_data *)of_id->data;
> +
> + iio = devm_iio_device_alloc(dev, sizeof(*adc));
> + if (IS_ERR(iio)) {
> + dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> + return PTR_ERR(iio);
> + }
> +
> + adc = iio_priv(iio);
> + if (IS_ERR(adc)) {
> + dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> + return PTR_ERR(adc);
> + }
> + adc->dfsdm = dev_get_drvdata(dev->parent);
> +
> + iio->dev.parent = dev;
> + iio->dev.of_node = np;
> + iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> +
> + platform_set_drvdata(pdev, adc);
> +
> + ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
> + if (ret != 0) {
> + dev_err(dev, "Missing reg property\n");
> + return -EINVAL;
> + }
> +
> + name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL);
> + if (!name)
> + return -ENOMEM;
> + iio->info = &stm32_dfsdm_info_adc;
> + snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> + iio->name = name;
> +
> + /*
> + * In a first step IRQs generated for channels are not treated.
> + * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> + */
> + irq = platform_get_irq(pdev, 0);
> + ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> + 0, pdev->name, adc);
> + if (ret < 0) {
> + dev_err(dev, "Failed to request IRQ\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set filter order\n");
> + return ret;
> + }
> +
> + adc->dfsdm->fl_list[adc->fl_id].ford = val;
> +
> + ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> + if (!ret)
> + adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
> +
> + adc->dev_data = dev_data;
> + ret = dev_data->init(iio);
> + if (ret < 0)
> + return ret;
> +
> + return iio_device_register(iio);
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> + struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> + iio_device_unregister(indio_dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> + .driver = {
> + .name = "stm32-dfsdm-adc",
> + .of_match_table = stm32_dfsdm_adc_match,
> + },
> + .probe = stm32_dfsdm_adc_probe,
> + .remove = stm32_dfsdm_adc_remove,
> +
Real nitpick but no blank line here!
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
^ permalink raw reply
* [PATCH v6 13/13] ASoC: stm32: add DFSDM DAI support
From: Jonathan Cameron @ 2017-12-02 15:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512150020-20335-14-git-send-email-arnaud.pouliquen@st.com>
On Fri, 1 Dec 2017 18:40:20 +0100
Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
> Add driver to handle DAI interface for PDM microphones connected
> to Digital Filter for Sigma Delta Modulators IP.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
include tied up with the move of that enum in the earlier patch.
Other than that the IIO stuff looks fine to me.
Mark, given this set is moderately invasive on the IIO side and
should just drop in cleanly on the sound side of things, either
I could take it via IIO or one of us can do an immutable branch
and we take it through both trees.
Don't mind which but if I'm either taking the series or doing
an immutable branch I'll obviously be waiting on your review!
Also need time for Rob to check the updating bindings.
Anyhow, it's coming together reasonably nicely in the end.
Good work Arnaud!
Jonathan
> ---
> V5 to V6 update:
> fix build warning
>
> sound/soc/stm/Kconfig | 11 ++
> sound/soc/stm/Makefile | 3 +
> sound/soc/stm/stm32_adfsdm.c | 386 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 400 insertions(+)
> create mode 100644 sound/soc/stm/stm32_adfsdm.c
>
> diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
> index 3398e6c..a78f770 100644
> --- a/sound/soc/stm/Kconfig
> +++ b/sound/soc/stm/Kconfig
> @@ -28,4 +28,15 @@ config SND_SOC_STM32_SPDIFRX
> help
> Say Y if you want to enable S/PDIF capture for STM32
>
> +config SND_SOC_STM32_DFSDM
> + tristate "SoC Audio support for STM32 DFSDM"
> + depends on (ARCH_STM32 && OF && STM32_DFSDM_ADC) || COMPILE_TEST
> + depends on SND_SOC
> + select SND_SOC_GENERIC_DMAENGINE_PCM
> + select SND_SOC_DMIC
> + select IIO_BUFFER_CB
> + help
> + Select this option to enable the STM32 Digital Filter
> + for Sigma Delta Modulators (DFSDM) driver used
> + in various STM32 series for digital microphone capture.
> endmenu
> diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
> index 4ed22e6..53e90e6 100644
> --- a/sound/soc/stm/Makefile
> +++ b/sound/soc/stm/Makefile
> @@ -12,3 +12,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
> # SPDIFRX
> snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o
> obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o
> +
> +#DFSDM
> +obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
> diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
> new file mode 100644
> index 0000000..890ec24
> --- /dev/null
> +++ b/sound/soc/stm/stm32_adfsdm.c
> @@ -0,0 +1,386 @@
> +/*
> + * This file is part of STM32 DFSDM ASoC DAI driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> + * Olivier Moysan <olivier.moysan@st.com>
> + *
> + * License terms: GPL V2.0.
> + *
> + * 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/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
I missed this before but a consumer should not need to include iio.h.
I would guess it was about that enum that I want you to move to types.h?
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/adc/stm32-dfsdm-adc.h>
> +
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +
> +#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
> +
> +#define DFSDM_MAX_PERIOD_SIZE (PAGE_SIZE / 2)
> +#define DFSDM_MAX_PERIODS 6
> +
> +struct stm32_adfsdm_priv {
> + struct snd_soc_dai_driver dai_drv;
> + struct snd_pcm_substream *substream;
> + struct device *dev;
> +
> + /* IIO */
> + struct iio_channel *iio_ch;
> + struct iio_cb_buffer *iio_cb;
> + bool iio_active;
> +
> + /* PCM buffer */
> + unsigned char *pcm_buff;
> + unsigned int pos;
> + bool allocated;
> +};
> +
> +struct stm32_adfsdm_data {
> + unsigned int rate; /* SNDRV_PCM_RATE value */
> + unsigned int freq; /* frequency in Hz */
> +};
> +
> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
> + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
> + SNDRV_PCM_INFO_PAUSE,
> + .formats = SNDRV_PCM_FMTBIT_S32_LE,
> +
> + .rate_min = 8000,
> + .rate_max = 32000,
> +
> + .channels_min = 1,
> + .channels_max = 1,
> +
> + .periods_min = 2,
> + .periods_max = DFSDM_MAX_PERIODS,
> +
> + .period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
> + .buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
> +};
> +
> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> + if (priv->iio_active) {
> + iio_channel_stop_all_cb(priv->iio_cb);
> + priv->iio_active = false;
> + }
> +}
> +
> +static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> + int ret;
> +
> + ret = iio_write_channel_attribute(priv->iio_ch,
> + substream->runtime->rate, 0,
> + IIO_CHAN_INFO_SAMP_FREQ);
> + if (ret < 0) {
> + dev_err(dai->dev, "%s: Failed to set %d sampling rate\n",
> + __func__, substream->runtime->rate);
> + return ret;
> + }
> +
> + if (!priv->iio_active) {
> + ret = iio_channel_start_all_cb(priv->iio_cb);
> + if (!ret)
> + priv->iio_active = true;
> + else
> + dev_err(dai->dev, "%s: IIO channel start failed (%d)\n",
> + __func__, ret);
> + }
> +
> + return ret;
> +}
> +
> +static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> + ssize_t size;
> +
> + dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
> +
> + /* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
> + if (dir == SND_SOC_CLOCK_IN) {
> + char str_freq[10];
> +
> + snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
> + size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
> + str_freq, sizeof(str_freq));
> + if (size != sizeof(str_freq)) {
> + dev_err(dai->dev, "%s: Failed to set SPI clock\n",
> + __func__);
> + return -EINVAL;
> + }
> + }
> + return 0;
> +}
> +
> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
> + .shutdown = stm32_adfsdm_shutdown,
> + .prepare = stm32_adfsdm_dai_prepare,
> + .set_sysclk = stm32_adfsdm_set_sysclk,
> +};
> +
> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
> + .capture = {
> + .channels_min = 1,
> + .channels_max = 1,
> + .formats = SNDRV_PCM_FMTBIT_S32_LE,
> + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
> + SNDRV_PCM_RATE_32000),
> + },
> + .ops = &stm32_adfsdm_dai_ops,
> +};
> +
> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
> + .name = "stm32_dfsdm_audio",
> +};
> +
> +static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
> +{
> + struct stm32_adfsdm_priv *priv = private;
> + struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
> + u8 *pcm_buff = priv->pcm_buff;
> + u8 *src_buff = (u8 *)data;
> + unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
> + unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
> + unsigned int old_pos = priv->pos;
> + unsigned int cur_size = size;
> +
> + dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %lu\n",
> + __func__, &pcm_buff[priv->pos], priv->pos, size);
> +
> + if ((priv->pos + size) > buff_size) {
> + memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
> + cur_size -= buff_size - priv->pos;
> + priv->pos = 0;
> + }
> +
> + memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
> + priv->pos = (priv->pos + cur_size) % buff_size;
> +
> + if (cur_size != size || (old_pos && (old_pos % period_size < size)))
> + snd_pcm_period_elapsed(priv->substream);
> +
> + return 0;
> +}
> +
> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct stm32_adfsdm_priv *priv =
> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + priv->pos = 0;
> + return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
> + stm32_afsdm_pcm_cb, priv);
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_STOP:
> + return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> + int ret;
> +
> + ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
> + if (!ret)
> + priv->substream = substream;
> +
> + return ret;
> +}
> +
> +static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct stm32_adfsdm_priv *priv =
> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> + snd_pcm_lib_free_pages(substream);
> + priv->substream = NULL;
> +
> + return 0;
> +}
> +
> +static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
> + struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct stm32_adfsdm_priv *priv =
> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> + return bytes_to_frames(substream->runtime, priv->pos);
> +}
> +
> +static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct stm32_adfsdm_priv *priv =
> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
> + int ret;
> +
> + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
> + if (ret < 0)
> + return ret;
> + priv->pcm_buff = substream->runtime->dma_area;
> +
> + return iio_channel_cb_set_buffer_watermark(priv->iio_cb,
> + params_period_size(params));
> +}
> +
> +static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> + snd_pcm_lib_free_pages(substream);
> +
> + return 0;
> +}
> +
> +static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
> + .open = stm32_adfsdm_pcm_open,
> + .close = stm32_adfsdm_pcm_close,
> + .hw_params = stm32_adfsdm_pcm_hw_params,
> + .hw_free = stm32_adfsdm_pcm_hw_free,
> + .trigger = stm32_adfsdm_trigger,
> + .pointer = stm32_adfsdm_pcm_pointer,
> +};
> +
> +static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
> +{
> + struct snd_pcm *pcm = rtd->pcm;
> + struct stm32_adfsdm_priv *priv =
> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
> + unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
> + int ret;
> +
> + /*
> + * FIXME :
> + * A platform as been registered per DAI.
> + * In soc_new_pcm function, pcm_new callback is called for each
> + * component of the sound card. So if n dai links are created this
> + * function is called n times.
> + */
> + if (priv->allocated)
> + return 0;
> +
> + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
> + priv->dev, size, size);
> + if (!ret)
> + priv->allocated = true;
> +
> + return ret;
> +}
> +
> +static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
> +{
> + struct snd_pcm_substream *substream;
> + struct snd_soc_pcm_runtime *rtd;
> + struct stm32_adfsdm_priv *priv;
> +
> + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
> + if (substream) {
> + rtd = substream->private_data;
> + priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> + snd_pcm_lib_preallocate_free_for_all(pcm);
> + priv->allocated = false;
> + }
> +}
> +
> +static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
> + .ops = &stm32_adfsdm_pcm_ops,
> + .pcm_new = stm32_adfsdm_pcm_new,
> + .pcm_free = stm32_adfsdm_pcm_free,
> +};
> +
> +static const struct of_device_id stm32_adfsdm_of_match[] = {
> + {.compatible = "st,stm32h7-dfsdm-dai"},
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
> +
> +static int stm32_adfsdm_probe(struct platform_device *pdev)
> +{
> + struct stm32_adfsdm_priv *priv;
> + int ret;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->dev = &pdev->dev;
> + priv->dai_drv = stm32_adfsdm_dai;
> +
> + dev_set_drvdata(&pdev->dev, priv);
> +
> + ret = devm_snd_soc_register_component(&pdev->dev,
> + &stm32_adfsdm_dai_component,
> + &priv->dai_drv, 1);
> + if (ret < 0)
> + return ret;
> +
> + /* Associate iio channel */
> + priv->iio_ch = devm_iio_channel_get_all(&pdev->dev);
> + if (IS_ERR(priv->iio_ch))
> + return PTR_ERR(priv->iio_ch);
> +
> + priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
> + if (IS_ERR(priv->iio_cb))
> + return PTR_ERR(priv->iio_ch);
> +
> + ret = devm_snd_soc_register_platform(&pdev->dev,
> + &stm32_adfsdm_soc_platform);
> + if (ret < 0)
> + dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
> + __func__);
> +
> + return ret;
> +}
> +
> +static struct platform_driver stm32_adfsdm_driver = {
> + .driver = {
> + .name = STM32_ADFSDM_DRV_NAME,
> + .of_match_table = stm32_adfsdm_of_match,
> + },
> + .probe = stm32_adfsdm_probe,
> +};
> +
> +module_platform_driver(stm32_adfsdm_driver);
> +
> +MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
^ permalink raw reply
* [PATCH v2] pinctrl: stm32: Fix copyright
From: Linus Walleij @ 2017-12-02 15:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171130084648.23588-1-benjamin.gaignard@st.com>
On Thu, Nov 30, 2017 at 9:46 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Uniformize STMicroelectronics copyrights header
> Add SPDX identifier
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> Acked-by: Alexandre TORGUE <alexandre.torgue@st.com>
> CC: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Pulled out the v1 I applied and applied this instead.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 2/4] ARM: dts: lager: Convert to named i2c-gpio bindings
From: Linus Walleij @ 2017-12-02 15:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512046646-15253-3-git-send-email-geert+renesas@glider.be>
On Thu, Nov 30, 2017 at 1:57 PM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
> named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
> gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
> more error-prone unnamed variant.
>
> Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
> flags, which were implicitly assumed before.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 4/4] ARM: dts: alt: Convert to named i2c-gpio bindings
From: Linus Walleij @ 2017-12-02 15:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512046646-15253-5-git-send-email-geert+renesas@glider.be>
On Thu, Nov 30, 2017 at 1:57 PM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
> named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
> gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
> more error-prone unnamed variant.
>
> Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
> flags, which were implicitly assumed before.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 1/4] ARM: dts: armadillo800eva: Convert to named i2c-gpio bindings
From: Linus Walleij @ 2017-12-02 15:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512046646-15253-2-git-send-email-geert+renesas@glider.be>
On Thu, Nov 30, 2017 at 1:57 PM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
> named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
> gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
> more error-prone unnamed variant.
>
> Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
> flags, which were implicitly assumed before.
>
> The latter gets rid of the message:
>
> gpio-208 (?): enforced open drain please flag it properly in DT/ACPI DSDT/board file
> gpio-91 (?): enforced open drain please flag it properly in DT/ACPI DSDT/board file
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 3/4] ARM: dts: koelsch: Convert to named i2c-gpio bindings
From: Linus Walleij @ 2017-12-02 15:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512046646-15253-4-git-send-email-geert+renesas@glider.be>
On Thu, Nov 30, 2017 at 1:57 PM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
> named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
> gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
> more error-prone unnamed variant.
>
> Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
> flags, which were implicitly assumed before.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] dt-bindings: pinctrl: uniphier: add UniPhier pinctrl binding
From: Linus Walleij @ 2017-12-02 15:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1511855386-10421-1-git-send-email-yamada.masahiro@socionext.com>
On Tue, Nov 28, 2017 at 8:49 AM, Masahiro Yamada
<yamada.masahiro@socionext.com> wrote:
> The driver has been in the tree for a while, but its binding document
> is missing. Hence, here it is.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Patch applied with Rob's ACK.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v4 03/10] dt-bindings: gpio: gpio-axp209: add pinctrl features
From: Linus Walleij @ 2017-12-02 15:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <7fdc42fd2d3572069292ebe1376bb446beb4d8a9.1512135804.git-series.quentin.schulz@free-electrons.com>
On Fri, Dec 1, 2017 at 2:44 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
> an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
>
> This adds the pinctrl features to the driver so GPIO0/1 can be used as
> ADC or LDO regulator.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Rob Herring <robh@kernel.org>
Please add a reference to the generic pinctrl bindings and state that you
use them and how.
pinctrl/pinctrl-bindings.txt
> +The GPIOs can be muxed to other functions and therefore, must be a subnode of
> +axp_gpio.
> +
> +Example:
> +
> +&axp_gpio {
> + gpio0_adc: gpio0-adc {
> + pins = "GPIO0";
> + function = "adc";
> + };
> +};
So write explicitly that this driver employs the per-pin muxing pattern.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v4 08/10] mfd: axp20x: add pinctrl cell for AXP813
From: Linus Walleij @ 2017-12-02 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e0b4ce746c052b78f08dff54d0ad7ce7ebc611ab.1512135804.git-series.quentin.schulz@free-electrons.com>
On Fri, Dec 1, 2017 at 2:44 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> As GPIO/pinctrl driver now supports AXP813, add a cell for it.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
It doesn't seem to have any dependencies so I guess Lee can simply
apply this separately.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v4 09/10] ARM: dtsi: axp81x: add GPIO DT node
From: Linus Walleij @ 2017-12-02 15:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <af90c57ebf97c8f5504a4b5b1dad3638bedadfef.1512135804.git-series.quentin.schulz@free-electrons.com>
On Fri, Dec 1, 2017 at 2:44 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> This adds DT node for the GPIO/pinctrl part present in AXP813/AXP818.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Please apply this through ARM SoC.
Yours,
Linus Walleij
^ 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