* Re: [PATCH 07/11] ASoC: mmp-sspa: Prepare/unprepare the clocks
From: Mark Brown @ 2020-05-12 16:29 UTC (permalink / raw)
To: Lubomir Rintel
Cc: Liam Girdwood, Michael Turquette, Stephen Boyd, Rob Herring,
linux-clk, devicetree, linux-kernel, linux-media
In-Reply-To: <20200512153654.GB604838@furthur.local>
[-- Attachment #1: Type: text/plain, Size: 587 bytes --]
On Tue, May 12, 2020 at 05:36:54PM +0200, Lubomir Rintel wrote:
> On Tue, May 12, 2020 at 01:45:20PM +0100, Mark Brown wrote:
> > This fix should've been earlier in the series so it could be sent as a
> > fix.
> I'll order it earlier on v2.
> However, there's not much of a point in picking this patch alone,
> because the driver is certainly not used anywhere and very likely
> doesn't even work to any sensible extent without the rest of the
> series.
Well, I've already applied everything except the DT patches for that
reason - it's just worth pointing out for future reference.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [RFC][PATCH 3/4] dma-buf: cma_heap: Extend logic to export CMA regions tagged with "linux,cma-heap"
From: Rob Herring @ 2020-05-12 16:37 UTC (permalink / raw)
To: Brian Starkey
Cc: John Stultz, Robin Murphy, lkml, Sumit Semwal, Andrew F. Davis,
Benjamin Gaignard, Liam Mark, Pratik Patel, Laura Abbott,
Chenbo Feng, Alistair Strachan, Sandeep Patil, Hridya Valsaraju,
Christoph Hellwig, Marek Szyprowski, Andrew Morton,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
dri-devel, linux-mm, nd
In-Reply-To: <20200504090628.d2q32dwyg6em5pp7@DESKTOP-E1NTVVP.localdomain>
On Mon, May 04, 2020 at 10:06:28AM +0100, Brian Starkey wrote:
> On Fri, May 01, 2020 at 12:01:40PM -0700, John Stultz wrote:
> > On Fri, May 1, 2020 at 4:08 AM Robin Murphy <robin.murphy@arm.com> wrote:
> > >
> > > On 2020-05-01 11:21 am, Brian Starkey wrote:
> > > > Hi John,
> > > >
> > > > On Fri, May 01, 2020 at 07:39:48AM +0000, John Stultz wrote:
> > > >> This patch reworks the cma_heap initialization so that
> > > >> we expose both the default CMA region and any CMA regions
> > > >> tagged with "linux,cma-heap" in the device-tree.
> > > >>
> > > >> Cc: Rob Herring <robh+dt@kernel.org>
> > > >> Cc: Sumit Semwal <sumit.semwal@linaro.org>
> > > >> Cc: "Andrew F. Davis" <afd@ti.com>
> > > >> Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> > > >> Cc: Liam Mark <lmark@codeaurora.org>
> > > >> Cc: Pratik Patel <pratikp@codeaurora.org>
> > > >> Cc: Laura Abbott <labbott@redhat.com>
> > > >> Cc: Brian Starkey <Brian.Starkey@arm.com>
> > > >> Cc: Chenbo Feng <fengc@google.com>
> > > >> Cc: Alistair Strachan <astrachan@google.com>
> > > >> Cc: Sandeep Patil <sspatil@google.com>
> > > >> Cc: Hridya Valsaraju <hridya@google.com>
> > > >> Cc: Christoph Hellwig <hch@lst.de>
> > > >> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
> > > >> Cc: Robin Murphy <robin.murphy@arm.com>
> > > >> Cc: Andrew Morton <akpm@linux-foundation.org>
> > > >> Cc: devicetree@vger.kernel.org
> > > >> Cc: dri-devel@lists.freedesktop.org
> > > >> Cc: linux-mm@kvack.org
> > > >> Signed-off-by: John Stultz <john.stultz@linaro.org>
> > > >> ---
> > > >> drivers/dma-buf/heaps/cma_heap.c | 18 +++++++++---------
> > > >> 1 file changed, 9 insertions(+), 9 deletions(-)
> > > >>
> > > >> diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c
> > > >> index 626cf7fd033a..dd154e2db101 100644
> > > >> --- a/drivers/dma-buf/heaps/cma_heap.c
> > > >> +++ b/drivers/dma-buf/heaps/cma_heap.c
> > > >> @@ -141,6 +141,11 @@ static int __add_cma_heap(struct cma *cma, void *data)
> > > >> {
> > > >> struct cma_heap *cma_heap;
> > > >> struct dma_heap_export_info exp_info;
> > > >> + struct cma *default_cma = dev_get_cma_area(NULL);
> > > >> +
> > > >> + /* We only add the default heap and explicitly tagged heaps */
> > > >> + if (cma != default_cma && !cma_dma_heap_enabled(cma))
> > > >> + return 0;
> > > >
> > > > Thinking about the pl111 thread[1], I'm wondering if we should also
> > > > let drivers call this directly to expose their CMA pools, even if they
> > > > aren't tagged for dma-heaps in DT. But perhaps that's too close to
> > > > policy.
> > >
> > > That sounds much like what my first thoughts were - apologies if I'm
> > > wildly off-base here, but as far as I understand:
> > >
> > > - Device drivers know whether they have their own "memory-region" or not.
> > > - Device drivers already have to do *something* to participate in dma-buf.
> > > - Device drivers know best how they make use of both the above.
> > > - Therefore couldn't it be left to drivers to choose whether to register
> > > their CMA regions as heaps, without having to mess with DT at all?
+1, but I'm biased toward any solution not using DT. :)
> > I guess I'm not opposed to this. But I guess I'd like to see some more
> > details? You're thinking the pl111 driver would add the
> > "memory-region" node itself?
> >
> > Assuming that's the case, my only worry is what if that memory-region
> > node isn't a CMA area, but instead something like a carveout? Does the
> > driver need to parse enough of the dt to figure out where to register
> > the region as a heap?
>
> My thinking was more like there would already be a reserved-memory
> node in DT for the chunk of memory, appropriately tagged so that it
> gets added as a CMA region.
>
> The device's node would have "memory-region=<&blah>;" and would use
> of_reserved_mem_device_init() to link up dev->cma_area to the
> corresponding cma region.
>
> So far, that's all in-place already. The bit that's missing is
> exposing that dev->cma_area to userspace as a dma_heap - so we could
> just have "int cma_heap_add(struct cma *cma)" or "int
> cma_heap_dev_add(struct device *dev)" or something exported for
> drivers to expose their device-assigned cma region if they wanted to.
>
> I don't think this runs into the lifetime problems of generalised
> heaps-as-modules either, because the CMA region will never go away
> even if the driver does.
>
> Alongside that, I do think the completely DT-driven approach can be
> useful too - because there may be regions which aren't associated with
> any specific device driver, that we want exported as heaps.
And they are associated with the hardware description rather than the
userspace environment?
Rob
^ permalink raw reply
* Re: [PATCH 3/3] ASoC: fsl_esai: Add new compatible string for imx8qm
From: Rob Herring @ 2020-05-12 16:38 UTC (permalink / raw)
To: Shengjiu Wang
Cc: Xiubo.Lee, broonie, robh+dt, alsa-devel, tiwai, lgirdwood,
devicetree, linux-kernel, festevam, timur, perex, nicoleotsuka,
linuxppc-dev
In-Reply-To: <307a82c80c27b807165e1f532dd9aecbad9b2eae.1588320656.git.shengjiu.wang@nxp.com>
On Fri, 1 May 2020 16:12:06 +0800, Shengjiu Wang wrote:
> Add new compatible string "fsl,imx8qm-esai" in the binding document.
>
> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> ---
> Documentation/devicetree/bindings/sound/fsl,esai.txt | 1 +
> 1 file changed, 1 insertion(+)
>
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v3 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
From: Rob Herring @ 2020-05-12 16:42 UTC (permalink / raw)
To: Jiaxun Yang
Cc: maz, Thomas Gleixner, Jason Cooper, Huacai Chen, linux-kernel,
devicetree, linux-mips
In-Reply-To: <20200501092139.2988670-2-jiaxun.yang@flygoat.com>
On Fri, May 01, 2020 at 05:21:33PM +0800, Jiaxun Yang wrote:
> Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
> .../interrupt-controller/loongson,htvec.yaml | 59 +++++++++++++++++++
> 1 file changed, 59 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
> new file mode 100644
> index 000000000000..547a80c89eba
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
> @@ -0,0 +1,59 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Loongson-3 HyperTransport Interrupt Vector Controller
> +
> +maintainers:
> + - Jiaxun Yang <jiaxun.yang@flygoat.com>
> +
> +allOf:
> + - $ref: /schemas/interrupt-controller.yaml#
Don't need this. It's already applied to any node named
'interrupt-controller'.
> +
> +description: |
Can drop '|' if you don't need formatting.
> + This interrupt controller is found in the Loongson-3 family of chips for
> + receiving vectorized interrupts from PCH's interrupt controller.
> +
> +properties:
> + compatible:
> + const: loongson,htvec-1.0
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + minItems: 1
> + maxItems: 4
> + description: |
> + Four parent interrupts that receive chained interrupts.
> +
> + interrupt-controller: true
> +
> + '#interrupt-cells':
> + const: 1
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-controller
> + - '#interrupt-cells'
Add:
additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/irq.h>
> + htvec: interrupt-controller@1fb000080 {
Unit-address doesn't match reg.
> + compatible = "loongson,htvec-1.0";
> + reg = <0xfb000080 0x40>;
> + interrupt-controller;
> + #interrupt-cells = <1>;
> +
> + interrupt-parent = <&liointc>;
> + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
> + <25 IRQ_TYPE_LEVEL_HIGH>,
> + <26 IRQ_TYPE_LEVEL_HIGH>,
> + <27 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +...
> --
> 2.26.0.rc2
>
^ permalink raw reply
* Re: [PATCH v1] ASoC: rockchip-i2s: add description for rk3308
From: Mark Brown @ 2020-05-12 16:44 UTC (permalink / raw)
To: heiko, Johan Jonker
Cc: linux-rockchip, devicetree, robh+dt, lgirdwood, linux-kernel,
alsa-devel, linux-arm-kernel
In-Reply-To: <20200507113238.7904-1-jbx6244@gmail.com>
On Thu, 7 May 2020 13:32:38 +0200, Johan Jonker wrote:
> The description below is already in use for rk3308.dtsi,
> but was somehow never added to a document, so add
> "rockchip,rk3308-i2s", "rockchip,rk3066-i2s"
> for i2s nodes on a rk3308 platform to rockchip-i2s.yaml.
> One of the rk3308 i2s nodes also has a different dma layout,
> so change that as well.
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.8
Thanks!
[1/1] ASoC: rockchip-i2s: add description for rk3308
commit: ab436c974e08d9254999d7a84d86b1d4c1749230
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply
* Re: [PATCH 01/11] ASoC: mmp-sspa: Flip SNDRV_PCM_FMTBIT_S24_3LE on
From: Mark Brown @ 2020-05-12 16:45 UTC (permalink / raw)
To: Liam Girdwood, Lubomir Rintel
Cc: Rob Herring, Michael Turquette, devicetree, Stephen Boyd,
linux-kernel, linux-media, linux-clk
In-Reply-To: <20200511210134.1224532-2-lkundrak@v3.sk>
On Mon, 11 May 2020 23:01:24 +0200, Lubomir Rintel wrote:
> The hw_params() callback handles the 3-byte format, not
> SNDRV_PCM_FMTBIT_S24_LE.
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.8
Thanks!
[1/7] ASoC: mmp-sspa: A trivial typo fix
commit: e0b9024d2c8851b18b953823204278602bf73086
[2/7] ASoC: mmp-sspa: Get rid of dma_params and phys_base
commit: c9aeda1c94973f835b3d1b6c785a414caaf935c3
[3/7] ASoC: mmp-sspa: Add support for soc-generic-dmaengine-pcm
commit: 724da05378ba7af6e273451a2c3f565a3315a9db
[4/7] ASoC: mmp-sspa: Remove the embedded struct ssp_device
commit: 3c4e89df3b45348dc0ee01a2ef1be710f7424ff7
[5/7] ASoC: mmp-sspa: Prepare/unprepare the clocks
commit: 8ecdcac8792b6787ecb2341d25cb82165cf0129d
[6/7] ASoC: mmp-sspa: Add support for the runtime power management
commit: 7d98cc648253c362ebfc582b11095a0e3f001896
[7/7] ASoC: mmp-sspa: Set appropriate bus format for given bit width
commit: 39ec7e9b699910792468cf41a179d9930052e8ff
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply
* Re: [PATCH v3 0/6] allow ramoops to collect all kmesg_dump events
From: Pavel Tatashin @ 2020-05-12 16:49 UTC (permalink / raw)
To: Petr Mladek
Cc: Kees Cook, Anton Vorontsov, Colin Cross, Tony Luck,
Jonathan Corbet, Rob Herring, Benson Leung,
Enric Balletbo i Serra, Sergey Senozhatsky, Steven Rostedt,
James Morris, Sasha Levin, Linux Doc Mailing List, LKML,
devicetree
In-Reply-To: <20200512155207.GF17734@linux-b0ei>
On Tue, May 12, 2020 at 11:52 AM Petr Mladek <pmladek@suse.com> wrote:
>
> On Tue 2020-05-12 10:03:44, Pavel Tatashin wrote:
> > > OK, I personally see this as two separate problems:
> > >
> > > 1. Missing support to set loglevel per console.
> > > 2. Missing support to dump messages for other reasons.
> > >
> > > I would remove the paragraph about console log levels completely.
> >
> > OK, I see your point, this paragraph can be removed, however, I think
> > it makes it clear to understand the rationale for this change. As I
> > understand, the per console loglevel has been proposed but were never
> > accepted.
>
> The proposal was not accepted because there were more requirements:
>
> + add console device into sysfs so that it can be modified there
> + make a reasonable backward compatible behavior
>
> I guess that the sysfs interface discouraged the author to continue
> on it.
>
> Note that console loglevel handling is very complicated. There are
> already four variables, see console_printk array in
> kernel/printk/printk.c. Per console loglevel would make it even
> more complicated.
>
> It is a nighmare. And introducing max_reason parameter goes the same way.
>
> > > Now, the max_reason logic makes sense only when all the values
> > > have some ordering. Is this the case?
> > >
> > > I see it as two distinct sets:
> > >
> > > + panic, oops, emerg: describe how critical is an error situation
> > > + restart, halt, poweroff: describe behavior when the system goes down
> > >
> > > Let's say that panic is more critical than oops. Is restart more
> > > critical than halt?
> > >
> > > If you want the dump during restart. Does it mean that you want it
> > > also during emergency situation?
> > >
> > > My fear is that this patchset is going to introduce user interface
> > > (max_reason) with a weird logic. IMHO, max_reason is confusing even
> > > in the code and we should not spread this to users.
> > >
> > > Is there any reason why the existing printk.always_kmsg_dump option
> > > is not enough for you?
> >
> > printk.always_kmsg_dump is not working for me because ramoops has its
> > own filtering based on dump_oops boolean, and ignores everything but
> > panics and conditionally oops.
> > max_reason makes the ramoops internal logic cleaner compared to using dump_oops.
>
> I see. Just to be sure. Is the main reason to add max_reason parameter
> to keep complatibility of the deprecated dump_oops parameter? Or is
> there any real use case for this granularity?
>
> I made some arecheology. ramoops.dump_oops parameter was added in 2010 by the
> initial commit 56d611a04fb2db77334e ("char drivers: RAM oops/panic
> logger."
>
> Note that the initial implementation printed Oops reason only when
> dump_oops was set. It printed all other reasons otherwise. It seems
> that there were only the two reasons at that time.
>
> Now, printk.always_kmsg_dump parameter was added later in 2012 by
> the commit c22ab332902333f8376601 ("kmsg_dump: don't run on non-error
> paths by default").
>
> IMHO, the later commit actually fixed the default behavior of ramoops.
>
> I wonder if anyone is actually using the ramoops.dump_oops parameter
> in reality. I would personally make it deprecated and change the
> default behavior to work according to printk.always_kmsg_dump parameter.
This sounds alright to me with one slight problem. I am doing this
work for an embedded arm64 SoC, so controlling everything via device
tree is preferable compared to having some settings via device tree
and others via kernel parameters, especially because the kernel
parameters are hardcoded by firmware that we try not to update too
often for uptime reasons.
>
> IMHO, ramoops.dump_oops just increases complexity and should not have
> been introduced at all. I would try hard to avoid introducing even bigger
> complecity and mess.
I agree, amoops.dump_oops should be depricated with or without
max_reason change.
>
> I know that there is the "do not break existing userspace" rule. The
> question is if there is any user and if it is worth it.
>
> > I agree, the reasons in kmsg_dump_reason do not order well (I
> > actually want to add another reason for kexec type reboots, and where
> > do I put it?), so how about if we change the ordering list to
> > bitfield/flags, and instead of max_reason provide: "reasons" bitset?
>
> It looks too complicated. I would really try hard to avoid the
> parameter at all.
OK. Should we remove max_reason from struct kmsg_dumper and also
remove the misleading comment about kmsg_dump_reason ordering?
Thank you,
Pasha
^ permalink raw reply
* Re: [PATCH v2 3/6] dmaengine: dw: Set DMA device max segment size parameter
From: Serge Semin @ 2020-05-12 17:01 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Serge Semin, Vineet Gupta, Vinod Koul, Viresh Kumar, Dan Williams,
Alexey Malahov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Rob Herring, linux-mips, devicetree, dmaengine,
linux-kernel
In-Reply-To: <20200512123551.GX185537@smile.fi.intel.com>
On Tue, May 12, 2020 at 03:35:51PM +0300, Andy Shevchenko wrote:
> On Tue, May 12, 2020 at 12:16:22AM +0300, Serge Semin wrote:
> > On Fri, May 08, 2020 at 02:21:52PM +0300, Andy Shevchenko wrote:
> > > On Fri, May 08, 2020 at 01:53:01PM +0300, Serge Semin wrote:
> > > > Maximum block size DW DMAC configuration corresponds to the max segment
> > > > size DMA parameter in the DMA core subsystem notation. Lets set it with a
> > > > value specific to the probed DW DMA controller. It shall help the DMA
> > > > clients to create size-optimized SG-list items for the controller. This in
> > > > turn will cause less dw_desc allocations, less LLP reinitializations,
> > > > better DMA device performance.
>
> > > Yeah, I have locally something like this and I didn't dare to upstream because
> > > there is an issue. We have this information per DMA controller, while we
> > > actually need this on per DMA channel basis.
> > >
> > > Above will work only for synthesized DMA with all channels having same block
> > > size. That's why above conditional is not needed anyway.
> >
> > Hm, I don't really see why the conditional isn't needed and this won't work. As
> > you can see in the loop above Initially I find a maximum of all channels maximum
> > block sizes and use it then as a max segment size parameter for the whole device.
> > If the DW DMA controller has the same max block size of all channels, then it
> > will be found. If the channels've been synthesized with different block sizes,
> > then the optimization will work for the one with greatest block size. The SG
> > list entries of the channels with lesser max block size will be split up
> > by the DW DMAC driver, which would have been done anyway without
> > max_segment_size being set. Here we at least provide the optimization for the
> > channels with greatest max block size.
> >
> > I do understand that it would be good to have this parameter setup on per generic
> > DMA channel descriptor basis. But DMA core and device descriptor doesn't provide
> > such facility, so setting at least some justified value is a good idea.
> >
> > >
> > > OTOH, I never saw the DesignWare DMA to be synthesized differently (I remember
> > > that Intel Medfield has interesting settings, but I don't remember if DMA
> > > channels are different inside the same controller).
> > >
> > > Vineet, do you have any information that Synopsys customers synthesized DMA
> > > controllers with different channel characteristics inside one DMA IP?
> >
> > AFAICS the DW DMAC channels can be synthesized with different max block size.
> > The IP core supports such configuration. So we can't assume that such DMAC
> > release can't be found in a real hardware just because we've never seen one.
> > No matter what Vineet will have to say in response to your question.
>
> My point here that we probably can avoid complications till we have real
> hardware where it's different. As I said I don't remember a such, except
> *maybe* Intel Medfield, which is quite outdated and not supported for wider
> audience anyway.
I see your point. My position is different in this matter and explained in the
previous emails. Let's see what Viresh and Vinod think of it.
-Sergey
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply
* Re: [PATCH v2 2/7] dt-bindings: watchdog: dw-wdt: Support devices with asynch clocks
From: Serge Semin @ 2020-05-12 17:16 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Serge Semin, Wim Van Sebroeck, Guenter Roeck, Rob Herring,
Alexey Malahov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Philipp Zabel, linux-mips, linux-watchdog,
devicetree, linux-kernel
In-Reply-To: <477b9f75-485c-3208-b58c-56a5c13e15bb@cogentembedded.com>
Hello Sergei
On Mon, May 11, 2020 at 11:28:00AM +0300, Sergei Shtylyov wrote:
> On 11.05.2020 11:25, Sergei Shtylyov wrote:
>
> > > DW Watchdog IP core can be synthesised with asynchronous timer/APB
> > > clocks support (WDT_ASYNC_CLK_MODE_ENABLE == 1). In this case
> > > a separate clock signal is supposed to be used to feed watchdog timer
> > > and APB interface of the device. Lets along with the watchdog timer
> > ^ verb missing? or comma?
>
> Oh, and probably "Let's" too. :-)
>
> >
> > > reference clock expect to have the optional APB3 bu interface clock
> > > sepcified in a DW WDT dt node.
> >
> > Specified.
Thanks for noticing this. I'll just change the sentence to: "Let's update
the DW Watchdog DT node schema so it would support the optional APB3 bus
clock specified along with the mandatory watchdog timer reference clock."
-Sergey
> >
> > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > > Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> > > Cc: Paul Burton <paulburton@kernel.org>
> > > Cc: Ralf Baechle <ralf@linux-mips.org>
> > > Cc: Arnd Bergmann <arnd@arndb.de>
> > > Cc: Philipp Zabel <p.zabel@pengutronix.de>
> > > Cc: linux-mips@vger.kernel.org
> > [...]
>
> MBR, Sergei
^ permalink raw reply
* Re: [PATCH v9 2/4] media: i2c: Add MAX9286 driver
From: Manivannan Sadhasivam @ 2020-05-12 17:40 UTC (permalink / raw)
To: Kieran Bingham
Cc: linux-renesas-soc, linux-media, devicetree, linux-kernel,
Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
Jacopo Mondi, Niklas Söderlund, sakari.ailus, Hans Verkuil,
Hyun Kwon, Rob Herring, Jacopo Mondi, Laurent Pinchart,
Niklas Söderlund
In-Reply-To: <20200512155105.1068064-3-kieran.bingham+renesas@ideasonboard.com>
Hi Kieran,
On Tue, May 12, 2020 at 04:51:03PM +0100, Kieran Bingham wrote:
> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
> CSI-2 output. The device supports multicamera streaming applications,
> and features the ability to synchronise the attached cameras.
>
> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
> is supported over I2C, which implements an I2C mux to facilitate
> communications with connected cameras across the reverse control
> channel.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>
> --
> v2:
> - Fix MAINTAINERS entry
>
> This posting is released with the following modifications to work
> without Sakari's VC developments:
> - max9286_g_mbus_config() re-instated
> - max9286_get_frame_desc() is not bus/csi aware
> - max9286_{get,set}_routing() removed
>
> v3:
> - Initialise notifier with v4l2_async_notifier_init
> - Update for new mbus csi2 format V4L2_MBUS_CSI2_DPHY
>
> v4: - Re-introduce required code to function with the VC series.
>
> - Implement max9286_get_routing, max9286_set_routing
> - Remove max9286_g_mbus_config
>
> v5: (internal release)
> - Fix printk formatting for hex value
> - max9286->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE (add |)
> - MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER -> MEDIA_ENT_F_VID_IF_BRIDGE
> - Remove 'device is bound' workaround
>
> v6:
> - v4l2_subdev_krouting instead of v4l2_subdev_routing separated
> to allow integration without the VC/V4L2-Mux series.
> - convert sd_to_max9286 to inline function
> - rename max9286_device to max9286_priv
> - Cleanup the v4l2_async_notifier
> - Extend MODULE_AUTHOR
> - Replace of_graph_get_endpoint_by_regs with fwnode_graph_get_endpoint_by_id
> - Pass default bus type when parsing fwnode endpoint (Manivannan Sadhasivam)
> - Use new YAML file reference in MAINTAINERS
> - Parse new i2c-mux node in max9286_get_i2c_by_id
> (This could/should be refactored to parse these separately first)
> - Spelling and calculation fixes in the FSYNC_LOCKED check comments
> - Identify each enabled i2c-mux channel in a single pass
> - max9286: Improve mux-state readbility [v2]
> - Fix frame sync lock durations
> - Add comment to describe /why/ we must open the mux in s_stream
> - use -EXDEV as return code for failed link synchronisation.
> - Fix reference counting of the dt nodeS
> - Convert to probe_new for I2C
> - Remove redundant max9286_i2c_mux_state
> - Provide optional enable-gpio (max9286-pwdn)
>
> v7:
> [Kieran]
> - Ensure powerdown lines are optional
> - Add a 4ms power-up delay
> - Add max9286_check_config_link() to core
> - Add GPIO chip controller for GPIO0OUT and GPIO1OUT
> - Fix GPIO registration
> - max9286: Split out async registration
> (fixes regulator -EPROBE_DEFERs failures)
> - Collect all V4L2 registrations
> - balance v4l2_async refcnting
> - Rename max9286_v4l2_async_ => max9286_v4l2_notifier_
>
> [Jacopo]
> - Remove redundanct MAXIM_I2C_SPEED macros
> - Move notifiers operations
> - Add delay after reverse channel reconfiguration
> - Move link setup to completion
> - Fix up max9286_check_config_link() implementation
> - Remove redundant dual configuration of reverse channel
>
> v8:
>
> [Kieran]
> - Update the bound_sources mask on unbind
> - Convert probe kzalloc usage to devm_ variant
> - Fix up cleanup path from GPIO PowerDown registration
> - cleanup GPIO device registration fail path
> - Convert to use devm_regulator_get()
> - Fit max9286_parse_dt print on one line
> - Move multi-device workarounds out of upstream driver
> - Remove I2C mod-table
> - Lock format changes
> - Describe pad index usage
> - Remove poc_enabled workaround
> - Rename the max9286_gpio to be more explicit on it's actions.
> - Move max9286_init_format call
> - Rework probe sequence and simplify error paths.
> - Simplify i2c comments
> - Implement Pixelrate control
> - Disable overlap window
>
> [Jacopo]
> - Adapt Kconfig to latest upstream changes
> - Put of node on error
> - Calculate pixel rate
> - Simplify overlap window disablement
>
> v9:
>
> [Kieran]
> - Kconfig: Depend on OF
> - Re-sort addition to Makefile
> ---
> MAINTAINERS | 10 +
> drivers/media/i2c/Kconfig | 13 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/max9286.c | 1332 +++++++++++++++++++++++++++++++++++
> 4 files changed, 1356 insertions(+)
> create mode 100644 drivers/media/i2c/max9286.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7bb6e22d5da..99e3bf7760fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10274,6 +10274,16 @@ F: Documentation/hwmon/max6697.rst
> F: drivers/hwmon/max6697.c
> F: include/linux/platform_data/max6697.h
>
> +MAX9286 QUAD GMSL DESERIALIZER DRIVER
> +M: Jacopo Mondi <jacopo+renesas@jmondi.org>
> +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml
> +F: drivers/media/i2c/max9286.c
> +
> MAX9860 MONO AUDIO VOICE CODEC DRIVER
> M: Peter Rosin <peda@axentia.se>
> L: alsa-devel@alsa-project.org (moderated for non-subscribers)
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 3abc80373ec0..2e390f41f6da 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -464,6 +464,19 @@ config VIDEO_VPX3220
> To compile this driver as a module, choose M here: the
> module will be called vpx3220.
>
> +config VIDEO_MAX9286
> + tristate "Maxim MAX9286 GMSL deserializer support"
> + depends on I2C && I2C_MUX
> + depends on OF
> + select V4L2_FWNODE
> + select VIDEO_V4L2_SUBDEV_API
> + select MEDIA_CONTROLLER
> + help
> + This driver supports the Maxim MAX9286 GMSL deserializer.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called max9286.
> +
> comment "Video and audio decoders"
>
> config VIDEO_SAA717X
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 77bf7d0b691f..f0b001ee4b05 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -117,6 +117,7 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o
> obj-$(CONFIG_VIDEO_IMX290) += imx290.o
> obj-$(CONFIG_VIDEO_IMX319) += imx319.o
> obj-$(CONFIG_VIDEO_IMX355) += imx355.o
> +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
> obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
>
> obj-$(CONFIG_SDR_MAX2175) += max2175.o
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> new file mode 100644
> index 000000000000..481d65f2b51d
> --- /dev/null
> +++ b/drivers/media/i2c/max9286.c
> @@ -0,0 +1,1332 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Maxim MAX9286 GMSL Deserializer Driver
> + *
> + * Copyright (C) 2017-2019 Jacopo Mondi
> + * Copyright (C) 2017-2019 Kieran Bingham
> + * Copyright (C) 2017-2019 Laurent Pinchart
> + * Copyright (C) 2017-2019 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* Register 0x00 */
> +#define MAX9286_MSTLINKSEL_AUTO (7 << 5)
> +#define MAX9286_MSTLINKSEL(n) ((n) << 5)
> +#define MAX9286_EN_VS_GEN BIT(4)
> +#define MAX9286_LINKEN(n) (1 << (n))
> +/* Register 0x01 */
> +#define MAX9286_FSYNCMODE_ECU (3 << 6)
> +#define MAX9286_FSYNCMODE_EXT (2 << 6)
> +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6)
> +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6)
> +#define MAX9286_GPIEN BIT(5)
> +#define MAX9286_ENLMO_RSTFSYNC BIT(2)
> +#define MAX9286_FSYNCMETH_AUTO (2 << 0)
> +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0)
> +#define MAX9286_FSYNCMETH_MANUAL (0 << 0)
> +#define MAX9286_REG_FSYNC_PERIOD_L 0x06
> +#define MAX9286_REG_FSYNC_PERIOD_M 0x07
> +#define MAX9286_REG_FSYNC_PERIOD_H 0x08
> +/* Register 0x0a */
> +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4))
> +#define MAX9286_REVCCEN(n) (1 << (n))
> +/* Register 0x0c */
> +#define MAX9286_HVEN BIT(7)
> +#define MAX9286_EDC_6BIT_HAMMING (2 << 5)
> +#define MAX9286_EDC_6BIT_CRC (1 << 5)
> +#define MAX9286_EDC_1BIT_PARITY (0 << 5)
> +#define MAX9286_DESEL BIT(4)
> +#define MAX9286_INVVS BIT(3)
> +#define MAX9286_INVHS BIT(2)
> +#define MAX9286_HVSRC_D0 (2 << 0)
> +#define MAX9286_HVSRC_D14 (1 << 0)
> +#define MAX9286_HVSRC_D18 (0 << 0)
> +/* Register 0x0f */
> +#define MAX9286_0X0F_RESERVED BIT(3)
> +/* Register 0x12 */
> +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6)
> +#define MAX9286_CSIDBL BIT(5)
> +#define MAX9286_DBL BIT(4)
> +#define MAX9286_DATATYPE_USER_8BIT (11 << 0)
> +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
> +#define MAX9286_DATATYPE_USER_24BIT (9 << 0)
> +#define MAX9286_DATATYPE_RAW14 (8 << 0)
> +#define MAX9286_DATATYPE_RAW11 (7 << 0)
> +#define MAX9286_DATATYPE_RAW10 (6 << 0)
> +#define MAX9286_DATATYPE_RAW8 (5 << 0)
> +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
> +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0)
> +#define MAX9286_DATATYPE_RGB555 (2 << 0)
> +#define MAX9286_DATATYPE_RGB565 (1 << 0)
> +#define MAX9286_DATATYPE_RGB888 (0 << 0)
> +/* Register 0x15 */
> +#define MAX9286_VC(n) ((n) << 5)
> +#define MAX9286_VCTYPE BIT(4)
> +#define MAX9286_CSIOUTEN BIT(3)
> +#define MAX9286_0X15_RESV (3 << 0)
> +/* Register 0x1b */
> +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
> +#define MAX9286_ENEQ(n) (1 << (n))
> +/* Register 0x27 */
> +#define MAX9286_LOCKED BIT(7)
> +/* Register 0x31 */
> +#define MAX9286_FSYNC_LOCKED BIT(6)
> +/* Register 0x34 */
> +#define MAX9286_I2CLOCACK BIT(7)
> +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5)
> +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5)
> +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5)
> +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5)
> +#define MAX9286_I2CMSTBT_837KBPS (7 << 2)
> +#define MAX9286_I2CMSTBT_533KBPS (6 << 2)
> +#define MAX9286_I2CMSTBT_339KBPS (5 << 2)
> +#define MAX9286_I2CMSTBT_173KBPS (4 << 2)
> +#define MAX9286_I2CMSTBT_105KBPS (3 << 2)
> +#define MAX9286_I2CMSTBT_84KBPS (2 << 2)
> +#define MAX9286_I2CMSTBT_28KBPS (1 << 2)
> +#define MAX9286_I2CMSTBT_8KBPS (0 << 2)
> +#define MAX9286_I2CSLVTO_NONE (3 << 0)
> +#define MAX9286_I2CSLVTO_1024US (2 << 0)
> +#define MAX9286_I2CSLVTO_256US (1 << 0)
> +#define MAX9286_I2CSLVTO_64US (0 << 0)
> +/* Register 0x3b */
> +#define MAX9286_REV_TRF(n) ((n) << 4)
> +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */
> +#define MAX9286_REV_AMP_X BIT(0)
> +/* Register 0x3f */
> +#define MAX9286_EN_REV_CFG BIT(6)
> +#define MAX9286_REV_FLEN(n) ((n) - 20)
> +/* Register 0x49 */
> +#define MAX9286_VIDEO_DETECT_MASK 0x0f
> +/* Register 0x69 */
> +#define MAX9286_LFLTBMONMASKED BIT(7)
> +#define MAX9286_LOCKMONMASKED BIT(6)
> +#define MAX9286_AUTOCOMBACKEN BIT(5)
> +#define MAX9286_AUTOMASKEN BIT(4)
> +#define MAX9286_MASKLINK(n) ((n) << 0)
> +
> +/*
> + * The sink and source pads are created to match the OF graph port numbers so
> + * that their indexes can be used interchangeably.
> + */
> +#define MAX9286_NUM_GMSL 4
> +#define MAX9286_N_SINKS 4
> +#define MAX9286_N_PADS 5
> +#define MAX9286_SRC_PAD 4
> +
> +struct max9286_source {
> + struct v4l2_async_subdev asd;
> + struct v4l2_subdev *sd;
> + struct fwnode_handle *fwnode;
> +};
> +
> +#define asd_to_max9286_source(_asd) \
> + container_of(_asd, struct max9286_source, asd)
> +
> +struct max9286_priv {
> + struct i2c_client *client;
> + struct gpio_desc *gpiod_pwdn;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MAX9286_N_PADS];
> + struct regulator *regulator;
> +
> + struct gpio_chip gpio;
> + u8 gpio_state;
> +
> + struct i2c_mux_core *mux;
> + unsigned int mux_channel;
> + bool mux_open;
> +
> + struct v4l2_ctrl_handler ctrls;
> + struct v4l2_ctrl *pixelrate;
> +
> + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
> +
> + /* Protects controls and fmt structures */
> + struct mutex mutex;
> +
> + unsigned int nsources;
> + unsigned int source_mask;
> + unsigned int route_mask;
> + unsigned int bound_sources;
> + unsigned int csi2_data_lanes;
> + struct max9286_source sources[MAX9286_NUM_GMSL];
> + struct v4l2_async_notifier notifier;
> +};
> +
> +static struct max9286_source *next_source(struct max9286_priv *priv,
> + struct max9286_source *source)
> +{
> + if (!source)
> + source = &priv->sources[0];
> + else
> + source++;
> +
> + for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
> + if (source->fwnode)
> + return source;
> + }
> +
> + return NULL;
> +}
> +
> +#define for_each_source(priv, source) \
> + for ((source) = NULL; ((source) = next_source((priv), (source))); )
> +
> +#define to_index(priv, source) ((source) - &(priv)->sources[0])
> +
> +static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct max9286_priv, sd);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * I2C IO
> + */
> +
> +static int max9286_read(struct max9286_priv *priv, u8 reg)
> +{
> + int ret;
> +
> + ret = i2c_smbus_read_byte_data(priv->client, reg);
> + if (ret < 0)
> + dev_err(&priv->client->dev,
> + "%s: register 0x%02x read failed (%d)\n",
> + __func__, reg, ret);
> +
> + return ret;
> +}
> +
> +static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
> +{
> + int ret;
> +
> + ret = i2c_smbus_write_byte_data(priv->client, reg, val);
> + if (ret < 0)
> + dev_err(&priv->client->dev,
> + "%s: register 0x%02x write failed (%d)\n",
> + __func__, reg, ret);
> +
> + return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * I2C Multiplexer
> + */
> +
> +static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
> +{
> + max9286_write(priv, 0x0a, conf);
> +
> + /*
> + * We must sleep after any change to the forward or reverse channel
> + * configuration.
> + */
> + usleep_range(3000, 5000);
> +}
> +
> +static void max9286_i2c_mux_open(struct max9286_priv *priv)
> +{
> + /* Open all channels on the MAX9286 */
> + max9286_i2c_mux_configure(priv, 0xff);
> +
> + priv->mux_open = true;
> +}
> +
> +static void max9286_i2c_mux_close(struct max9286_priv *priv)
> +{
> + /*
> + * Ensure that both the forward and reverse channel are disabled on the
> + * mux, and that the channel ID is invalidated to ensure we reconfigure
> + * on the next max9286_i2c_mux_select() call.
> + */
> + max9286_i2c_mux_configure(priv, 0x00);
> +
> + priv->mux_open = false;
> + priv->mux_channel = -1;
> +}
> +
> +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
> +{
> + struct max9286_priv *priv = i2c_mux_priv(muxc);
> +
> + /* Channel select is disabled when configured in the opened state. */
> + if (priv->mux_open)
> + return 0;
> +
> + if (priv->mux_channel == chan)
> + return 0;
> +
> + priv->mux_channel = chan;
> +
> + max9286_i2c_mux_configure(priv,
> + MAX9286_FWDCCEN(chan) |
> + MAX9286_REVCCEN(chan));
> +
> + return 0;
> +}
> +
> +static int max9286_i2c_mux_init(struct max9286_priv *priv)
> +{
> + struct max9286_source *source;
> + int ret;
> +
> + if (!i2c_check_functionality(priv->client->adapter,
> + I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> + return -ENODEV;
> +
> + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
> + priv->nsources, 0, I2C_MUX_LOCKED,
> + max9286_i2c_mux_select, NULL);
> + if (!priv->mux)
> + return -ENOMEM;
> +
> + priv->mux->priv = priv;
> +
> + for_each_source(priv, source) {
> + unsigned int index = to_index(priv, source);
> +
> + ret = i2c_mux_add_adapter(priv->mux, 0, index, 0);
> + if (ret < 0)
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + i2c_mux_del_adapters(priv->mux);
> + return ret;
> +}
> +
> +static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
> +{
> + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
> + MAX9286_I2CMSTBT_105KBPS;
> +
> + if (localack)
> + config |= MAX9286_I2CLOCACK;
> +
> + max9286_write(priv, 0x34, config);
> + usleep_range(3000, 5000);
> +}
> +
> +/*
> + * max9286_check_video_links() - Make sure video links are detected and locked
> + *
> + * Performs safety checks on video link status. Make sure they are detected
> + * and all enabled links are locked.
> + *
> + * Returns 0 for success, -EIO for errors.
> + */
> +static int max9286_check_video_links(struct max9286_priv *priv)
> +{
> + unsigned int i;
> + int ret;
> +
> + /*
> + * Make sure valid video links are detected.
> + * The delay is not characterized in de-serializer manual, wait up
> + * to 5 ms.
> + */
> + for (i = 0; i < 10; i++) {
> + ret = max9286_read(priv, 0x49);
> + if (ret < 0)
> + return -EIO;
> +
> + if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
> + break;
> +
> + usleep_range(350, 500);
> + }
> +
> + if (i == 10) {
> + dev_err(&priv->client->dev,
> + "Unable to detect video links: 0x%02x\n", ret);
> + return -EIO;
> + }
> +
> + /* Make sure all enabled links are locked (4ms max). */
> + for (i = 0; i < 10; i++) {
> + ret = max9286_read(priv, 0x27);
> + if (ret < 0)
> + return -EIO;
> +
> + if (ret & MAX9286_LOCKED)
> + break;
> +
> + usleep_range(350, 450);
> + }
> +
> + if (i == 10) {
> + dev_err(&priv->client->dev, "Not all enabled links locked\n");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * max9286_check_config_link() - Detect and wait for configuration links
> + *
> + * Determine if the configuration channel is up and settled for a link.
> + *
> + * Returns 0 for success, -EIO for errors.
> + */
> +static int max9286_check_config_link(struct max9286_priv *priv,
> + unsigned int source_mask)
> +{
> + unsigned int conflink_mask = (source_mask & 0x0f) << 4;
> + unsigned int i;
> + int ret;
> +
> + /*
> + * Make sure requested configuration links are detected.
> + * The delay is not characterized in the chip manual: wait up
> + * to 5 milliseconds.
> + */
> + for (i = 0; i < 10; i++) {
> + ret = max9286_read(priv, 0x49) & 0xf0;
> + if (ret < 0)
> + return -EIO;
> +
> + if (ret == conflink_mask)
> + break;
> +
> + usleep_range(350, 500);
> + }
> +
> + if (ret != conflink_mask) {
> + dev_err(&priv->client->dev,
> + "Unable to detect configuration links: 0x%02x expected 0x%02x\n",
> + ret, conflink_mask);
> + return -EIO;
> + }
> +
> + dev_info(&priv->client->dev,
> + "Successfully detected configuration links after %u loops: 0x%02x\n",
> + i, conflink_mask);
> +
> + return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 Subdev
> + */
> +
> +static int max9286_set_pixelrate(struct max9286_priv *priv)
> +{
> + struct max9286_source *source = NULL;
> + u64 pixelrate = 0;
> +
> + for_each_source(priv, source) {
> + struct v4l2_ctrl *ctrl;
> + u64 source_rate = 0;
> +
> + /* Pixel rate is mandatory to be reported by sources. */
> + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
> + V4L2_CID_PIXEL_RATE);
> + if (!ctrl) {
> + pixelrate = 0;
> + break;
> + }
> +
> + /* All source must report the same pixel rate. */
> + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
> + if (!pixelrate) {
> + pixelrate = source_rate;
> + } else if (pixelrate != source_rate) {
> + dev_err(&priv->client->dev,
> + "Unable to calculate pixel rate\n");
> + return -EINVAL;
> + }
> + }
> +
> + if (!pixelrate) {
> + dev_err(&priv->client->dev,
> + "No pixel rate control available in sources\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * The CSI-2 transmitter pixel rate is the single source rate multiplied
> + * by the number of available sources.
> + */
> + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
> + pixelrate * priv->nsources);
> +}
> +
> +static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_subdev *asd)
> +{
> + struct max9286_priv *priv = sd_to_max9286(notifier->sd);
> + struct max9286_source *source = asd_to_max9286_source(asd);
> + unsigned int index = to_index(priv, source);
> + unsigned int src_pad;
> + int ret;
> +
> + ret = media_entity_get_fwnode_pad(&subdev->entity,
> + source->fwnode,
> + MEDIA_PAD_FL_SOURCE);
> + if (ret < 0) {
> + dev_err(&priv->client->dev,
> + "Failed to find pad for %s\n", subdev->name);
> + return ret;
> + }
> +
> + priv->bound_sources |= BIT(index);
> + source->sd = subdev;
> + src_pad = ret;
> +
> + ret = media_create_pad_link(&source->sd->entity, src_pad,
> + &priv->sd.entity, index,
> + MEDIA_LNK_FL_ENABLED |
> + MEDIA_LNK_FL_IMMUTABLE);
> + if (ret) {
> + dev_err(&priv->client->dev,
> + "Unable to link %s:%u -> %s:%u\n",
> + source->sd->name, src_pad, priv->sd.name, index);
> + return ret;
> + }
> +
> + dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n",
> + subdev->name, src_pad, index);
> +
> + /*
> + * We can only register v4l2_async_notifiers, which do not provide a
> + * means to register a complete callback. bound_sources allows us to
> + * identify when all remote serializers have completed their probe.
> + */
> + if (priv->bound_sources != priv->source_mask)
> + return 0;
> +
> + /*
> + * All enabled sources have probed and enabled their reverse control
> + * channels:
> + *
> + * - Verify all configuration links are properly detected
> + * - Disable auto-ack as communication on the control channel are now
> + * stable.
> + */
> + max9286_check_config_link(priv, priv->source_mask);
> +
> + /*
> + * Re-configure I2C with local acknowledge disabled after cameras have
> + * probed.
> + */
> + max9286_configure_i2c(priv, false);
> +
> + return max9286_set_pixelrate(priv);
> +}
> +
> +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_subdev *asd)
> +{
> + struct max9286_priv *priv = sd_to_max9286(notifier->sd);
> + struct max9286_source *source = asd_to_max9286_source(asd);
> + unsigned int index = to_index(priv, source);
> +
> + source->sd = NULL;
> + priv->bound_sources &= ~BIT(index);
> +}
> +
> +static const struct v4l2_async_notifier_operations max9286_notify_ops = {
> + .bound = max9286_notify_bound,
> + .unbind = max9286_notify_unbind,
> +};
> +
> +static int max9286_v4l2_notifier_register(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct max9286_source *source = NULL;
> + int ret;
> +
> + if (!priv->nsources)
> + return 0;
> +
> + v4l2_async_notifier_init(&priv->notifier);
> +
> + for_each_source(priv, source) {
> + unsigned int i = to_index(priv, source);
> +
> + source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
> + source->asd.match.fwnode = source->fwnode;
> +
> + ret = v4l2_async_notifier_add_subdev(&priv->notifier,
> + &source->asd);
> + if (ret) {
> + dev_err(dev, "Failed to add subdev for source %d", i);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + /*
> + * Balance the reference counting handled through
> + * v4l2_async_notifier_cleanup()
> + */
> + fwnode_handle_get(source->fwnode);
> + }
> +
> + priv->notifier.ops = &max9286_notify_ops;
> +
> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> + if (ret) {
> + dev_err(dev, "Failed to register subdev_notifier");
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
> +{
> + if (!priv->nsources)
> + return;
> +
> + v4l2_async_notifier_unregister(&priv->notifier);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> +}
> +
> +static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct max9286_priv *priv = sd_to_max9286(sd);
> + struct max9286_source *source;
> + unsigned int i;
> + bool sync = false;
> + int ret;
> +
> + if (enable) {
> + /*
> + * The frame sync between cameras is transmitted across the
> + * reverse channel as GPIO. We must open all channels while
> + * streaming to allow this synchronisation signal to be shared.
> + */
> + max9286_i2c_mux_open(priv);
> +
> + /* Start all cameras. */
> + for_each_source(priv, source) {
> + ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
> + if (ret)
> + return ret;
> + }
> +
> + ret = max9286_check_video_links(priv);
> + if (ret)
> + return ret;
> +
> + /*
> + * Wait until frame synchronization is locked.
> + *
> + * Manual says frame sync locking should take ~6 VTS.
> + * From practical experience at least 8 are required. Give
> + * 12 complete frames time (~400ms at 30 fps) to achieve frame
> + * locking before returning error.
> + */
> + for (i = 0; i < 40; i++) {
> + if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) {
> + sync = true;
> + break;
> + }
> + usleep_range(9000, 11000);
> + }
> +
> + if (!sync) {
> + dev_err(&priv->client->dev,
> + "Failed to get frame synchronization\n");
> + return -EXDEV; /* Invalid cross-device link */
> + }
> +
> + /*
> + * Enable CSI output, VC set according to link number.
> + * Bit 7 must be set (chip manual says it's 0 and reserved).
> + */
> + max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE |
> + MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
> + } else {
> + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
> +
> + /* Stop all cameras. */
> + for_each_source(priv, source)
> + v4l2_subdev_call(source->sd, video, s_stream, 0);
> +
> + max9286_i2c_mux_close(priv);
> + }
> +
> + return 0;
> +}
> +
> +static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->pad || code->index > 0)
> + return -EINVAL;
> +
> + code->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> + return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +max9286_get_pad_format(struct max9286_priv *priv,
> + struct v4l2_subdev_pad_config *cfg,
> + unsigned int pad, u32 which)
> +{
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &priv->fmt[pad];
> + default:
> + return NULL;
> + }
> +}
> +
> +static int max9286_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *format)
> +{
> + struct max9286_priv *priv = sd_to_max9286(sd);
> + struct v4l2_mbus_framefmt *cfg_fmt;
> +
> + if (format->pad >= MAX9286_SRC_PAD)
> + return -EINVAL;
> +
> + /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
> + switch (format->format.code) {
> + case MEDIA_BUS_FMT_UYVY8_2X8:
> + case MEDIA_BUS_FMT_VYUY8_2X8:
> + case MEDIA_BUS_FMT_YUYV8_2X8:
> + case MEDIA_BUS_FMT_YVYU8_2X8:
> + break;
> + default:
> + format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
Is there any reason for not setting default format to MEDIA_BUS_FMT_UYVY8_2X8?
Thanks,
Mani
> + break;
> + }
> +
> + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which);
> + if (!cfg_fmt)
> + return -EINVAL;
> +
> + mutex_lock(&priv->mutex);
> + *cfg_fmt = format->format;
> + mutex_unlock(&priv->mutex);
> +
> + return 0;
> +}
> +
> +static int max9286_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *format)
> +{
> + struct max9286_priv *priv = sd_to_max9286(sd);
> + struct v4l2_mbus_framefmt *cfg_fmt;
> +
> + if (format->pad >= MAX9286_SRC_PAD)
> + return -EINVAL;
> +
> + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which);
> + if (!cfg_fmt)
> + return -EINVAL;
> +
> + mutex_lock(&priv->mutex);
> + format->format = *cfg_fmt;
> + mutex_unlock(&priv->mutex);
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops max9286_video_ops = {
> + .s_stream = max9286_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
> + .enum_mbus_code = max9286_enum_mbus_code,
> + .get_fmt = max9286_get_fmt,
> + .set_fmt = max9286_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_ops max9286_subdev_ops = {
> + .video = &max9286_video_ops,
> + .pad = &max9286_pad_ops,
> +};
> +
> +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
> +{
> + fmt->width = 1280;
> + fmt->height = 800;
> + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> + fmt->colorspace = V4L2_COLORSPACE_SRGB;
> + fmt->field = V4L2_FIELD_NONE;
> + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +}
> +
> +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_mbus_framefmt *format;
> + unsigned int i;
> +
> + for (i = 0; i < MAX9286_N_SINKS; i++) {
> + format = v4l2_subdev_get_try_format(subdev, fh->pad, i);
> + max9286_init_format(format);
> + }
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
> + .open = max9286_open,
> +};
> +
> +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + switch (ctrl->id) {
> + case V4L2_CID_PIXEL_RATE:
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
> + .s_ctrl = max9286_s_ctrl,
> +};
> +
> +static int max9286_v4l2_register(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct fwnode_handle *ep;
> + int ret;
> + int i;
> +
> + /* Register v4l2 async notifiers for connected Camera subdevices */
> + ret = max9286_v4l2_notifier_register(priv);
> + if (ret) {
> + dev_err(dev, "Unable to register V4L2 async notifiers\n");
> + return ret;
> + }
> +
> + /* Configure V4L2 for the MAX9286 itself */
> +
> + for (i = 0; i < MAX9286_N_SINKS; i++)
> + max9286_init_format(&priv->fmt[i]);
> +
> + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
> + priv->sd.internal_ops = &max9286_subdev_internal_ops;
> + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + v4l2_ctrl_handler_init(&priv->ctrls, 1);
> + priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
> + &max9286_ctrl_ops,
> + V4L2_CID_PIXEL_RATE,
> + 1, INT_MAX, 1, 50000000);
> +
> + priv->sd.ctrl_handler = &priv->ctrls;
> + ret = priv->ctrls.error;
> + if (ret)
> + goto err_async;
> +
> + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> +
> + priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
> + for (i = 0; i < MAX9286_SRC_PAD; i++)
> + priv->pads[i].flags = MEDIA_PAD_FL_SINK;
> + ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS,
> + priv->pads);
> + if (ret)
> + goto err_async;
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD,
> + 0, 0);
> + if (!ep) {
> + dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
> + ret = -ENOENT;
> + goto err_async;
> + }
> + priv->sd.fwnode = ep;
> +
> + ret = v4l2_async_register_subdev(&priv->sd);
> + if (ret < 0) {
> + dev_err(dev, "Unable to register subdevice\n");
> + goto err_put_node;
> + }
> +
> + return 0;
> +
> +err_put_node:
> + fwnode_handle_put(ep);
> +err_async:
> + max9286_v4l2_notifier_unregister(priv);
> +
> + return ret;
> +}
> +
> +static void max9286_v4l2_unregister(struct max9286_priv *priv)
> +{
> + fwnode_handle_put(priv->sd.fwnode);
> + v4l2_async_unregister_subdev(&priv->sd);
> + max9286_v4l2_notifier_unregister(priv);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Probe/Remove
> + */
> +
> +static int max9286_setup(struct max9286_priv *priv)
> +{
> + /*
> + * Link ordering values for all enabled links combinations. Orders must
> + * be assigned sequentially from 0 to the number of enabled links
> + * without leaving any hole for disabled links. We thus assign orders to
> + * enabled links first, and use the remaining order values for disabled
> + * links are all links must have a different order value;
> + */
> + static const u8 link_order[] = {
> + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
> + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
> + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
> + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
> + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
> + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
> + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
> + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
> + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
> + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
> + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
> + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
> + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
> + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
> + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
> + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
> + };
> +
> + /*
> + * Set the I2C bus speed.
> + *
> + * Enable I2C Local Acknowledge during the probe sequences of the camera
> + * only. This should be disabled after the mux is initialised.
> + */
> + max9286_configure_i2c(priv, true);
> +
> + /*
> + * Reverse channel setup.
> + *
> + * - Enable custom reverse channel configuration (through register 0x3f)
> + * and set the first pulse length to 35 clock cycles.
> + * - Increase the reverse channel amplitude to 170mV to accommodate the
> + * high threshold enabled by the serializer driver.
> + */
> + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
> + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
> + MAX9286_REV_AMP_X);
> + usleep_range(2000, 2500);
> +
> + /*
> + * Enable GMSL links, mask unused ones and autodetect link
> + * used as CSI clock source.
> + */
> + max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
> + max9286_write(priv, 0x0b, link_order[priv->route_mask]);
> + max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
> +
> + /*
> + * Video format setup:
> + * Disable CSI output, VC is set according to Link number.
> + */
> + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
> +
> + /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
> + max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
> + MAX9286_CSILANECNT(priv->csi2_data_lanes) |
> + MAX9286_DATATYPE_YUV422_8BIT);
> +
> + /* Automatic: FRAMESYNC taken from the slowest Link. */
> + max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
> + MAX9286_FSYNCMETH_AUTO);
> +
> + /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
> + max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
> + MAX9286_HVSRC_D14);
> +
> + /*
> + * The overlap window seems to provide additional validation by tracking
> + * the delay between vsync and frame sync, generating an error if the
> + * delay is bigger than the programmed window, though it's not yet clear
> + * what value should be set.
> + *
> + * As it's an optional value and can be disabled, we do so by setting
> + * a 0 overlap value.
> + */
> + max9286_write(priv, 0x63, 0);
> + max9286_write(priv, 0x64, 0);
> +
> + /*
> + * Wait for 2ms to allow the link to resynchronize after the
> + * configuration change.
> + */
> + usleep_range(2000, 5000);
> +
> + return 0;
> +}
> +
> +static void max9286_gpio_set(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct max9286_priv *priv = gpiochip_get_data(chip);
> +
> + if (value)
> + priv->gpio_state |= BIT(offset);
> + else
> + priv->gpio_state &= ~BIT(offset);
> +
> + max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state);
> +}
> +
> +static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct max9286_priv *priv = gpiochip_get_data(chip);
> +
> + return priv->gpio_state & BIT(offset);
> +}
> +
> +static int max9286_register_gpio(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct gpio_chip *gpio = &priv->gpio;
> + int ret;
> +
> + static const char * const names[] = {
> + "GPIO0OUT",
> + "GPIO1OUT",
> + };
> +
> + /* Configure the GPIO */
> + gpio->label = dev_name(dev);
> + gpio->parent = dev;
> + gpio->owner = THIS_MODULE;
> + gpio->of_node = dev->of_node;
> + gpio->ngpio = 2;
> + gpio->base = -1;
> + gpio->set = max9286_gpio_set;
> + gpio->get = max9286_gpio_get;
> + gpio->can_sleep = true;
> + gpio->names = names;
> +
> + /* GPIO values default to high */
> + priv->gpio_state = BIT(0) | BIT(1);
> +
> + ret = devm_gpiochip_add_data(dev, gpio, priv);
> + if (ret)
> + dev_err(dev, "Unable to create gpio_chip\n");
> +
> + return ret;
> +}
> +
> +static int max9286_init(struct device *dev)
> +{
> + struct max9286_priv *priv;
> + struct i2c_client *client;
> + int ret;
> +
> + client = to_i2c_client(dev);
> + priv = i2c_get_clientdata(client);
> +
> + /* Enable the bus power. */
> + ret = regulator_enable(priv->regulator);
> + if (ret < 0) {
> + dev_err(&client->dev, "Unable to turn PoC on\n");
> + return ret;
> + }
> +
> + ret = max9286_setup(priv);
> + if (ret) {
> + dev_err(dev, "Unable to setup max9286\n");
> + goto err_regulator;
> + }
> +
> + /*
> + * Register all V4L2 interactions for the MAX9286 and notifiers for
> + * any subdevices connected.
> + */
> + ret = max9286_v4l2_register(priv);
> + if (ret) {
> + dev_err(dev, "Failed to register with V4L2\n");
> + goto err_regulator;
> + }
> +
> + ret = max9286_i2c_mux_init(priv);
> + if (ret) {
> + dev_err(dev, "Unable to initialize I2C multiplexer\n");
> + goto err_v4l2_register;
> + }
> +
> + /* Leave the mux channels disabled until they are selected. */
> + max9286_i2c_mux_close(priv);
> +
> + return 0;
> +
> +err_v4l2_register:
> + max9286_v4l2_unregister(priv);
> +err_regulator:
> + regulator_disable(priv->regulator);
> +
> + return ret;
> +}
> +
> +static void max9286_cleanup_dt(struct max9286_priv *priv)
> +{
> + struct max9286_source *source;
> +
> + for_each_source(priv, source) {
> + fwnode_handle_put(source->fwnode);
> + source->fwnode = NULL;
> + }
> +}
> +
> +static int max9286_parse_dt(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct device_node *i2c_mux;
> + struct device_node *node = NULL;
> + unsigned int i2c_mux_mask = 0;
> +
> + of_node_get(dev->of_node);
> + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
> + if (!i2c_mux) {
> + dev_err(dev, "Failed to find i2c-mux node\n");
> + of_node_put(dev->of_node);
> + return -EINVAL;
> + }
> +
> + /* Identify which i2c-mux channels are enabled */
> + for_each_child_of_node(i2c_mux, node) {
> + u32 id = 0;
> +
> + of_property_read_u32(node, "reg", &id);
> + if (id >= MAX9286_NUM_GMSL)
> + continue;
> +
> + if (!of_device_is_available(node)) {
> + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
> + continue;
> + }
> +
> + i2c_mux_mask |= BIT(id);
> + }
> + of_node_put(node);
> + of_node_put(i2c_mux);
> +
> + /* Parse the endpoints */
> + for_each_endpoint_of_node(dev->of_node, node) {
> + struct max9286_source *source;
> + struct of_endpoint ep;
> +
> + of_graph_parse_endpoint(node, &ep);
> + dev_dbg(dev, "Endpoint %pOF on port %d",
> + ep.local_node, ep.port);
> +
> + if (ep.port > MAX9286_NUM_GMSL) {
> + dev_err(dev, "Invalid endpoint %s on port %d",
> + of_node_full_name(ep.local_node), ep.port);
> + continue;
> + }
> +
> + /* For the source endpoint just parse the bus configuration. */
> + if (ep.port == MAX9286_SRC_PAD) {
> + struct v4l2_fwnode_endpoint vep = {
> + .bus_type = V4L2_MBUS_CSI2_DPHY
> + };
> + int ret;
> +
> + ret = v4l2_fwnode_endpoint_parse(
> + of_fwnode_handle(node), &vep);
> + if (ret) {
> + of_node_put(node);
> + of_node_put(dev->of_node);
> + return ret;
> + }
> +
> + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
> + dev_err(dev,
> + "Media bus %u type not supported\n",
> + vep.bus_type);
> + v4l2_fwnode_endpoint_free(&vep);
> + of_node_put(node);
> + of_node_put(dev->of_node);
> + return -EINVAL;
> + }
> +
> + priv->csi2_data_lanes =
> + vep.bus.mipi_csi2.num_data_lanes;
> + v4l2_fwnode_endpoint_free(&vep);
> +
> + continue;
> + }
> +
> + /* Skip if the corresponding GMSL link is unavailable. */
> + if (!(i2c_mux_mask & BIT(ep.port)))
> + continue;
> +
> + if (priv->sources[ep.port].fwnode) {
> + dev_err(dev,
> + "Multiple port endpoints are not supported: %d",
> + ep.port);
> +
> + continue;
> + }
> +
> + source = &priv->sources[ep.port];
> + source->fwnode = fwnode_graph_get_remote_endpoint(
> + of_fwnode_handle(node));
> + if (!source->fwnode) {
> + dev_err(dev,
> + "Endpoint %pOF has no remote endpoint connection\n",
> + ep.local_node);
> +
> + continue;
> + }
> +
> + priv->source_mask |= BIT(ep.port);
> + priv->nsources++;
> + }
> + of_node_put(node);
> + of_node_put(dev->of_node);
> +
> + priv->route_mask = priv->source_mask;
> +
> + return 0;
> +}
> +
> +static int max9286_probe(struct i2c_client *client)
> +{
> + struct max9286_priv *priv;
> + int ret;
> +
> + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + mutex_init(&priv->mutex);
> +
> + priv->client = client;
> + i2c_set_clientdata(client, priv);
> +
> + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpiod_pwdn))
> + return PTR_ERR(priv->gpiod_pwdn);
> +
> + gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
> + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
> +
> + /* Wait at least 4ms before the I2C lines latch to the address */
> + if (priv->gpiod_pwdn)
> + usleep_range(4000, 5000);
> +
> + /*
> + * The MAX9286 starts by default with all ports enabled, we disable all
> + * ports early to ensure that all channels are disabled if we error out
> + * and keep the bus consistent.
> + */
> + max9286_i2c_mux_close(priv);
> +
> + /*
> + * The MAX9286 initialises with auto-acknowledge enabled by default.
> + * This can be invasive to other transactions on the same bus, so
> + * disable it early. It will be enabled only as and when needed.
> + */
> + max9286_configure_i2c(priv, false);
> +
> + ret = max9286_register_gpio(priv);
> + if (ret)
> + goto err_powerdown;
> +
> + priv->regulator = devm_regulator_get(&client->dev, "poc");
> + if (IS_ERR(priv->regulator)) {
> + if (PTR_ERR(priv->regulator) != -EPROBE_DEFER)
> + dev_err(&client->dev,
> + "Unable to get PoC regulator (%ld)\n",
> + PTR_ERR(priv->regulator));
> + ret = PTR_ERR(priv->regulator);
> + goto err_powerdown;
> + }
> +
> + ret = max9286_parse_dt(priv);
> + if (ret)
> + goto err_powerdown;
> +
> + ret = max9286_init(&client->dev);
> + if (ret < 0)
> + goto err_cleanup_dt;
> +
> + return 0;
> +
> +err_cleanup_dt:
> + max9286_cleanup_dt(priv);
> +err_powerdown:
> + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
> +
> + return ret;
> +}
> +
> +static int max9286_remove(struct i2c_client *client)
> +{
> + struct max9286_priv *priv = i2c_get_clientdata(client);
> +
> + i2c_mux_del_adapters(priv->mux);
> +
> + max9286_v4l2_unregister(priv);
> +
> + regulator_disable(priv->regulator);
> +
> + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
> +
> + max9286_cleanup_dt(priv);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id max9286_dt_ids[] = {
> + { .compatible = "maxim,max9286" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, max9286_dt_ids);
> +
> +static struct i2c_driver max9286_i2c_driver = {
> + .driver = {
> + .name = "max9286",
> + .of_match_table = of_match_ptr(max9286_dt_ids),
> + },
> + .probe_new = max9286_probe,
> + .remove = max9286_remove,
> +};
> +
> +module_i2c_driver(max9286_i2c_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
> +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>
^ permalink raw reply
* Re: [PATCH v9 2/5] PCI: Add Loongson PCI Controller support
From: Bjorn Helgaas @ 2020-05-12 18:06 UTC (permalink / raw)
To: Jiaxun Yang
Cc: linux-pci, Rob Herring, Bjorn Helgaas, Rob Herring,
Thomas Bogendoerfer, Huacai Chen, Lorenzo Pieralisi, Paul Burton,
devicetree, linux-kernel, linux-mips
In-Reply-To: <20200512074413.3557835-2-jiaxun.yang@flygoat.com>
On Tue, May 12, 2020 at 03:43:56PM +0800, Jiaxun Yang wrote:
> This controller can be found on Loongson-2K SoC, Loongson-3
> systems with RS780E/LS7A PCH.
>
> The RS780E part of code was previously located at
> arch/mips/pci/ops-loongson3.c and now it can use generic PCI
> driver implementation.
>
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> +static void system_bus_quirk(struct pci_dev *pdev)
> +{
> + u16 tmp;
> +
> + /*
> + * These devices are not sharing resouces with rest of devices
> + * on host bus and firmware will ensure their BARs are placed
> + * in safe ranges. Also there might be some config registers
> + * in their config space so kernel shouldn't ignore them.
"Firmware ensuring BARs are placed in 'safe' ranges" is not a
sufficient answer. As I said before, Linux needs to know both the
ADDRESS and the SIZE of whatever non-standard BARs these are.
Otherwise, we're liable to assign that space to a different device.
If you have to hard-code the size, so be it. That would mean the
hardware is completely broken, but at least we could make Linux deal
with it. If the hardware consumes address space we don't know about,
we can't deal with that.
> + */
> + pdev->mmio_always_on = 1;
> + pdev->non_compliant_bars = 1;
> +}
> +void __iomem *pci_loongson_map_bus(struct pci_bus *bus, unsigned int devfn,
> + int where)
> +{
> + unsigned char busnum = bus->number;
> + struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
> + struct loongson_pci *priv = pci_host_bridge_priv(bridge);
> +
> + /*
> + * Do not read more than one device on the bus other than
> + * the host bus 0.
> + */
If the hardware is restricted such that the root bus number must be
zero, pleae say that explicitly here. Otherwise, it just looks like
a bug.
> + if (priv->flags & FLAG_DEV_FIX && busnum != 0 &&
> + PCI_SLOT(devfn) > 0)
> + return NULL;
> +
> + /* CFG0 can only access standard space */
> + if (where < PCI_CFG_SPACE_SIZE && priv->cfg0_base)
> + return cfg0_map(priv, busnum, devfn, where);
> +
> + /* CFG1 can access extended space */
> + if (where < PCI_CFG_SPACE_EXP_SIZE && priv->cfg1_base)
> + return cfg1_map(priv, busnum, devfn, where);
> +
> + return NULL;
> +}
^ permalink raw reply
* Re: [PATCH 1/4] arm64: dts: qcom: msm8916: Add blsp_i2c1
From: Bjorn Andersson @ 2020-05-12 18:15 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Andy Gross, linux-arm-msm, devicetree, ~postmarketos/upstreaming
In-Reply-To: <20200426140642.204395-2-stephan@gerhold.net>
On Sun 26 Apr 07:06 PDT 2020, Stephan Gerhold wrote:
> MSM8916 has another I2C QUP controller that can be enabled on
> GPIO 2 and 3.
>
> Add blsp_i2c1 to msm8916.dtsi and disable it by default.
>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Regards,
Bjorn
> Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
> ---
> On samsung-a2015 there is an external TFA9895 speaker amplifier
> connected to blsp_i2c1. There isn't a proper mainline driver
> for it (yet?), but it's nice to have the I2C bus in mainline at least.
> ---
> arch/arm64/boot/dts/qcom/msm8916-pins.dtsi | 24 ++++++++++++++++++++++
> arch/arm64/boot/dts/qcom/msm8916.dtsi | 15 ++++++++++++++
> 2 files changed, 39 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> index 242aaea68804..b45fd12856ea 100644
> --- a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> @@ -262,6 +262,30 @@ pinconf {
> };
> };
>
> + i2c1_default: i2c1_default {
> + pinmux {
> + function = "blsp_i2c1";
> + pins = "gpio2", "gpio3";
> + };
> + pinconf {
> + pins = "gpio2", "gpio3";
> + drive-strength = <2>;
> + bias-disable;
> + };
> + };
> +
> + i2c1_sleep: i2c1_sleep {
> + pinmux {
> + function = "gpio";
> + pins = "gpio2", "gpio3";
> + };
> + pinconf {
> + pins = "gpio2", "gpio3";
> + drive-strength = <2>;
> + bias-disable;
> + };
> + };
> +
> i2c2_default: i2c2_default {
> pinmux {
> function = "blsp_i2c2";
> diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
> index 8b429954ea29..6ab7cabde370 100644
> --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
> @@ -612,6 +612,21 @@ blsp_spi6: spi@78ba000 {
> status = "disabled";
> };
>
> + blsp_i2c1: i2c@78b5000 {
> + compatible = "qcom,i2c-qup-v2.2.1";
> + reg = <0x078b5000 0x500>;
> + interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&gcc GCC_BLSP1_AHB_CLK>,
> + <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
> + clock-names = "iface", "core";
> + pinctrl-names = "default", "sleep";
> + pinctrl-0 = <&i2c1_default>;
> + pinctrl-1 = <&i2c1_sleep>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + status = "disabled";
> + };
> +
> blsp_i2c2: i2c@78b6000 {
> compatible = "qcom,i2c-qup-v2.2.1";
> reg = <0x078b6000 0x500>;
> --
> 2.26.2
>
^ permalink raw reply
* Re: [PATCH v9 2/4] media: i2c: Add MAX9286 driver
From: Manivannan Sadhasivam @ 2020-05-12 18:17 UTC (permalink / raw)
To: Kieran Bingham
Cc: linux-renesas-soc, linux-media, devicetree, linux-kernel,
Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
Jacopo Mondi, Niklas Söderlund, sakari.ailus, Hans Verkuil,
Hyun Kwon, Rob Herring, Jacopo Mondi, Laurent Pinchart,
Niklas Söderlund
In-Reply-To: <20200512155105.1068064-3-kieran.bingham+renesas@ideasonboard.com>
On Tue, May 12, 2020 at 04:51:03PM +0100, Kieran Bingham wrote:
> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
> CSI-2 output. The device supports multicamera streaming applications,
> and features the ability to synchronise the attached cameras.
>
> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
> is supported over I2C, which implements an I2C mux to facilitate
> communications with connected cameras across the reverse control
> channel.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>
> --
> v2:
> - Fix MAINTAINERS entry
>
> This posting is released with the following modifications to work
> without Sakari's VC developments:
> - max9286_g_mbus_config() re-instated
> - max9286_get_frame_desc() is not bus/csi aware
> - max9286_{get,set}_routing() removed
>
> v3:
> - Initialise notifier with v4l2_async_notifier_init
> - Update for new mbus csi2 format V4L2_MBUS_CSI2_DPHY
>
> v4: - Re-introduce required code to function with the VC series.
>
> - Implement max9286_get_routing, max9286_set_routing
> - Remove max9286_g_mbus_config
>
> v5: (internal release)
> - Fix printk formatting for hex value
> - max9286->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE (add |)
> - MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER -> MEDIA_ENT_F_VID_IF_BRIDGE
> - Remove 'device is bound' workaround
>
> v6:
> - v4l2_subdev_krouting instead of v4l2_subdev_routing separated
> to allow integration without the VC/V4L2-Mux series.
> - convert sd_to_max9286 to inline function
> - rename max9286_device to max9286_priv
> - Cleanup the v4l2_async_notifier
> - Extend MODULE_AUTHOR
> - Replace of_graph_get_endpoint_by_regs with fwnode_graph_get_endpoint_by_id
> - Pass default bus type when parsing fwnode endpoint (Manivannan Sadhasivam)
> - Use new YAML file reference in MAINTAINERS
> - Parse new i2c-mux node in max9286_get_i2c_by_id
> (This could/should be refactored to parse these separately first)
> - Spelling and calculation fixes in the FSYNC_LOCKED check comments
> - Identify each enabled i2c-mux channel in a single pass
> - max9286: Improve mux-state readbility [v2]
> - Fix frame sync lock durations
> - Add comment to describe /why/ we must open the mux in s_stream
> - use -EXDEV as return code for failed link synchronisation.
> - Fix reference counting of the dt nodeS
> - Convert to probe_new for I2C
> - Remove redundant max9286_i2c_mux_state
> - Provide optional enable-gpio (max9286-pwdn)
>
> v7:
> [Kieran]
> - Ensure powerdown lines are optional
> - Add a 4ms power-up delay
> - Add max9286_check_config_link() to core
> - Add GPIO chip controller for GPIO0OUT and GPIO1OUT
> - Fix GPIO registration
> - max9286: Split out async registration
> (fixes regulator -EPROBE_DEFERs failures)
> - Collect all V4L2 registrations
> - balance v4l2_async refcnting
> - Rename max9286_v4l2_async_ => max9286_v4l2_notifier_
>
> [Jacopo]
> - Remove redundanct MAXIM_I2C_SPEED macros
> - Move notifiers operations
> - Add delay after reverse channel reconfiguration
> - Move link setup to completion
> - Fix up max9286_check_config_link() implementation
> - Remove redundant dual configuration of reverse channel
>
> v8:
>
> [Kieran]
> - Update the bound_sources mask on unbind
> - Convert probe kzalloc usage to devm_ variant
> - Fix up cleanup path from GPIO PowerDown registration
> - cleanup GPIO device registration fail path
> - Convert to use devm_regulator_get()
> - Fit max9286_parse_dt print on one line
> - Move multi-device workarounds out of upstream driver
> - Remove I2C mod-table
> - Lock format changes
> - Describe pad index usage
> - Remove poc_enabled workaround
> - Rename the max9286_gpio to be more explicit on it's actions.
> - Move max9286_init_format call
> - Rework probe sequence and simplify error paths.
> - Simplify i2c comments
> - Implement Pixelrate control
> - Disable overlap window
>
> [Jacopo]
> - Adapt Kconfig to latest upstream changes
> - Put of node on error
> - Calculate pixel rate
> - Simplify overlap window disablement
>
> v9:
>
> [Kieran]
> - Kconfig: Depend on OF
> - Re-sort addition to Makefile
> ---
> MAINTAINERS | 10 +
> drivers/media/i2c/Kconfig | 13 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/max9286.c | 1332 +++++++++++++++++++++++++++++++++++
> 4 files changed, 1356 insertions(+)
> create mode 100644 drivers/media/i2c/max9286.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7bb6e22d5da..99e3bf7760fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10274,6 +10274,16 @@ F: Documentation/hwmon/max6697.rst
> F: drivers/hwmon/max6697.c
> F: include/linux/platform_data/max6697.h
>
> +MAX9286 QUAD GMSL DESERIALIZER DRIVER
> +M: Jacopo Mondi <jacopo+renesas@jmondi.org>
> +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml
> +F: drivers/media/i2c/max9286.c
> +
> MAX9860 MONO AUDIO VOICE CODEC DRIVER
> M: Peter Rosin <peda@axentia.se>
> L: alsa-devel@alsa-project.org (moderated for non-subscribers)
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 3abc80373ec0..2e390f41f6da 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -464,6 +464,19 @@ config VIDEO_VPX3220
> To compile this driver as a module, choose M here: the
> module will be called vpx3220.
>
> +config VIDEO_MAX9286
> + tristate "Maxim MAX9286 GMSL deserializer support"
> + depends on I2C && I2C_MUX
> + depends on OF
> + select V4L2_FWNODE
> + select VIDEO_V4L2_SUBDEV_API
> + select MEDIA_CONTROLLER
> + help
> + This driver supports the Maxim MAX9286 GMSL deserializer.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called max9286.
> +
> comment "Video and audio decoders"
>
> config VIDEO_SAA717X
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 77bf7d0b691f..f0b001ee4b05 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -117,6 +117,7 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o
> obj-$(CONFIG_VIDEO_IMX290) += imx290.o
> obj-$(CONFIG_VIDEO_IMX319) += imx319.o
> obj-$(CONFIG_VIDEO_IMX355) += imx355.o
> +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
> obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
>
> obj-$(CONFIG_SDR_MAX2175) += max2175.o
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> new file mode 100644
> index 000000000000..481d65f2b51d
> --- /dev/null
> +++ b/drivers/media/i2c/max9286.c
> @@ -0,0 +1,1332 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Maxim MAX9286 GMSL Deserializer Driver
> + *
> + * Copyright (C) 2017-2019 Jacopo Mondi
> + * Copyright (C) 2017-2019 Kieran Bingham
> + * Copyright (C) 2017-2019 Laurent Pinchart
> + * Copyright (C) 2017-2019 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* Register 0x00 */
> +#define MAX9286_MSTLINKSEL_AUTO (7 << 5)
> +#define MAX9286_MSTLINKSEL(n) ((n) << 5)
> +#define MAX9286_EN_VS_GEN BIT(4)
> +#define MAX9286_LINKEN(n) (1 << (n))
> +/* Register 0x01 */
> +#define MAX9286_FSYNCMODE_ECU (3 << 6)
> +#define MAX9286_FSYNCMODE_EXT (2 << 6)
> +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6)
> +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6)
> +#define MAX9286_GPIEN BIT(5)
> +#define MAX9286_ENLMO_RSTFSYNC BIT(2)
> +#define MAX9286_FSYNCMETH_AUTO (2 << 0)
> +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0)
> +#define MAX9286_FSYNCMETH_MANUAL (0 << 0)
> +#define MAX9286_REG_FSYNC_PERIOD_L 0x06
> +#define MAX9286_REG_FSYNC_PERIOD_M 0x07
> +#define MAX9286_REG_FSYNC_PERIOD_H 0x08
> +/* Register 0x0a */
> +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4))
> +#define MAX9286_REVCCEN(n) (1 << (n))
> +/* Register 0x0c */
> +#define MAX9286_HVEN BIT(7)
> +#define MAX9286_EDC_6BIT_HAMMING (2 << 5)
> +#define MAX9286_EDC_6BIT_CRC (1 << 5)
> +#define MAX9286_EDC_1BIT_PARITY (0 << 5)
> +#define MAX9286_DESEL BIT(4)
> +#define MAX9286_INVVS BIT(3)
> +#define MAX9286_INVHS BIT(2)
> +#define MAX9286_HVSRC_D0 (2 << 0)
> +#define MAX9286_HVSRC_D14 (1 << 0)
> +#define MAX9286_HVSRC_D18 (0 << 0)
> +/* Register 0x0f */
> +#define MAX9286_0X0F_RESERVED BIT(3)
> +/* Register 0x12 */
> +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6)
> +#define MAX9286_CSIDBL BIT(5)
> +#define MAX9286_DBL BIT(4)
> +#define MAX9286_DATATYPE_USER_8BIT (11 << 0)
> +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
> +#define MAX9286_DATATYPE_USER_24BIT (9 << 0)
> +#define MAX9286_DATATYPE_RAW14 (8 << 0)
> +#define MAX9286_DATATYPE_RAW11 (7 << 0)
> +#define MAX9286_DATATYPE_RAW10 (6 << 0)
> +#define MAX9286_DATATYPE_RAW8 (5 << 0)
> +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
> +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0)
> +#define MAX9286_DATATYPE_RGB555 (2 << 0)
> +#define MAX9286_DATATYPE_RGB565 (1 << 0)
> +#define MAX9286_DATATYPE_RGB888 (0 << 0)
> +/* Register 0x15 */
> +#define MAX9286_VC(n) ((n) << 5)
> +#define MAX9286_VCTYPE BIT(4)
> +#define MAX9286_CSIOUTEN BIT(3)
> +#define MAX9286_0X15_RESV (3 << 0)
> +/* Register 0x1b */
> +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
> +#define MAX9286_ENEQ(n) (1 << (n))
> +/* Register 0x27 */
> +#define MAX9286_LOCKED BIT(7)
> +/* Register 0x31 */
> +#define MAX9286_FSYNC_LOCKED BIT(6)
> +/* Register 0x34 */
> +#define MAX9286_I2CLOCACK BIT(7)
> +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5)
> +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5)
> +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5)
> +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5)
> +#define MAX9286_I2CMSTBT_837KBPS (7 << 2)
> +#define MAX9286_I2CMSTBT_533KBPS (6 << 2)
> +#define MAX9286_I2CMSTBT_339KBPS (5 << 2)
> +#define MAX9286_I2CMSTBT_173KBPS (4 << 2)
> +#define MAX9286_I2CMSTBT_105KBPS (3 << 2)
> +#define MAX9286_I2CMSTBT_84KBPS (2 << 2)
> +#define MAX9286_I2CMSTBT_28KBPS (1 << 2)
> +#define MAX9286_I2CMSTBT_8KBPS (0 << 2)
> +#define MAX9286_I2CSLVTO_NONE (3 << 0)
> +#define MAX9286_I2CSLVTO_1024US (2 << 0)
> +#define MAX9286_I2CSLVTO_256US (1 << 0)
> +#define MAX9286_I2CSLVTO_64US (0 << 0)
> +/* Register 0x3b */
> +#define MAX9286_REV_TRF(n) ((n) << 4)
> +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */
> +#define MAX9286_REV_AMP_X BIT(0)
> +/* Register 0x3f */
> +#define MAX9286_EN_REV_CFG BIT(6)
> +#define MAX9286_REV_FLEN(n) ((n) - 20)
> +/* Register 0x49 */
> +#define MAX9286_VIDEO_DETECT_MASK 0x0f
> +/* Register 0x69 */
> +#define MAX9286_LFLTBMONMASKED BIT(7)
> +#define MAX9286_LOCKMONMASKED BIT(6)
> +#define MAX9286_AUTOCOMBACKEN BIT(5)
> +#define MAX9286_AUTOMASKEN BIT(4)
> +#define MAX9286_MASKLINK(n) ((n) << 0)
> +
> +/*
> + * The sink and source pads are created to match the OF graph port numbers so
> + * that their indexes can be used interchangeably.
> + */
> +#define MAX9286_NUM_GMSL 4
> +#define MAX9286_N_SINKS 4
> +#define MAX9286_N_PADS 5
> +#define MAX9286_SRC_PAD 4
> +
> +struct max9286_source {
> + struct v4l2_async_subdev asd;
> + struct v4l2_subdev *sd;
> + struct fwnode_handle *fwnode;
> +};
> +
> +#define asd_to_max9286_source(_asd) \
> + container_of(_asd, struct max9286_source, asd)
> +
> +struct max9286_priv {
> + struct i2c_client *client;
> + struct gpio_desc *gpiod_pwdn;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MAX9286_N_PADS];
> + struct regulator *regulator;
> +
> + struct gpio_chip gpio;
> + u8 gpio_state;
> +
> + struct i2c_mux_core *mux;
> + unsigned int mux_channel;
> + bool mux_open;
> +
> + struct v4l2_ctrl_handler ctrls;
> + struct v4l2_ctrl *pixelrate;
> +
> + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
> +
> + /* Protects controls and fmt structures */
> + struct mutex mutex;
> +
> + unsigned int nsources;
> + unsigned int source_mask;
> + unsigned int route_mask;
> + unsigned int bound_sources;
> + unsigned int csi2_data_lanes;
> + struct max9286_source sources[MAX9286_NUM_GMSL];
> + struct v4l2_async_notifier notifier;
> +};
> +
[...]
> +static int max9286_register_gpio(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct gpio_chip *gpio = &priv->gpio;
> + int ret;
> +
> + static const char * const names[] = {
> + "GPIO0OUT",
> + "GPIO1OUT",
> + };
> +
> + /* Configure the GPIO */
> + gpio->label = dev_name(dev);
So if you have more than one MAX9286 in a system, all gpiochips will appear
with the same name. I'd recommend to append the index to distinguish properly.
> + gpio->parent = dev;
> + gpio->owner = THIS_MODULE;
> + gpio->of_node = dev->of_node;
> + gpio->ngpio = 2;
> + gpio->base = -1;
> + gpio->set = max9286_gpio_set;
> + gpio->get = max9286_gpio_get;
> + gpio->can_sleep = true;
> + gpio->names = names;
> +
> + /* GPIO values default to high */
> + priv->gpio_state = BIT(0) | BIT(1);
> +
> + ret = devm_gpiochip_add_data(dev, gpio, priv);
> + if (ret)
> + dev_err(dev, "Unable to create gpio_chip\n");
> +
> + return ret;
> +}
> +
[...]
> +static int max9286_parse_dt(struct max9286_priv *priv)
> +{
> + struct device *dev = &priv->client->dev;
> + struct device_node *i2c_mux;
> + struct device_node *node = NULL;
> + unsigned int i2c_mux_mask = 0;
> +
> + of_node_get(dev->of_node);
Why this is needed?
> + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
> + if (!i2c_mux) {
> + dev_err(dev, "Failed to find i2c-mux node\n");
> + of_node_put(dev->of_node);
> + return -EINVAL;
> + }
> +
> + /* Identify which i2c-mux channels are enabled */
> + for_each_child_of_node(i2c_mux, node) {
> + u32 id = 0;
> +
> + of_property_read_u32(node, "reg", &id);
> + if (id >= MAX9286_NUM_GMSL)
> + continue;
> +
> + if (!of_device_is_available(node)) {
> + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
> + continue;
> + }
> +
> + i2c_mux_mask |= BIT(id);
> + }
> + of_node_put(node);
> + of_node_put(i2c_mux);
> +
> + /* Parse the endpoints */
> + for_each_endpoint_of_node(dev->of_node, node) {
> + struct max9286_source *source;
> + struct of_endpoint ep;
> +
> + of_graph_parse_endpoint(node, &ep);
> + dev_dbg(dev, "Endpoint %pOF on port %d",
> + ep.local_node, ep.port);
> +
> + if (ep.port > MAX9286_NUM_GMSL) {
> + dev_err(dev, "Invalid endpoint %s on port %d",
> + of_node_full_name(ep.local_node), ep.port);
> + continue;
> + }
> +
> + /* For the source endpoint just parse the bus configuration. */
> + if (ep.port == MAX9286_SRC_PAD) {
> + struct v4l2_fwnode_endpoint vep = {
> + .bus_type = V4L2_MBUS_CSI2_DPHY
> + };
> + int ret;
> +
> + ret = v4l2_fwnode_endpoint_parse(
> + of_fwnode_handle(node), &vep);
> + if (ret) {
> + of_node_put(node);
> + of_node_put(dev->of_node);
> + return ret;
> + }
> +
> + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
> + dev_err(dev,
> + "Media bus %u type not supported\n",
> + vep.bus_type);
> + v4l2_fwnode_endpoint_free(&vep);
> + of_node_put(node);
> + of_node_put(dev->of_node);
> + return -EINVAL;
> + }
> +
> + priv->csi2_data_lanes =
> + vep.bus.mipi_csi2.num_data_lanes;
> + v4l2_fwnode_endpoint_free(&vep);
> +
> + continue;
> + }
> +
> + /* Skip if the corresponding GMSL link is unavailable. */
> + if (!(i2c_mux_mask & BIT(ep.port)))
> + continue;
> +
> + if (priv->sources[ep.port].fwnode) {
> + dev_err(dev,
> + "Multiple port endpoints are not supported: %d",
> + ep.port);
> +
> + continue;
> + }
> +
> + source = &priv->sources[ep.port];
> + source->fwnode = fwnode_graph_get_remote_endpoint(
> + of_fwnode_handle(node));
> + if (!source->fwnode) {
> + dev_err(dev,
> + "Endpoint %pOF has no remote endpoint connection\n",
> + ep.local_node);
> +
> + continue;
> + }
> +
> + priv->source_mask |= BIT(ep.port);
> + priv->nsources++;
> + }
> + of_node_put(node);
> + of_node_put(dev->of_node);
> +
> + priv->route_mask = priv->source_mask;
> +
> + return 0;
> +}
> +
[...]
> +static int max9286_remove(struct i2c_client *client)
> +{
> + struct max9286_priv *priv = i2c_get_clientdata(client);
> +
> + i2c_mux_del_adapters(priv->mux);
> +
> + max9286_v4l2_unregister(priv);
> +
> + regulator_disable(priv->regulator);
> +
> + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
Usual power down sequence is to pull the power down gpio low and then turn off
the regulators. This helps in clearing up the internal state machine properly.
Thanks,
Mani
> +
> + max9286_cleanup_dt(priv);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id max9286_dt_ids[] = {
> + { .compatible = "maxim,max9286" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, max9286_dt_ids);
> +
> +static struct i2c_driver max9286_i2c_driver = {
> + .driver = {
> + .name = "max9286",
> + .of_match_table = of_match_ptr(max9286_dt_ids),
> + },
> + .probe_new = max9286_probe,
> + .remove = max9286_remove,
> +};
> +
> +module_i2c_driver(max9286_i2c_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
> +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>
^ permalink raw reply
* Re: [PATCH 2/4] arm64: dts: qcom: msm8916: Add blsp_i2c5
From: Bjorn Andersson @ 2020-05-12 18:16 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Andy Gross, linux-arm-msm, devicetree, ~postmarketos/upstreaming
In-Reply-To: <20200426140642.204395-3-stephan@gerhold.net>
On Sun 26 Apr 07:06 PDT 2020, Stephan Gerhold wrote:
> MSM8916 has another I2C QUP controller that can be enabled on
> GPIO 18 and 19.
>
> Add blsp_i2c5 to msm8916.dtsi and disable it by default.
>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
> ---
> Most MSM8916 smartphones/tablets have a touchscreen
> connected to blsp_i2c5.
> ---
> arch/arm64/boot/dts/qcom/msm8916-pins.dtsi | 24 ++++++++++++++++++++++
> arch/arm64/boot/dts/qcom/msm8916.dtsi | 15 ++++++++++++++
> 2 files changed, 39 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> index b45fd12856ea..31886860766a 100644
> --- a/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8916-pins.dtsi
> @@ -334,6 +334,30 @@ pinconf {
> };
> };
>
> + i2c5_default: i2c5_default {
> + pinmux {
> + function = "blsp_i2c5";
> + pins = "gpio18", "gpio19";
> + };
> + pinconf {
> + pins = "gpio18", "gpio19";
> + drive-strength = <2>;
> + bias-disable;
> + };
> + };
> +
> + i2c5_sleep: i2c5_sleep {
> + pinmux {
> + function = "gpio";
> + pins = "gpio18", "gpio19";
> + };
> + pinconf {
> + pins = "gpio18", "gpio19";
> + drive-strength = <2>;
> + bias-disable;
> + };
> + };
> +
> i2c6_default: i2c6_default {
> pinmux {
> function = "blsp_i2c6";
> diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
> index 6ab7cabde370..57ab5573a2d2 100644
> --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
> @@ -657,6 +657,21 @@ blsp_i2c4: i2c@78b8000 {
> status = "disabled";
> };
>
> + blsp_i2c5: i2c@78b9000 {
> + compatible = "qcom,i2c-qup-v2.2.1";
> + reg = <0x078b9000 0x500>;
> + interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&gcc GCC_BLSP1_AHB_CLK>,
> + <&gcc GCC_BLSP1_QUP5_I2C_APPS_CLK>;
> + clock-names = "iface", "core";
> + pinctrl-names = "default", "sleep";
> + pinctrl-0 = <&i2c5_default>;
> + pinctrl-1 = <&i2c5_sleep>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + status = "disabled";
> + };
> +
> blsp_i2c6: i2c@78ba000 {
> compatible = "qcom,i2c-qup-v2.2.1";
> reg = <0x078ba000 0x500>;
> --
> 2.26.2
>
^ permalink raw reply
* [RFC v1 3/3] arm64: dts: qcom: sc7180: Add qfprom-efuse
From: Ravi Kumar Bokka @ 2020-05-12 18:18 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring
Cc: linux-kernel, devicetree, rnayak, saiprakash.ranjan, dhavalp,
mturney, sparate, c_rbokka, mkurumel, Ravi Kumar Bokka
In-Reply-To: <1589307480-27508-1-git-send-email-rbokka@codeaurora.org>
This patch adds device tree node for qfprom-efuse controller.
Signed-off-by: Ravi Kumar Bokka <rbokka@codeaurora.org>
---
arch/arm64/boot/dts/qcom/sc7180-idp.dts | 4 ++++
arch/arm64/boot/dts/qcom/sc7180.dtsi | 9 +++++++++
2 files changed, 13 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sc7180-idp.dts b/arch/arm64/boot/dts/qcom/sc7180-idp.dts
index 4dd8ebc..7378c82 100644
--- a/arch/arm64/boot/dts/qcom/sc7180-idp.dts
+++ b/arch/arm64/boot/dts/qcom/sc7180-idp.dts
@@ -234,6 +234,10 @@
};
};
+&qfprom_efuse {
+ vcc-supply = <&vreg_l11a_1p8>;
+};
+
&qspi {
status = "okay";
pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi
index 4216b57..bbd22fb 100644
--- a/arch/arm64/boot/dts/qcom/sc7180.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi
@@ -457,6 +457,15 @@
#power-domain-cells = <1>;
};
+ qfprom_efuse: efuse@780000 {
+ compatible = "qcom,sc7180-qfprom-efuse";
+ reg = <0 0x00780000 0 0x100>,
+ <0 0x00780120 0 0x7a0>,
+ <0 0x00782000 0 0x100>;
+ clocks = <&gcc GCC_SEC_CTRL_CLK_SRC>;
+ clock-names = "secclk";
+ };
+
qfprom@784000 {
compatible = "qcom,qfprom";
reg = <0 0x00784000 0 0x8ff>;
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by the Linux Foundation.
^ permalink raw reply related
* [RFC v1 2/3] drivers: nvmem: Add driver for QTI qfprom-efuse support
From: Ravi Kumar Bokka @ 2020-05-12 18:17 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring
Cc: linux-kernel, devicetree, rnayak, saiprakash.ranjan, dhavalp,
mturney, sparate, c_rbokka, mkurumel, Ravi Kumar Bokka
In-Reply-To: <1589307480-27508-1-git-send-email-rbokka@codeaurora.org>
This patch adds new driver for QTI qfprom-efuse controller. This driver can
access the raw qfprom regions for fuse blowing.
The current existed qfprom driver is only supports for cpufreq, thermal sensors
drivers by read out calibration data, speed bins..etc which is stored
by qfprom efuses.
Signed-off-by: Ravi Kumar Bokka <rbokka@codeaurora.org>
---
drivers/nvmem/Kconfig | 10 +
drivers/nvmem/Makefile | 2 +
drivers/nvmem/qfprom-efuse.c | 476 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 488 insertions(+)
create mode 100644 drivers/nvmem/qfprom-efuse.c
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index d7b7f6d..c9345c5 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -121,6 +121,16 @@ config QCOM_QFPROM
This driver can also be built as a module. If so, the module
will be called nvmem_qfprom.
+config QTI_QFPROM_EFUSE
+ tristate "QTI_QFPROM_EFUSE Support"
+ depends on HAS_IOMEM
+ help
+ Say y here to enable QFPROM-Efuse support. This driver provides access
+ QTI qfprom efuse via nvmem interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_qfprom-efuse.
+
config NVMEM_SPMI_SDAM
tristate "SPMI SDAM Support"
depends on SPMI
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index a7c3772..1d8fe43 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o
nvmem_mtk-efuse-y := mtk-efuse.o
obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o
nvmem_qfprom-y := qfprom.o
+obj-$(CONFIG_QTI_QFPROM_EFUSE) += nvmem_qfprom-efuse.o
+nvmem_qfprom-efuse-y := qfprom-efuse.o
obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o
nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o
obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
diff --git a/drivers/nvmem/qfprom-efuse.c b/drivers/nvmem/qfprom-efuse.c
new file mode 100644
index 0000000..2e3c275
--- /dev/null
+++ b/drivers/nvmem/qfprom-efuse.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define QFPROM_BLOW_STATUS_BUSY 0x1
+#define QFPROM_BLOW_STATUS_READY 0x0
+
+/* Blow timer clock frequency in Mhz for 10nm LPe technology */
+#define QFPROM_BLOW_TIMER_OFFSET 0x03c
+#define QFPROM_BLOW_TIMER_RESET_VALUE 0x0
+
+/* Amount of time required to hold charge to blow fuse in micro-seconds */
+#define QFPROM_FUSE_BLOW_POLL_PERIOD 100
+#define QFPROM_BLOW_STATUS_OFFSET 0x048
+
+#define QFPROM_ACCEL_OFFSET 0x044
+
+/**
+ * struct qfprom_efuse_platform_data - structure holding qfprom-efuse
+ * platform data
+ *
+ * @name: qfprom-efuse compatible name
+ * @fuse_blow_time_in_us: Should contain the wait time when doing the fuse blow
+ * @accel_value: Should contain qfprom accel value
+ * @accel_reset_value: The reset value of qfprom accel value
+ * @qfprom_blow_timer_value: The timer value of qfprom when doing efuse blow
+ * @qfprom_blow_reset_freq: The frequency required to set when fuse blowing
+ * is done
+ * @qfprom_blow_set_freq: The frequency required to set when we start the
+ * fuse blowing
+ * @qfprom_max_vol: max voltage required to set fuse blow
+ * @qfprom_min_vol: min voltage required to set fuse blow
+ */
+struct qfprom_efuse_platform_data {
+ const char *name;
+ u8 fuse_blow_time_in_us;
+ u32 accel_value;
+ u32 accel_reset_value;
+ u32 qfprom_blow_timer_value;
+ u32 qfprom_blow_reset_freq;
+ u32 qfprom_blow_set_freq;
+ u32 qfprom_max_vol;
+ u32 qfprom_min_vol;
+};
+
+/**
+ * struct qfprom_efuse_priv - structure holding qfprom-efuse attributes
+ *
+ * @qfpbase: iomapped memory space for qfprom base
+ * @qfpraw: iomapped memory space for qfprom raw fuse region
+ * @qfpmap: iomapped memory space for qfprom fuse blow timer
+
+ * @dev: qfprom device structure
+ * @secclk: clock supply
+ * @vcc: regulator supply
+
+ * @qfpraw_start: qfprom raw fuse start region
+ * @qfpraw_end: qfprom raw fuse end region
+ * @qfprom_efuse_platform_data: qfprom platform data
+ */
+struct qfprom_efuse_priv {
+ void __iomem *qfpbase;
+ void __iomem *qfpraw;
+ void __iomem *qfpmap;
+ struct device *dev;
+ struct clk *secclk;
+ struct regulator *vcc;
+ resource_size_t qfpraw_start;
+ resource_size_t qfpraw_end;
+ struct qfprom_efuse_platform_data efuse;
+};
+
+/*
+ * restore the gcc_sec_ctrl_clk frequency to default value(19.2 MHz)
+ */
+static int qfprom_reset_clock_settings(const struct qfprom_efuse_priv *priv)
+{
+ int ret;
+
+ ret = clk_set_rate(priv->secclk, priv->efuse.qfprom_blow_reset_freq);
+ if (ret) {
+ dev_err(priv->dev, "clk_set_rate() failed to enable secclk\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * set the gcc_sec_ctrl_clk to 4.8 MHz
+ */
+static int qfprom_set_clock_settings(const struct qfprom_efuse_priv *priv)
+{
+ int ret;
+
+ ret = clk_set_rate(priv->secclk, priv->efuse.qfprom_blow_set_freq);
+
+ if (ret) {
+ dev_err(priv->dev, "clk_set_rate() failed to enable secclk\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * set and reset the voltage for 1.8V and OFF(0V) on VDD_QFPROM (LDO11)
+ */
+static int qfprom_set_voltage_settings(const struct qfprom_efuse_priv *priv,
+ int min_uV, int max_uV)
+{
+ int ret;
+
+ ret = regulator_set_voltage(priv->vcc, min_uV, max_uV);
+
+ if (ret) {
+ dev_err(priv->dev, "regulator_set_voltage() failed!\n");
+ return ret;
+ }
+
+ ret = regulator_enable(priv->vcc);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable regulator\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * resets the value of the blow timer, accel register and the clock
+ * and voltage settings
+ */
+static int qfprom_disable_fuse_blowing(const struct qfprom_efuse_priv *priv)
+{
+ int ret;
+
+ ret = qfprom_set_voltage_settings(priv, 0, priv->efuse.qfprom_max_vol);
+ if (ret) {
+ dev_err(priv->dev, "qfprom_set_voltage_settings failed\n");
+ return ret;
+ }
+
+ ret = qfprom_reset_clock_settings(priv);
+ if (ret) {
+ dev_err(priv->dev, "qfprom_reset_clock_settings failed\n");
+ return ret;
+ }
+
+ writel(QFPROM_BLOW_TIMER_RESET_VALUE, priv->qfpmap +
+ QFPROM_BLOW_TIMER_OFFSET);
+ writel(priv->efuse.accel_reset_value,
+ priv->qfpmap + QFPROM_ACCEL_OFFSET);
+
+ return 0;
+}
+
+/*
+ * sets the value of the blow timer, accel register and the clock
+ * and voltage settings
+ */
+static int qfprom_enable_fuse_blowing(const struct qfprom_efuse_priv *priv)
+{
+ int ret;
+
+ ret = qfprom_disable_fuse_blowing(priv);
+ if (ret) {
+ dev_err(priv->dev, "qfprom_disable_fuse_blowing()\n");
+ return ret;
+ }
+
+ writel(priv->efuse.qfprom_blow_timer_value, priv->qfpmap +
+ QFPROM_BLOW_TIMER_OFFSET);
+ writel(priv->efuse.accel_value, priv->qfpmap + QFPROM_ACCEL_OFFSET);
+
+ ret = qfprom_set_clock_settings(priv);
+ if (ret) {
+ dev_err(priv->dev, "qpfrom_set_clock_settings()\n");
+ return ret;
+ }
+
+ ret = qfprom_set_voltage_settings(priv, priv->efuse.qfprom_min_vol,
+ priv->efuse.qfprom_max_vol);
+ if (ret) {
+ dev_err(priv->dev, "qfprom_set_voltage_settings()\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * verifying to make sure address being written or read is from qfprom
+ * raw address range
+ */
+bool addr_in_qfprom_range(const struct qfprom_efuse_priv *priv, u32 reg,
+ size_t bytes)
+{
+ if (((reg + bytes) > reg) && (reg >= priv->qfpraw_start) &&
+ ((reg + bytes) <= priv->qfpraw_end)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * API for reading from raw qfprom region
+ */
+static int qfprom_efuse_reg_read(void *context, unsigned int reg, void *_val,
+ size_t bytes)
+{
+ struct qfprom_efuse_priv *priv = context;
+ u32 *value = _val;
+ u32 align_check;
+ int i = 0, words = bytes / 4;
+
+ dev_info(priv->dev,
+ "reading raw qfprom region offset: 0x%08x of size: %zd\n",
+ reg, bytes);
+
+ if (bytes % 4 != 0x00) {
+ dev_err(priv->dev,
+ "Bytes: %zd to read should be word align\n",
+ bytes);
+ return -EINVAL;
+ }
+
+ if (!addr_in_qfprom_range(priv, reg, bytes)) {
+ dev_err(priv->dev,
+ "Invalid qfprom raw region offset 0x%08x & bytes %zd\n",
+ reg, bytes);
+ return -EINVAL;
+ }
+
+ align_check = (reg & 0xF);
+
+ if (((align_check & ~3) == align_check) && value != NULL)
+ while (words--)
+ *value++ = readl(priv->qfpbase + reg + (i++ * 4));
+
+ else
+ dev_err(priv->dev,
+ "Invalid input parameter 0x%08x fuse blow address\n",
+ reg);
+
+ return 0;
+}
+
+/*
+ * API for writing to raw qfprom region - fuse blowing
+ * returns success or failure code as per the conditions
+ */
+static int qfprom_efuse_reg_write(void *context, unsigned int reg, void *_val,
+ size_t bytes)
+{
+ struct qfprom_efuse_priv *priv = context;
+ u32 *value = _val;
+ u32 align_check;
+ u32 blow_status = QFPROM_BLOW_STATUS_BUSY;
+ int ret;
+ int i = 0, words = bytes / 4;
+
+ dev_info(priv->dev,
+ "writing to raw qfprom region : 0x%08x of size: %zd\n",
+ reg, bytes);
+
+ if (bytes % 4 != 0x00) {
+ dev_err(priv->dev, "Bytes: %zd should be word align\n", bytes);
+ return -EINVAL;
+ }
+
+ if (!addr_in_qfprom_range(priv, reg, bytes)) {
+ dev_err(priv->dev,
+ "Invalid qfprom raw region offset 0x%08x & bytes %zd\n",
+ reg, bytes);
+ return -EINVAL;
+ }
+
+ align_check = (reg & 0xF);
+ if (value && ((align_check & ~3) == align_check)) {
+ ret = qfprom_enable_fuse_blowing(priv);
+ if (ret) {
+ dev_err(priv->dev, "qfprom_enable_fuse_blowing");
+ return ret;
+ }
+
+ ret = readl_relaxed_poll_timeout(priv->qfpmap +
+ QFPROM_BLOW_STATUS_OFFSET, blow_status,
+ (blow_status != QFPROM_BLOW_STATUS_BUSY),
+ QFPROM_FUSE_BLOW_POLL_PERIOD,
+ priv->efuse.fuse_blow_time_in_us);
+
+ if (ret) {
+ dev_err(priv->dev, "Timeout blow status ready\n");
+ return ret;
+ }
+
+ if (blow_status == QFPROM_BLOW_STATUS_READY) {
+ while (words--)
+ writel(*value++,
+ priv->qfpbase + reg + (i++ * 4));
+
+ ret = readl_relaxed_poll_timeout(priv->qfpmap +
+ QFPROM_BLOW_STATUS_OFFSET, blow_status,
+ (blow_status != QFPROM_BLOW_STATUS_BUSY),
+ QFPROM_FUSE_BLOW_POLL_PERIOD,
+ priv->efuse.fuse_blow_time_in_us);
+
+ if (ret) {
+ dev_err(priv->dev, "Timeout blow-status ready");
+ return ret;
+ }
+ }
+
+ ret = qfprom_disable_fuse_blowing(priv);
+ if (ret)
+ return ret;
+ } else {
+ dev_err(priv->dev, "Invalid input parameter fuse blow address");
+ return -EINVAL;
+ }
+
+ dev_info(priv->dev, "written successfully raw qfprom region\n");
+
+ return 0;
+}
+
+static int qfprom_efuse_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *qfpbase, *qfpraw, *qfpmap;
+ struct nvmem_device *nvmem;
+ struct nvmem_config *econfig;
+ struct qfprom_efuse_priv *priv;
+ const struct qfprom_efuse_platform_data *drvdata;
+ int ret;
+
+ dev_info(&pdev->dev, "[%s]: Invoked\n", __func__);
+
+ drvdata = of_device_get_match_data(&pdev->dev);
+ if (!drvdata)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->efuse.fuse_blow_time_in_us = drvdata->fuse_blow_time_in_us;
+ priv->efuse.accel_value = drvdata->accel_value;
+ priv->efuse.accel_reset_value = drvdata->accel_reset_value;
+ priv->efuse.qfprom_blow_timer_value = drvdata->qfprom_blow_timer_value;
+ priv->efuse.qfprom_blow_reset_freq = drvdata->qfprom_blow_reset_freq;
+ priv->efuse.qfprom_blow_set_freq = drvdata->qfprom_blow_set_freq;
+ priv->efuse.qfprom_max_vol = drvdata->qfprom_max_vol;
+ priv->efuse.qfprom_min_vol = drvdata->qfprom_min_vol;
+ priv->dev = dev;
+
+ qfpbase = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ priv->qfpbase = devm_ioremap_resource(dev, qfpbase);
+ if (IS_ERR(priv->qfpbase)) {
+ ret = PTR_ERR(priv->qfpbase);
+ goto err;
+ }
+
+ qfpraw = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ priv->qfpraw = devm_ioremap_resource(dev, qfpraw);
+ if (IS_ERR(priv->qfpraw)) {
+ ret = PTR_ERR(priv->qfpraw);
+ goto err;
+ }
+
+ priv->qfpraw_start = qfpraw->start - qfpbase->start;
+ priv->qfpraw_end = qfpraw->end - qfpbase->start;
+
+ qfpmap = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+
+ priv->qfpmap = devm_ioremap_resource(dev, qfpmap);
+ if (IS_ERR(priv->qfpmap)) {
+ ret = PTR_ERR(priv->qfpmap);
+ goto err;
+ }
+
+ priv->vcc = devm_regulator_get(&pdev->dev, "vcc");
+ if (IS_ERR(priv->vcc)) {
+ ret = PTR_ERR(priv->vcc);
+ if (ret == -ENODEV)
+ ret = -EPROBE_DEFER;
+
+ goto err;
+ }
+
+ priv->secclk = devm_clk_get(dev, "secclk");
+ if (IS_ERR(priv->secclk)) {
+ ret = PTR_ERR(priv->secclk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "secclk error getting : %d\n", ret);
+ goto err;
+ }
+
+ ret = clk_prepare_enable(priv->secclk);
+ if (ret) {
+ dev_err(dev, "clk_prepare_enable() failed\n");
+ goto err;
+ }
+
+ econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
+ if (!econfig)
+ return -ENOMEM;
+
+ econfig->dev = dev;
+ econfig->name = "qfprom-efuse";
+ econfig->stride = 1;
+ econfig->word_size = 1;
+ econfig->reg_read = qfprom_efuse_reg_read;
+ econfig->reg_write = qfprom_efuse_reg_write;
+ econfig->size = resource_size(qfpraw);
+ econfig->priv = priv;
+
+ nvmem = devm_nvmem_register(dev, econfig);
+
+ return PTR_ERR_OR_ZERO(nvmem);
+
+err:
+ clk_disable_unprepare(priv->secclk);
+ return ret;
+}
+
+static const struct qfprom_efuse_platform_data sc7180_qfp_efuse_data = {
+ .name = "sc7180-qfprom-efuse",
+ .fuse_blow_time_in_us = 10,
+ .accel_value = 0xD10,
+ .accel_reset_value = 0x800,
+ .qfprom_blow_timer_value = 25,
+ .qfprom_blow_reset_freq = 19200000,
+ .qfprom_blow_set_freq = 4800000,
+ .qfprom_max_vol = 1904000,
+ .qfprom_min_vol = 1800000,
+};
+
+static const struct of_device_id qfprom_efuse_of_match[] = {
+ {
+ .compatible = "qcom,sc7180-qfprom-efuse",
+ .data = &sc7180_qfp_efuse_data
+ },
+ {/* sentinel */},
+};
+
+MODULE_DEVICE_TABLE(of, qfprom_efuse_of_match);
+
+static struct platform_driver qfprom_efuse_driver = {
+ .probe = qfprom_efuse_probe,
+ .driver = {
+ .name = "sc7180-qfprom-efuse",
+ .of_match_table = qfprom_efuse_of_match,
+ },
+};
+
+module_platform_driver(qfprom_efuse_driver);
+MODULE_DESCRIPTION("QTI QFPROM Efuse driver");
+MODULE_LICENSE("GPL v2");
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by the Linux Foundation.
^ permalink raw reply related
* [RFC v1 1/3] dt-bindings: nvmem: Add devicetree bindings for qfprom-efuse
From: Ravi Kumar Bokka @ 2020-05-12 18:17 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring
Cc: linux-kernel, devicetree, rnayak, saiprakash.ranjan, dhavalp,
mturney, sparate, c_rbokka, mkurumel, Ravi Kumar Bokka
In-Reply-To: <1589307480-27508-1-git-send-email-rbokka@codeaurora.org>
This patch adds dt-bindings document for qfprom-efuse controller.
Signed-off-by: Ravi Kumar Bokka <rbokka@codeaurora.org>
---
.../devicetree/bindings/nvmem/qfprom-efuse.yaml | 40 ++++++++++++++++++++++
1 file changed, 40 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/qfprom-efuse.yaml
diff --git a/Documentation/devicetree/bindings/nvmem/qfprom-efuse.yaml b/Documentation/devicetree/bindings/nvmem/qfprom-efuse.yaml
new file mode 100644
index 0000000..d262c99
--- /dev/null
+++ b/Documentation/devicetree/bindings/nvmem/qfprom-efuse.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nvmem/qfprom-efuse.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies Inc, QFPROM Efuse bindings
+
+maintainers:
+ - Ravi Kumar Bokka <rbokka@codeaurora.org>
+
+allOf:
+ - $ref: "nvmem.yaml#"
+
+properties:
+ compatible:
+ enum:
+ - qcom,sc7180-qfprom-efuse
+
+ reg:
+ maxItems: 3
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-sc7180.h>
+
+ efuse@780000 {
+ compatible = "qcom,sc7180-qfprom-efuse";
+ reg = <0 0x00780000 0 0x100>,
+ <0 0x00780120 0 0x7a0>,
+ <0 0x00782000 0 0x100>;
+ clocks = <&gcc GCC_SEC_CTRL_CLK_SRC>;
+ clock-names = "secclk";
+ };
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by the Linux Foundation.
^ permalink raw reply related
* [RFC v1 0/3] Add QTI QFPROM-Efuse driver support
From: Ravi Kumar Bokka @ 2020-05-12 18:17 UTC (permalink / raw)
To: Srinivas Kandagatla, Rob Herring
Cc: linux-kernel, devicetree, rnayak, saiprakash.ranjan, dhavalp,
mturney, sparate, c_rbokka, mkurumel, Ravi Kumar Bokka
This patch series adds qfprom-efuse controller driver support.
This driver can access the raw qfprom regions for fuse blowing.
The current existed qfprom driver is only supports for cpufreq, thermal sensors
drivers by read out calibration data, speed bins..etc which is stored by
qfprom efuses.
Ravi Kumar Bokka (3):
dt-bindings: nvmem: Add devicetree bindings for qfprom-efuse
drivers: nvmem: Add driver for QTI qfprom-efuse support
arm64: dts: qcom: sc7180: Add qfprom-efuse
.../devicetree/bindings/nvmem/qfprom-efuse.yaml | 40 ++
arch/arm64/boot/dts/qcom/sc7180-idp.dts | 4 +
arch/arm64/boot/dts/qcom/sc7180.dtsi | 9 +
drivers/nvmem/Kconfig | 10 +
drivers/nvmem/Makefile | 2 +
drivers/nvmem/qfprom-efuse.c | 476 +++++++++++++++++++++
6 files changed, 541 insertions(+)
create mode 100644 Documentation/devicetree/bindings/nvmem/qfprom-efuse.yaml
create mode 100644 drivers/nvmem/qfprom-efuse.c
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by the Linux Foundation.
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
From: Serge Semin @ 2020-05-12 18:25 UTC (permalink / raw)
To: Rob Herring
Cc: Serge Semin, Thomas Bogendoerfer, Greg Kroah-Hartman,
Arnd Bergmann, Alexey Malahov, Paul Burton, Ralf Baechle,
Olof Johansson, Boris Brezillon, Paul Cercueil, Thomas Gleixner,
Masahiro Yamada, open list:MIPS, SoC Team, devicetree,
linux-kernel@vger.kernel.org
In-Reply-To: <CAL_Jsq+rka7hXVy46=O3pTrN0DLCAQQ=TSpSqzoB2dxb30h66g@mail.gmail.com>
On Mon, May 11, 2020 at 05:43:58PM -0500, Rob Herring wrote:
> On Thu, May 7, 2020 at 6:07 PM Serge Semin
> <Sergey.Semin@baikalelectronics.ru> wrote:
> >
> > There is a single register provided by the SoC system controller,
> > which can be used to tune the L2-cache RAM up. It only provides a way
> > to change the L2-RAM access latencies. So aside from "be,bt1-l2-ctl"
> > compatible string the device node can be optionally equipped with the
> > properties of Tag/Data/WS latencies.
> >
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > Cc: Paul Burton <paulburton@kernel.org>
> > Cc: Ralf Baechle <ralf@linux-mips.org>
> > Cc: Olof Johansson <olof@lixom.net>
> > Cc: Boris Brezillon <bbrezillon@kernel.org>
> > Cc: Paul Cercueil <paul@crapouillou.net>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
> > Cc: linux-mips@vger.kernel.org
> > Cc: soc@kernel.org
> >
> > ---
> >
> > Changelog v2:
> > - Move driver to the memory subsystem.
> > - Use dual GPL/BSD license.
> > - Use single lined copyright header.
> > - Move "allOf" restrictions to the root level of the properties.
> > - Discard syscon compatible string and reg property.
> > - The DT node is supposed to be a child of the Baikal-T1 system controller
> > node.
> > ---
> > .../memory-controllers/baikal,bt1-l2-ctl.yaml | 59 +++++++++++++++++++
> > 1 file changed, 59 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
> > new file mode 100644
> > index 000000000000..263f0cdab4e6
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
> > @@ -0,0 +1,59 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/memory-controllers/baikal,bt1-l2-ctl.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Baikal-T1 L2-cache Control Block
> > +
> > +maintainers:
> > + - Serge Semin <fancer.lancer@gmail.com>
> > +
> > +description: |
> > + By means of the System Controller Baikal-T1 SoC exposes a few settings to
> > + tune the MIPS P5600 CM2 L2 cache performance up. In particular it's possible
> > + to change the Tag, Data and Way-select RAM access latencies. Baikal-T1
> > + L2-cache controller block is responsible for the tuning. Its DT node is
> > + supposed to be a child of the system controller.
>
> Is there a register range for just the L2 registers in the system
> controller. If so, please add a 'reg' property.
It's just a single register, though almost fully dedicated for this feature.
Should I add the reg property anyway? Since you touched this topic, aside from
this l2-control block the system controller has also got sub-blocks of PLLs, clock
dividers, reboot, reboot-mode and indirectly addressed i2c in the same MMIO space.
These blocks all have got a dedicated registers range within the syscon regmap
space. Shall I add an optional reg property for them too? If so shall their node
names to be in the regexp-format like "^name(@[0-9a-f]+)?" ?
>
> This should all be part of the system controller schema either as 1
> file or by a $ref from the system controller to this file. That's how
> we ensure "supposed to be a child of the system controller".
Oh, that's clever solution. I was thinking of how to signify this parent-child
dependency. I'll add the $ref in the corresponding properties of the system
controller. So this DT schema should live here, separately from the syscon DT
node. Thanks for the note.
>
> > +
> > +properties:
> > + compatible:
> > + const: baikal,bt1-l2-ctl
> > +
> > + baikal,l2-ws-latency:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: Cycles of latency for Way-select RAM accesses
> > + default: 0
> > + minimum: 0
> > + maximum: 3
> > +
> > + baikal,l2-tag-latency:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: Cycles of latency for Tag RAM accesses
> > + default: 0
> > + minimum: 0
> > + maximum: 3
> > +
> > + baikal,l2-data-latency:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: Cycles of latency for Data RAM accesses
> > + default: 1
> > + minimum: 0
> > + maximum: 3
> > +
> > +additionalProperties: false
> > +
> > +required:
> > + - compatible
> > +
> > +examples:
> > + - |
> > + l2_ctl {
> > + compatible = "baikal,bt1-l2-ctl";
> > +
> > + baikal,l2-ws-latency = <0>;
> > + baikal,l2-tag-latency = <0>;
>
> 0 is the default, why list it?
1 is the default for the l2-data-latency too. Why not? It's just an
example.
-Sergey
>
> > + baikal,l2-data-latency = <1>;
> > + };
> > +...
> > --
> > 2.25.1
> >
^ permalink raw reply
* Re: [PATCH 3/4] arm64: dts: qcom: msm8916-samsung-a2015: Add touchscreen regulator
From: Bjorn Andersson @ 2020-05-12 18:29 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Andy Gross, linux-arm-msm, devicetree, ~postmarketos/upstreaming,
Michael Srba
In-Reply-To: <20200426140642.204395-4-stephan@gerhold.net>
On Sun 26 Apr 07:06 PDT 2020, Stephan Gerhold wrote:
> A3U and A5U both use an extra touchscreen LDO regulator
> that provides 3.3V for the touch screen controller.
>
> Add it as fixed regulator to the common include.
>
> Cc: Michael Srba <Michael.Srba@seznam.cz>
> Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
> ---
> .../qcom/msm8916-samsung-a2015-common.dtsi | 25 +++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-a2015-common.dtsi b/arch/arm64/boot/dts/qcom/msm8916-samsung-a2015-common.dtsi
> index 43c5e0f882f1..67a286422a13 100644
> --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-a2015-common.dtsi
> +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-a2015-common.dtsi
> @@ -138,6 +138,19 @@ hall-sensor {
> };
> };
>
> + reg_vdd_tsp: regulator-vdd-tsp {
> + compatible = "regulator-fixed";
> + regulator-name = "vdd_tsp";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> +
> + gpio = <&msmgpio 73 GPIO_ACTIVE_HIGH>;
> + enable-active-high;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&tsp_en_default>;
> + };
> +
> i2c-muic {
> compatible = "i2c-gpio";
> sda-gpios = <&msmgpio 105 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
> @@ -195,6 +208,18 @@ pinconf {
> bias-disable;
> };
> };
> +
> + tsp_en_default: tsp_en_default {
Node names should not include '_', so this line should be
tsp_en_default: tsp-en-default {
I fixed this up in both patch 3 and 4 and applied the series.
Thanks,
Bjorn
> + pinmux {
> + function = "gpio";
> + pins = "gpio73";
> + };
> + pinconf {
> + pins = "gpio73";
> + drive-strength = <2>;
> + bias-disable;
> + };
> + };
> };
>
> &smd_rpm_regulators {
> --
> 2.26.2
>
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
From: Serge Semin @ 2020-05-12 18:31 UTC (permalink / raw)
To: Rob Herring
Cc: Serge Semin, Paul Cercueil, Masahiro Yamada, SoC Team,
Greg Kroah-Hartman, Thomas Gleixner, Olof Johansson,
linux-kernel@vger.kernel.org, Arnd Bergmann, Thomas Bogendoerfer,
Alexey Malahov, Boris Brezillon, Paul Burton, devicetree,
Ralf Baechle, open list:MIPS
In-Reply-To: <CAL_JsqJEZF5xkNGGiBy5rgUFfg=hWp6qi=tzFW84cGD9m-p5SA@mail.gmail.com>
On Mon, May 11, 2020 at 05:37:41PM -0500, Rob Herring wrote:
> On Mon, May 11, 2020 at 3:25 PM Serge Semin
> <Sergey.Semin@baikalelectronics.ru> wrote:
> >
> > On Mon, May 11, 2020 at 10:38:04AM -0500, Rob Herring wrote:
> > > On Fri, 8 May 2020 02:07:03 +0300, Serge Semin wrote:
> > > > There is a single register provided by the SoC system controller,
> > > > which can be used to tune the L2-cache RAM up. It only provides a way
> > > > to change the L2-RAM access latencies. So aside from "be,bt1-l2-ctl"
> > > > compatible string the device node can be optionally equipped with the
> > > > properties of Tag/Data/WS latencies.
> > > >
> > > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > > Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > > > Cc: Paul Burton <paulburton@kernel.org>
> > > > Cc: Ralf Baechle <ralf@linux-mips.org>
> > > > Cc: Olof Johansson <olof@lixom.net>
> > > > Cc: Boris Brezillon <bbrezillon@kernel.org>
> > > > Cc: Paul Cercueil <paul@crapouillou.net>
> > > > Cc: Thomas Gleixner <tglx@linutronix.de>
> > > > Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > Cc: linux-mips@vger.kernel.org
> > > > Cc: soc@kernel.org
> > > >
> > > > ---
> > > >
> > > > Changelog v2:
> > > > - Move driver to the memory subsystem.
> > > > - Use dual GPL/BSD license.
> > > > - Use single lined copyright header.
> > > > - Move "allOf" restrictions to the root level of the properties.
> > > > - Discard syscon compatible string and reg property.
> > > > - The DT node is supposed to be a child of the Baikal-T1 system controller
> > > > node.
> > > > ---
> > > > .../memory-controllers/baikal,bt1-l2-ctl.yaml | 59 +++++++++++++++++++
> > > > 1 file changed, 59 insertions(+)
> > > > create mode 100644 Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
> > > >
> > >
> > >
> > > My bot found errors running 'make dt_binding_check' on your patch:
> > >
> > > [nip] ...
> > >
> > > See https://patchwork.ozlabs.org/patch/1285665
> > >
> > > If you already ran 'make dt_binding_check' and didn't see the above
> > > error(s), then make sure dt-schema is up to date:
> > >
> > > pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
> > >
> > > Please check and re-submit.
> > >
> >
> > The problem is due to an absent vendor prefix in the test kernel source tree
> > environment. As I said in the cover-letter the new vendor prefix will be added
> > in the framework of the next patchset:
> > https://lkml.org/lkml/2020/5/6/1047
> >
> > Rob, please review that patchset first, merge in the corresponding patch from
> > there and test this binding out then.
>
> Did you read the part about a 'bot'? My bot doesn't read cover letters
> and I only occasionally do. Do you want to write me a script that can
> do this?
A script, that would read a cover-letter for you? =)
Anyway I wasn't talking to the bot, but to you explaining why the problem
happened and of how to solve it. I didn't mean to blame you or the 'bot'
for not reading the letter. Sorry, if it seemed like I did.
-Sergey
>
> Rob
^ permalink raw reply
* Re: [PATCH v3 0/6] allow ramoops to collect all kmesg_dump events
From: Kees Cook @ 2020-05-12 18:45 UTC (permalink / raw)
To: Petr Mladek
Cc: Pavel Tatashin, Anton Vorontsov, Colin Cross, Tony Luck,
Jonathan Corbet, Rob Herring, Benson Leung,
Enric Balletbo i Serra, Sergey Senozhatsky, Steven Rostedt,
James Morris, Sasha Levin, Linux Doc Mailing List, LKML,
devicetree
In-Reply-To: <20200512155207.GF17734@linux-b0ei>
On Tue, May 12, 2020 at 05:52:07PM +0200, Petr Mladek wrote:
> On Tue 2020-05-12 10:03:44, Pavel Tatashin wrote:
> > > OK, I personally see this as two separate problems:
> > >
> > > 1. Missing support to set loglevel per console.
> > > 2. Missing support to dump messages for other reasons.
> > >
> > > I would remove the paragraph about console log levels completely.
> >
> > OK, I see your point, this paragraph can be removed, however, I think
> > it makes it clear to understand the rationale for this change. As I
> > understand, the per console loglevel has been proposed but were never
> > accepted.
I understood Pavel's rationale as an output from my questions in the v1
series, that went like this, paraphrased:
Pavel: "I need to have other kmsg dump reasons available to pstore."
Kees: "Why can't you just use the pstore console dumper?"
Pavel: "It's too much for the slow device; we only need to know about
specific events that are already provided by kmsg dump."
Kees: "Ah! Sounds good, max_reasons it is."
So, AIUI, loglevel remains orthogonal to this, and it's my fault for
even causing to be be brought up. Please disregard! :)
> > printk.always_kmsg_dump is not working for me because ramoops has its
> > own filtering based on dump_oops boolean, and ignores everything but
> > panics and conditionally oops.
> > max_reason makes the ramoops internal logic cleaner compared to using dump_oops.
>
> I see. Just to be sure. Is the main reason to add max_reason parameter
> to keep complatibility of the deprecated dump_oops parameter? Or is
> there any real use case for this granularity?
In my mind it seemed like a nice mapping, so it was an easy port.
> I wonder if anyone is actually using the ramoops.dump_oops parameter
> in reality. I would personally make it deprecated and change the
> default behavior to work according to printk.always_kmsg_dump parameter.
Yes. For things I'm aware of: ARM devices with very tiny persistent RAM
were using ramoops and setting dump_oops to 0 (specifically, setting
the DT "no-dump-oops" to 1), and larger Android and Chrome OS devices
using ramoops were setting to dump_oops to 1[1].
The logic built into pstore recognizes a difference between panic and
non-panic dumps as well, as the expectation is that there is little to
no kernel infrastructure available for use during a panic kmsg.
> IMHO, ramoops.dump_oops just increases complexity and should not have
> been introduced at all. I would try hard to avoid introducing even bigger
> complecity and mess.
I think dump_oops was the wrong implementation, but granularity control
is still needed. It is an old parameter, and is baked into many device
trees on systems, so I can't just drop it. (In fact, I've had to support
some other DT compat issues[2] as well.)
> I know that there is the "do not break existing userspace" rule. The
> question is if there is any user and if it is worth it.
For dump_oops, yes, there is unfortunately.
> > I agree, the reasons in kmsg_dump_reason do not order well (I
> > actually want to add another reason for kexec type reboots, and where
> > do I put it?), so how about if we change the ordering list to
> > bitfield/flags, and instead of max_reason provide: "reasons" bitset?
>
> It looks too complicated. I would really try hard to avoid the
> parameter at all.
Here are the problems I see being solved by this:
- lifting kmsg dump reason filtering out of the individual pstore
backends and making it part of the "infrastructure", so that
there is a central place to set expectations. Right now there
is a mix of explicit and implicit kmsg dump handling:
- arch/powerpc/kernel/nvram_64.c has a hard-coded list
- drivers/firmware/efi/efi-pstore.c doesn't expect anything but
OOPS and PANIC.
- drivers/mtd/mtdoops.c tries to filter using its own dump_oops
and doesn't expect anything but OOPS and PANIC.
- fs/pstore/ram.c: has a hard-coded list and uses its own
dump_oops.
- drivers/mtd/mtdpstore.c (under development[3]) expected only
OOPS and PANIC and had its own dump_oops.
- providing a way for backends that can deal with all kmsg dump reasons
to do so without breaking existing default behavior (i.e. getting
Pavel what he's interested in).
So, that said, I'm totally fine with a bit field. I just need a way to
map the kmsg dump reasons onto the existing backend expectations and to
have Pavel's needs addressed.
-Kees
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/platform/chrome/chromeos_pstore.c?h=v5.6#n60
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/pstore/ram.c?h=v5.6#n708
[3] https://lore.kernel.org/lkml/20200511233229.27745-11-keescook@chromium.org/
--
Kees Cook
^ permalink raw reply
* Re: [PATCH v3 0/6] allow ramoops to collect all kmesg_dump events
From: Kees Cook @ 2020-05-12 18:53 UTC (permalink / raw)
To: Pavel Tatashin
Cc: Petr Mladek, Anton Vorontsov, Colin Cross, Tony Luck,
Jonathan Corbet, Rob Herring, Benson Leung,
Enric Balletbo i Serra, Sergey Senozhatsky, Steven Rostedt,
James Morris, Sasha Levin, Linux Doc Mailing List, LKML,
devicetree
In-Reply-To: <CA+CK2bC0argMNHzynedpwN6ekOg8yypN03JvmAKGWQ5Aegxh+Q@mail.gmail.com>
On Tue, May 12, 2020 at 12:49:10PM -0400, Pavel Tatashin wrote:
> On Tue, May 12, 2020 at 11:52 AM Petr Mladek <pmladek@suse.com> wrote:
> > I wonder if anyone is actually using the ramoops.dump_oops parameter
> > in reality. I would personally make it deprecated and change the
> > default behavior to work according to printk.always_kmsg_dump parameter.
>
> This sounds alright to me with one slight problem. I am doing this
> work for an embedded arm64 SoC, so controlling everything via device
> tree is preferable compared to having some settings via device tree
> and others via kernel parameters, especially because the kernel
> parameters are hardcoded by firmware that we try not to update too
> often for uptime reasons.
I'm entirely convinced that this area of pstore needs to be cleaned up
and I want to have the pstore backends be able to declare their kmsg
dump reason filters in a configurable fashion. So at least on the pstore
end, I intend to have some way to do this.
> > IMHO, ramoops.dump_oops just increases complexity and should not have
> > been introduced at all. I would try hard to avoid introducing even bigger
> > complecity and mess.
>
> I agree, amoops.dump_oops should be depricated with or without
> max_reason change.
Yup. dump_oops will be deprecated in favor of whatever we settle on here.
> > I know that there is the "do not break existing userspace" rule. The
> > question is if there is any user and if it is worth it.
> >
> > > I agree, the reasons in kmsg_dump_reason do not order well (I
> > > actually want to add another reason for kexec type reboots, and where
> > > do I put it?), so how about if we change the ordering list to
> > > bitfield/flags, and instead of max_reason provide: "reasons" bitset?
> >
> > It looks too complicated. I would really try hard to avoid the
> > parameter at all.
>
> OK. Should we remove max_reason from struct kmsg_dumper and also
> remove the misleading comment about kmsg_dump_reason ordering?
I'm also fine with this. I can have pstore infrastructure doing the
filtering if kmsg dump doesn't want to. Given the existence of
printk.always_kmsg_dump, though, it seemed like it was better to have
kmsg dump do this filtering instead.
At this point my preference is to switch to a bit field -- I don't see a
reason for ordering. The only cases that remain "special" appear to be
PANIC and EMERG (which, again, aren't ordered adjacent).
-Kees
--
Kees Cook
^ permalink raw reply
* Re: [PATCH v2 5/6] dmaengine: dw: Introduce max burst length hw config
From: Andy Shevchenko @ 2020-05-12 19:12 UTC (permalink / raw)
To: Serge Semin
Cc: Serge Semin, Vinod Koul, Viresh Kumar, Dan Williams,
Alexey Malahov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Rob Herring, linux-mips, devicetree, dmaengine,
linux-kernel
In-Reply-To: <20200512140820.ssjv6pl7busqqi3t@mobilestation>
On Tue, May 12, 2020 at 05:08:20PM +0300, Serge Semin wrote:
> On Fri, May 08, 2020 at 02:41:53PM +0300, Andy Shevchenko wrote:
> > On Fri, May 08, 2020 at 01:53:03PM +0300, Serge Semin wrote:
> > > IP core of the DW DMA controller may be synthesized with different
> > > max burst length of the transfers per each channel. According to Synopsis
> > > having the fixed maximum burst transactions length may provide some
> > > performance gain. At the same time setting up the source and destination
> > > multi size exceeding the max burst length limitation may cause a serious
> > > problems. In our case the system just hangs up. In order to fix this
> > > lets introduce the max burst length platform config of the DW DMA
> > > controller device and don't let the DMA channels configuration code
> > > exceed the burst length hardware limitation. Depending on the IP core
> > > configuration the maximum value can vary from channel to channel.
> > > It can be detected either in runtime from the DWC parameter registers
> > > or from the dedicated dts property.
> >
> > I'm wondering what can be the scenario when your peripheral will ask something
> > which is not supported by DMA controller?
>
> I may misunderstood your statement, because seeing your activity around my
> patchsets including the SPI patchset and sometimes very helpful comments,
> this question answer seems too obvious to see you asking it.
>
> No need to go far for an example. See the DW APB SSI driver. Its DMA module
> specifies the burst length to be 16, while not all of ours channels supports it.
> Yes, originally it has been developed for the Intel Midfield SPI, but since I
> converted the driver into a generic code we can't use a fixed value. For instance
> in our hardware only two DMA channels of total 16 are capable of bursting up to
> 16 bytes (data items) at a time, the rest of them are limited with up to 4 bytes
> burst length. While there are two SPI interfaces, each of which need to have two
> DMA channels for communications. So I need four channels in total to allocate to
> provide the DMA capability for all interfaces. In order to set the SPI controller
> up with valid optimized parameters the max-burst-length is required. Otherwise we
> can end up with buffers overrun/underrun.
Right, and we come to the question which channel better to be used by SPI and
the rest devices. Without specific filter function you can easily get into a
case of inverted optimizations, when SPI got channels with burst = 4, while
it's needed 16, and other hardware otherwise. Performance wise it's worse
scenario which we may avoid in the first place, right?
> > Peripheral needs to supply a lot of configuration parameters specific to the
> > DMA controller in use (that's why we have struct dw_dma_slave).
> > So, seems to me the feasible approach is supply correct data in the first place.
>
> How to supply a valid data if clients don't know the DMA controller limitations
> in general?
This is a good question. DMA controllers are quite different and having unified
capabilities structure for all is almost impossible task to fulfil. That's why
custom filter function(s) can help here. Based on compatible string you can
implement whatever customized quirks like two functions, for example, to try 16
burst size first and fallback to 4 if none was previously found.
> > If you have specific channels to acquire then you probably need to provide a
> > custom xlate / filter functions. Because above seems a bit hackish workaround
> > of dynamic channel allocation mechanism.
>
> No, I don't have a specific channel to acquire and in general you may use any
> returned from the DMA subsystem (though some platforms may need a dedicated
> channels to use, in this case xlate / filter is required). In our SoC any DW DMAC
> channel can be used for any DMA-capable peripherals like SPI, I2C, UART. But the
> their DMA settings must properly and optimally configured. It can be only done
> if you know the DMA controller parameters like max burst length, max block-size,
> etc.
>
> So no. The change proposed by this patch isn't workaround, but a useful feature,
> moreover expected to be supported by the generic DMA subsystem.
See above.
> > But let's see what we can do better. Since maximum is defined on the slave side
> > device, it probably needs to define minimum as well, otherwise it's possible
> > that some hardware can't cope underrun bursts.
>
> There is no need to define minimum if such limit doesn't exists except a
> natural 1. Moreover it doesn't exist for all DMA controllers seeing noone has
> added such capability into the generic DMA subsystem so far.
There is a contract between provider and consumer about DMA resource. That's
why both sides should participate in fulfilling it. Theoretically it may be a
hardware that doesn't support minimum burst available in DMA by a reason. For
such we would need minimum to be provided as well.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [RFC v1 2/3] drivers: nvmem: Add driver for QTI qfprom-efuse support
From: Randy Dunlap @ 2020-05-12 19:20 UTC (permalink / raw)
To: Ravi Kumar Bokka, Srinivas Kandagatla, Rob Herring
Cc: linux-kernel, devicetree, rnayak, saiprakash.ranjan, dhavalp,
mturney, sparate, c_rbokka, mkurumel
In-Reply-To: <1589307480-27508-3-git-send-email-rbokka@codeaurora.org>
On 5/12/20 11:17 AM, Ravi Kumar Bokka wrote:
> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> index d7b7f6d..c9345c5 100644
> --- a/drivers/nvmem/Kconfig
> +++ b/drivers/nvmem/Kconfig
> @@ -121,6 +121,16 @@ config QCOM_QFPROM
> This driver can also be built as a module. If so, the module
> will be called nvmem_qfprom.
>
> +config QTI_QFPROM_EFUSE
> + tristate "QTI_QFPROM_EFUSE Support"
> + depends on HAS_IOMEM
> + help
> + Say y here to enable QFPROM-Efuse support. This driver provides access
access to
> + QTI qfprom efuse via nvmem interface.
> +
> + This driver can also be built as a module. If so, the module
> + will be called nvmem_qfprom-efuse.
The last 3 non-blank lines should be indented with one tab + 2 spaces
instead of lots of spaces.
> +
> config NVMEM_SPMI_SDAM
> tristate "SPMI SDAM Support"
> depends on SPMI
--
~Randy
^ 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