* Re: [PATCH v8 0/2] SDM845 System Cache Driver
From: rishabhb @ 2018-06-04 16:18 UTC (permalink / raw)
To: linux-arm-kernel, linux-arm-msm, devicetree
Cc: linux-kernel, linux-arm, tsoni, ckadabi, evgreen, robh,
andy.shevchenko
In-Reply-To: <1527122121-31452-1-git-send-email-rishabhb@codeaurora.org>
On 2018-05-23 17:35, Rishabh Bhatnagar wrote:
> This series implements system cache or LLCC(Last Level Cache
> Controller)
> driver for SDM845 SOC. The purpose of the driver is to partition the
> system cache and program the settings such as priortiy, lines to probe
> while doing a look up in the system cache, low power related settings
> etc.
> The partitions are called cache slices. Each cache slice is associated
> with size and SCID(System Cache ID). The driver also provides API for
> clients to query the cache slice details,activate and deactivate them.
>
> The driver can be broadly classified into:
> * SOC specific driver: llcc-sdm845.c: Cache partitioning and cache
> slice
> properties for usecases on sdm845 that need to use system cache.
>
> * API : llcc-slice.c: Exports APIs to clients to query cache slice
> details,
> activate and deactivate cache slices.
>
> Changes since v7:
> * Change the DT node name to cache-controller.
> * Use the module_platform_driver_macro
> * Use GENMASK and SZ_* macros
> * Correct indentation, and remove unnecessary assignemnts.
> * Addresed all comments by Andy Schevchenko except the comment to
> ignore some
> lines of code going over 80 characters.
>
> Changes since v6:
> * Remove the max-slices property from DT.
> * Make client's slice_ids as macros.
> * Unlock mutex while returning from function in case of error.
>
> Changes since v5:
> * Remove client information from DT.
> * Make the llcc driver data as global.
> * Check return value of llcc_update_act_ctrl function
> * Change error returned from -EFAULT to -EINVAL
>
> Changes since v4:
> * Remove null pointer checks as per comments.
> * Remove extra blank lines.
>
> Changes since v3:
> * Use the regmap_read_poll_timeout function
> * Check for regmap read/write errors.
> * Remove memory barrier after regmap write
> * Derive memory bank offsets using stride macro variable
> * Remove debug statements from code
> * Remove the qcom_llcc_remove function
> * Use if IS_ENABLED in place of ifdef for built-in module
> * Change EXPORT_SYMBOL to EXPORT_SYMBOL_GPL
> * Remove unnecessary free functions
> * Change the variable names as per review comments
>
> Changes since v2:
> * Corrected the Makefile to fix compilation.
>
> Changes since v1:
> * Added Makefile and Kconfig.
>
> Changes since v0:
> * Removed the syscon and simple-mfd approach
> * Updated the device tree nodes to mention LLCC as a single HW block
> * Moved llcc bank offsets from device tree and handled the offset
> in the driver.
>
> ckadabi@codeaurora.org (2):
> dt-bindings: Documentation for qcom, llcc
> drivers: soc: Add LLCC driver
>
> .../devicetree/bindings/arm/msm/qcom,llcc.txt | 26 ++
> drivers/soc/qcom/Kconfig | 17 ++
> drivers/soc/qcom/Makefile | 2 +
> drivers/soc/qcom/llcc-sdm845.c | 94 ++++++
> drivers/soc/qcom/llcc-slice.c | 335
> +++++++++++++++++++++
> include/linux/soc/qcom/llcc-qcom.h | 180 +++++++++++
> 6 files changed, 654 insertions(+)
> create mode 100644
> Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
> create mode 100644 drivers/soc/qcom/llcc-sdm845.c
> create mode 100644 drivers/soc/qcom/llcc-slice.c
> create mode 100644 include/linux/soc/qcom/llcc-qcom.h
Does this spin look fine to everyone? If yes can we go ahead and merge
this?
^ permalink raw reply
* Re: [PATCH] arm64: dts: stingray: use NUM_SATA to configure number of sata ports
From: Florian Fainelli @ 2018-06-04 16:22 UTC (permalink / raw)
To: Scott Branden, Rob Herring, Mark Rutland, Catalin Marinas,
Will Deacon, Ray Jui
Cc: BCM Kernel Feedback, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <1526668446-20048-1-git-send-email-scott.branden@broadcom.com>
On 05/18/2018 11:34 AM, Scott Branden wrote:
> Move remaining sata configuration to stingray-sata.dtsi and enable
> ports based on NUM_SATA defined.
> Now, all that needs to be done is define NUM_SATA per board.
Rob could you review this and let us know if this approach is okay or
not? Thank you!
>
> Signed-off-by: Scott Branden <scott.branden@broadcom.com>
> ---
> .../boot/dts/broadcom/stingray/bcm958742-base.dtsi | 64 --------------------
> .../boot/dts/broadcom/stingray/bcm958742k.dts | 2 +
> .../boot/dts/broadcom/stingray/bcm958742t.dts | 2 +
> .../boot/dts/broadcom/stingray/stingray-sata.dtsi | 68 ++++++++++++++++++++++
> 4 files changed, 72 insertions(+), 64 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi b/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
> index 8862ec9..cacc25e 100644
> --- a/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
> +++ b/arch/arm64/boot/dts/broadcom/stingray/bcm958742-base.dtsi
> @@ -72,70 +72,6 @@
> <0x00000008 0x80000000 0x1 0x80000000>; /* 6G @ 34G */
> };
>
> -&sata0 {
> - status = "okay";
> -};
> -
> -&sata_phy0{
> - status = "okay";
> -};
> -
> -&sata1 {
> - status = "okay";
> -};
> -
> -&sata_phy1{
> - status = "okay";
> -};
> -
> -&sata2 {
> - status = "okay";
> -};
> -
> -&sata_phy2{
> - status = "okay";
> -};
> -
> -&sata3 {
> - status = "okay";
> -};
> -
> -&sata_phy3{
> - status = "okay";
> -};
> -
> -&sata4 {
> - status = "okay";
> -};
> -
> -&sata_phy4{
> - status = "okay";
> -};
> -
> -&sata5 {
> - status = "okay";
> -};
> -
> -&sata_phy5{
> - status = "okay";
> -};
> -
> -&sata6 {
> - status = "okay";
> -};
> -
> -&sata_phy6{
> - status = "okay";
> -};
> -
> -&sata7 {
> - status = "okay";
> -};
> -
> -&sata_phy7{
> - status = "okay";
> -};
> -
> &mdio_mux_iproc {
> mdio@10 {
> gphy0: eth-phy@10 {
> diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742k.dts b/arch/arm64/boot/dts/broadcom/stingray/bcm958742k.dts
> index 77efa28..a515346 100644
> --- a/arch/arm64/boot/dts/broadcom/stingray/bcm958742k.dts
> +++ b/arch/arm64/boot/dts/broadcom/stingray/bcm958742k.dts
> @@ -32,6 +32,8 @@
>
> /dts-v1/;
>
> +#define NUM_SATA 8
> +
> #include "bcm958742-base.dtsi"
>
> / {
> diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts b/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
> index 5084b03..6a4d19e 100644
> --- a/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
> +++ b/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
> @@ -32,6 +32,8 @@
>
> /dts-v1/;
>
> +#define NUM_SATA 8
> +
> #include "bcm958742-base.dtsi"
>
> / {
> diff --git a/arch/arm64/boot/dts/broadcom/stingray/stingray-sata.dtsi b/arch/arm64/boot/dts/broadcom/stingray/stingray-sata.dtsi
> index 8c68e0c..7f6d176 100644
> --- a/arch/arm64/boot/dts/broadcom/stingray/stingray-sata.dtsi
> +++ b/arch/arm64/boot/dts/broadcom/stingray/stingray-sata.dtsi
> @@ -43,7 +43,11 @@
> interrupts = <GIC_SPI 321 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 0)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata0_port0: sata-port@0 {
> reg = <0>;
> @@ -58,7 +62,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 0)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata0_phy0: sata-phy@0 {
> reg = <0>;
> @@ -73,7 +81,11 @@
> interrupts = <GIC_SPI 323 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 1)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata1_port0: sata-port@0 {
> reg = <0>;
> @@ -88,7 +100,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 1)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata1_phy0: sata-phy@0 {
> reg = <0>;
> @@ -103,7 +119,11 @@
> interrupts = <GIC_SPI 325 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 2)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata2_port0: sata-port@0 {
> reg = <0>;
> @@ -118,7 +138,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 2)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata2_phy0: sata-phy@0 {
> reg = <0>;
> @@ -133,7 +157,11 @@
> interrupts = <GIC_SPI 327 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 3)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata3_port0: sata-port@0 {
> reg = <0>;
> @@ -148,7 +176,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 3)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata3_phy0: sata-phy@0 {
> reg = <0>;
> @@ -163,7 +195,11 @@
> interrupts = <GIC_SPI 329 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 4)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata4_port0: sata-port@0 {
> reg = <0>;
> @@ -178,7 +214,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 4)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata4_phy0: sata-phy@0 {
> reg = <0>;
> @@ -193,7 +233,11 @@
> interrupts = <GIC_SPI 331 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 5)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata5_port0: sata-port@0 {
> reg = <0>;
> @@ -208,7 +252,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 5)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata5_phy0: sata-phy@0 {
> reg = <0>;
> @@ -223,7 +271,11 @@
> interrupts = <GIC_SPI 333 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 6)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata6_port0: sata-port@0 {
> reg = <0>;
> @@ -238,7 +290,11 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 6)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata6_phy0: sata-phy@0 {
> reg = <0>;
> @@ -253,7 +309,11 @@
> interrupts = <GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>;
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 7)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata7_port0: sata-port@0 {
> reg = <0>;
> @@ -268,11 +328,19 @@
> reg-names = "phy";
> #address-cells = <1>;
> #size-cells = <0>;
> +#if (NUM_SATA > 7)
> + status = "okay";
> +#else
> status = "disabled";
> +#endif
>
> sata7_phy0: sata-phy@0 {
> reg = <0>;
> #phy-cells = <0>;
> };
> };
> +
> +#if (NUM_SATA > 8)
> +#error "NUM_SATA > 8"
> +#endif
> };
>
--
Florian
^ permalink raw reply
* Re: [PATCH 06/15] drm/sun4i: tcon: Add support for tcon-top
From: Maxime Ripard @ 2018-06-04 16:23 UTC (permalink / raw)
To: Jernej Škrabec
Cc: Chen-Yu Tsai, Rob Herring, Mark Rutland, dri-devel, devicetree,
linux-arm-kernel, linux-kernel, linux-clk, linux-sunxi
In-Reply-To: <8937075.mEvrCV6laa@jernej-laptop>
[-- Attachment #1: Type: text/plain, Size: 13423 bytes --]
On Mon, Jun 04, 2018 at 05:09:56PM +0200, Jernej Škrabec wrote:
> Dne ponedeljek, 04. junij 2018 ob 13:50:34 CEST je Maxime Ripard napisal(a):
> > On Fri, Jun 01, 2018 at 09:19:43AM -0700, Chen-Yu Tsai wrote:
> > > On Fri, Jun 1, 2018 at 8:29 AM, Maxime Ripard <maxime.ripard@bootlin.com>
> wrote:
> > > > On Thu, May 31, 2018 at 07:54:08PM +0200, Jernej Škrabec wrote:
> > > >> Dne četrtek, 31. maj 2018 ob 11:21:33 CEST je Maxime Ripard napisal(a):
> > > >> > On Thu, May 24, 2018 at 03:01:09PM -0700, Chen-Yu Tsai wrote:
> > > >> > > >> > > + if (tcon->quirks->needs_tcon_top) {
> > > >> > > >> > > + struct device_node *np;
> > > >> > > >> > > +
> > > >> > > >> > > + np = of_parse_phandle(dev->of_node,
> > > >> > > >> > > "allwinner,tcon-top",
> > > >> > > >> > > 0);
> > > >> > > >> > > + if (np) {
> > > >> > > >> > > + struct platform_device *pdev;
> > > >> > > >> > > +
> > > >> > > >> > > + pdev = of_find_device_by_node(np);
> > > >> > > >> > > + if (pdev)
> > > >> > > >> > > + tcon->tcon_top =
> > > >> > > >> > > platform_get_drvdata(pdev);
> > > >> > > >> > > + of_node_put(np);
> > > >> > > >> > > +
> > > >> > > >> > > + if (!tcon->tcon_top)
> > > >> > > >> > > + return -EPROBE_DEFER;
> > > >> > > >> > > + }
> > > >> > > >> > > + }
> > > >> > > >> > > +
> > > >> > > >> >
> > > >> > > >> > I might have missed it, but I've not seen the bindings
> > > >> > > >> > additions for
> > > >> > > >> > that property. This shouldn't really be done that way anyway,
> > > >> > > >> > instead
> > > >> > > >> > of using a direct phandle, you should be using the of-graph,
> > > >> > > >> > with the
> > > >> > > >> > TCON-top sitting where it belongs in the flow of data.
> > > >> > > >>
> > > >> > > >> Just to answer to the first question, it did describe it in
> > > >> > > >> "[PATCH
> > > >> > > >> 07/15] dt- bindings: display: sun4i-drm: Add R40 HDMI pipeline".
> > > >> > > >>
> > > >> > > >> As why I designed it that way - HW representation could be
> > > >> > > >> described
> > > >> > > >> that way> >>
> > > >> > > >>
> > > >> > > >> (ASCII art makes sense when fixed width font is used to view
> it):
> > > >> > > >> / LCD0/LVDS0
> > > >> > > >>
> > > >> > > >> / TCON-LCD0
> > > >> > > >>
> > > >> > > >> | \ MIPI DSI
> > > >> > > >>
> > > >> > > >> mixer0 |
> > > >> > > >>
> > > >> > > >> \ / TCON-LCD1 - LCD1/LVDS1
> > > >> > > >>
> > > >> > > >> TCON-TOP
> > > >> > > >>
> > > >> > > >> / \ TCON-TV0 - TVE0/RGB
> > > >> > > >>
> > > >> > > >> mixer1 | \
> > > >> > > >>
> > > >> > > >> | TCON-TOP - HDMI
> > > >> > > >> |
> > > >> > > >> | /
> > > >> > > >>
> > > >> > > >> \ TCON-TV1 - TVE1/RGB
> > > >> > > >>
> > > >> > > >> This is a bit simplified, since there is also TVE-TOP, which is
> > > >> > > >> responsible
> > > >> > > >> for sharing 4 DACs between both TVE encoders. You can have two
> > > >> > > >> TV outs
> > > >> > > >> (PAL/ NTSC) or TVE0 as TV out and TVE1 as RGB or vice versa. It
> > > >> > > >> even
> > > >> > > >> seems that you can arbitrarly choose which DAC is responsible
> > > >> > > >> for
> > > >> > > >> which signal, so there is a ton of possible end combinations,
> > > >> > > >> but I'm
> > > >> > > >> not 100% sure.
> > > >> > > >>
> > > >> > > >> Even though I wrote TCON-TOP twice, this is same unit in HW. R40
> > > >> > > >> manual
> > > >> > > >> suggest more possibilities, although some of them seem wrong,
> > > >> > > >> like RGB
> > > >> > > >> feeding from LCD TCON. That is confirmed to be wrong when
> > > >> > > >> checking BSP
> > > >> > > >> code.
> > > >> > > >>
> > > >> > > >> Additionally, TCON-TOP comes in the middle of TVE0 and LCD0,
> > > >> > > >> TVE1 and
> > > >> > > >> LCD1 for pin muxing, although I'm not sure why is that needed at
> > > >> > > >> all,
> > > >> > > >> since according to R40 datasheet, TVE0 and TVE1 pins are
> > > >> > > >> dedicated and
> > > >> > > >> not on PORT D and PORT H, respectively, as TCON-TOP
> > > >> > > >> documentation
> > > >> > > >> suggest. However, HSYNC and PSYNC lines might be shared between
> > > >> > > >> TVE
> > > >> > > >> (when it works in RGB mode) and LCD. But that is just my guess
> > > >> > > >> since
> > > >> > > >> I'm not really familiar with RGB and LCD interfaces.
> > > >> > > >>
> > > >> > > >> I'm really not sure what would be the best representation in
> > > >> > > >> OF-graph.
> > > >> > > >> Can you suggest one?
> > > >> > > >
> > > >> > > > Rob might disagree on this one, but I don't see anything wrong
> > > >> > > > with
> > > >> > > > having loops in the graph. If the TCON-TOP can be both the input
> > > >> > > > and
> > > >> > > > output of the TCONs, then so be it, and have it described that
> > > >> > > > way in
> > > >> > > > the graph.
> > > >> > > >
> > > >> > > > The code is already able to filter out nodes that have already
> > > >> > > > been
> > > >> > > > added to the list of devices we need to wait for in the component
> > > >> > > > framework, so that should work as well.
> > > >> > > >
> > > >> > > > And we'd need to describe TVE-TOP as well, even though we don't
> > > >> > > > have a
> > > >> > > > driver for it yet. That will simplify the backward compatibility
> > > >> > > > later
> > > >> > > > on.
> > > >> > >
> > > >> > > I'm getting the feeling that TCON-TOP / TVE-TOP is the glue layer
> > > >> > > that
> > > >> > > binds everything together, and provides signal routing, kind of
> > > >> > > like
> > > >> > > DE-TOP on A64. So the signal mux controls that were originally
> > > >> > > found
> > > >> > > in TCON0 and TVE0 were moved out.
> > > >> > >
> > > >> > > The driver needs to know about that, but the graph about doesn't
> > > >> > > make
> > > >> > > much sense directly. Without looking at the manual, I understand it
> > > >> > > to
> > > >> > > likely be one mux between the mixers and TCONs, and one between the
> > > >> > > TCON-TVs and HDMI. Would it make more sense to just have the graph
> > > >> > > connections between the muxed components, and remove TCON-TOP from
> > > >> > > it, like we had in the past? A phandle could be used to reference
> > > >> > > the TCON-TOP for mux controls, in addition to the clocks and
> > > >> > > resets.
> > > >> > >
> > > >> > > For TVE, we would need something to represent each of the output
> > > >> > > pins,
> > > >> > > so the device tree can actually describe what kind of signal, be it
> > > >> > > each component of RGB/YUV or composite video, is wanted on each
> > > >> > > pin,
> > > >> > > if any. This is also needed on the A20 for the Cubietruck, so we
> > > >> > > can
> > > >> > > describe which pins are tied to the VGA connector, and which one
> > > >> > > does
> > > >> > > R, G, or B.
> > > >> >
> > > >> > I guess we'll see how the DT maintainers feel about this, but my
> > > >> > impression is that the OF graph should model the flow of data between
> > > >> > the devices. If there's a mux somewhere, then the data is definitely
> > > >> > going through it, and as such it should be part of the graph.
> > > >>
> > > >> I concur, but I spent few days thinking how to represent this sanely in
> > > >> graph, but I didn't find any good solution. I'll represent here my
> > > >> idea and please tell your opinion before I start implementing it.
> > > >>
> > > >> First, let me be clear that mixer0 and mixer1 don't have same
> > > >> capabilities
> > > >> (different number of planes, mixer0 supports writeback, mixer1 does
> > > >> not,
> > > >> etc.). Thus, it does matter which mixer is connected to which
> > > >> TCON/encoder.
> > > >> mixer0 is meant to be connected to main display and mixer1 to
> > > >> auxiliary. That obviously depends on end system.
> > > >>
> > > >> So, TCON TOP has 3 muxes, which have to be represented in graph. Two of
> > > >> them are for mixer/TCON relationship (each of them 1 input and 4
> > > >> possible outputs) and one for TV TCON/HDMI pair selection (2 possible
> > > >> inputs, 1 output).
> > > >>
> > > >> According to current practice in sun4i-drm driver, graph has to have
> > > >> port 0, representing input and port 1, representing output. This would
> > > >> mean that graph looks something like that:
> > > >>
> > > >> tcon_top: tcon-top@1c70000 {
> > > >>
> > > >> compatible = "allwinner,sun8i-r40-tcon-top";
> > > >> ...
> > > >> ports {
> > > >>
> > > >> #address-cells = <1>;
> > > >> #size-cells = <0>;
> > > >>
> > > >> tcon_top_in: port@0 {
> > > >>
> > > >> #address-cells = <1>;
> > > >> #size-cells = <0>;
> > > >> reg = <0>;
> > > >>
> > > >> tcon_top_in_mixer0: endpoint@0 {
> > > >>
> > > >> reg = <0>;
> > > >> remote-endpoint = <&mixer0_out_tcon_top>;
> > > >>
> > > >> };
> > > >>
> > > >> tcon_top_in_mixer1: endpoint@1 {
> > > >>
> > > >> reg = <1>;
> > > >> remote-endpoint = <&mixer1_out_tcon_top>;
> > > >>
> > > >> };
> > > >>
> > > >> tcon_top_in_tcon_tv: endpoint@2 {
> > > >>
> > > >> reg = <2>;
> > > >> // here is HDMI input connection, part of
> > > >> board DTS
> > > >> remote-endpoint = <board specific phandle
> > > >> to TV TCON output>;
> > > >>
> > > >> };
> > > >>
> > > >> };
> > > >>
> > > >> tcon_top_out: port@1 {
> > > >>
> > > >> #address-cells = <1>;
> > > >> #size-cells = <0>;
> > > >> reg = <1>;
> > > >>
> > > >> tcon_top_out_tcon0: endpoint@0 {
> > > >>
> > > >> reg = <0>;
> > > >> // here is mixer0 output connection, part
> > > >> of board DTS
> > > >> remote-endpoint = <board specific phandle
> > > >> to TCON>;
> > > >>
> > > >> };
> > > >>
> > > >> tcon_top_out_tcon1: endpoint@1 {
> > > >>
> > > >> reg = <1>;
> > > >> // here is mixer1 output connection, part
> > > >> of board DTS
> > > >> remote-endpoint = <board specific phandle
> > > >> to TCON>;
> > > >>
> > > >> };
> > > >>
> > > >> tcon_top_out_hdmi: endpoint@2 {
> > > >>
> > > >> reg = <2>;
> > > >> remote-endpoint = <&hdmi_in_tcon_top>;
> > > >>
> > > >> };
> > > >>
> > > >> };
> > > >>
> > > >> };
> > > >>
> > > >> };
> > > >
> > > > IIRC, each port is supposed to be one route for the data, so we would
> > > > have multiple ports, one for the mixers in input and for the tcon in
> > > > output, and one for the TCON in input and for the HDMI/TV in
> > > > output. Rob might correct me here.
>
> Ok, that seems more clean approach. I'll have to extend graph traversing
> algorithm in sun4i_drv.c, but that's no problem.
>
> Just to be clear, you have in mind 3 pairs of ports (0/1 for mixer0 mux, 2/3
> for mixer1 and 4/5 for HDMI input), right? That way each mux is represented
> with one pair of ports, even numbered for input and odd numbered for output.
Yep, unless Rob feels otherwise.
Maxime
--
Maxime Ripard, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH v5 0/6] Driver for at91 usart in spi mode
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
Hello,
This is the second version of driver. I added a mfd driver which by
default probes atmel_serial driver and if in dt is specified to probe
the spi driver, then the spi-at91-usart driver will be probed. The
compatible for atmel_serial is now the compatible for at91-usart mfd
driver and compatilbe for atmel_serial driver was changed in order to
keep the bindings for serial as they are.
Changes in v1:
- added spi-at91-usart driver
Changes in v2:
- added at91-usart mfd driver
- modified spi-at91-usart driver to work as mfd driver child
- modified atmel_serial driver to work as mfd driver child
Changes in v3:
- fixed spi slaves probing
Changes in v4:
- modified the spi driver to use cs gpio support form spi subsystem
- fixed dma transfers for serial driver
- squashed binding for spi and serial and moved them to mfd/atmel-usart.txt
Changes in v5:
- fixed usage of stdout-path property with atmel_serial driver
Radu Pirea (6):
MAINTAINERS: add at91 usart mfd driver
dt-bindings: add binding for atmel-usart in SPI mode
mfd: at91-usart: added mfd driver for usart
MAINTAINERS: add at91 usart spi driver
spi: at91-usart: add driver for at91-usart as spi
tty/serial: atmel: changed the driver to work under at91-usart mfd
.../bindings/{serial => mfd}/atmel-usart.txt | 25 +-
MAINTAINERS | 16 +
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/at91-usart.c | 67 +++
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-at91-usart.c | 436 ++++++++++++++++++
drivers/tty/serial/Kconfig | 1 +
drivers/tty/serial/atmel_serial.c | 41 +-
include/dt-bindings/mfd/at91-usart.h | 17 +
11 files changed, 604 insertions(+), 19 deletions(-)
rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
create mode 100644 drivers/mfd/at91-usart.c
create mode 100644 drivers/spi/spi-at91-usart.c
create mode 100644 include/dt-bindings/mfd/at91-usart.h
--
2.17.1
^ permalink raw reply
* [PATCH v5 1/6] MAINTAINERS: add at91 usart mfd driver
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
Added entry for at91 usart mfd driver.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e2a2fddbd19..12203d07c6af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9160,6 +9160,7 @@ M: Richard Genoud <richard.genoud@gmail.com>
S: Maintained
F: drivers/tty/serial/atmel_serial.c
F: drivers/tty/serial/atmel_serial.h
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
MICROCHIP / ATMEL DMA DRIVER
M: Ludovic Desroches <ludovic.desroches@microchip.com>
@@ -9192,6 +9193,14 @@ S: Supported
F: drivers/mtd/nand/raw/atmel/*
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
+MICROCHIP AT91 USART MFD DRIVER
+M: Radu Pirea <radu.pirea@microchip.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: drivers/mfd/at91-usart.c
+F: include/dt-bindings/mfd/at91-usart.h
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
M: Woojung Huh <Woojung.Huh@microchip.com>
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
--
2.17.1
^ permalink raw reply related
* [PATCH v5 2/6] dt-bindings: add binding for atmel-usart in SPI mode
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: devicetree, linux-kernel, linux-spi, linux-serial, Radu Pirea,
linux-arm-kernel
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
This patch moves the bindings for serial from serial/atmel-usart.txt to
mfd/atmel-usart.txt and adds bindings for USART in SPI mode.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../bindings/{serial => mfd}/atmel-usart.txt | 25 +++++++++++++++++--
include/dt-bindings/mfd/at91-usart.h | 17 +++++++++++++
2 files changed, 40 insertions(+), 2 deletions(-)
rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
create mode 100644 include/dt-bindings/mfd/at91-usart.h
diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
similarity index 76%
rename from Documentation/devicetree/bindings/serial/atmel-usart.txt
rename to Documentation/devicetree/bindings/mfd/atmel-usart.txt
index 7c0d6b2f53e4..3b9e18642c3b 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
@@ -1,6 +1,6 @@
* Atmel Universal Synchronous Asynchronous Receiver/Transmitter (USART)
-Required properties:
+Required properties for USART:
- compatible: Should be "atmel,<chip>-usart" or "atmel,<chip>-dbgu"
The compatible <chip> indicated will be the first SoC to support an
additional mode or an USART new feature.
@@ -11,7 +11,13 @@ Required properties:
Required elements: "usart"
- clocks: phandles to input clocks.
-Optional properties:
+Required properties for USART in SPI mode:
+- #size-cells : Must be <0>
+- #address-cells : Must be <1>
+- cs-gpios: chipselects (internal cs not supported)
+- atmel,usart-mode : Must be <USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
+
+Optional properties in serial mode:
- atmel,use-dma-rx: use of PDC or DMA for receiving data
- atmel,use-dma-tx: use of PDC or DMA for transmitting data
- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
@@ -62,3 +68,18 @@ Example:
dma-names = "tx", "rx";
atmel,fifo-size = <32>;
};
+
+- SPI mode:
+ #include <dt-bindings/mfd/at91-usart.h>
+
+ spi0: spi@f001c000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91rm9200-usart", "atmel,at91sam9260-usart";
+ atmel,usart-mode = <USART_MODE_SPI>;
+ reg = <0xf001c000 0x100>;
+ interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&usart0_clk>;
+ clock-names = "usart";
+ cs-gpios = <&pioB 3 0>;
+ };
diff --git a/include/dt-bindings/mfd/at91-usart.h b/include/dt-bindings/mfd/at91-usart.h
new file mode 100644
index 000000000000..ac811628a42d
--- /dev/null
+++ b/include/dt-bindings/mfd/at91-usart.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides macros for AT91 USART DT bindings.
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#ifndef __DT_BINDINGS_AT91_USART_H__
+#define __DT_BINDINGS_AT91_USART_H__
+
+#define AT91_USART_MODE_SERIAL 1
+#define AT91_USART_MODE_SPI 2
+
+#endif /* __DT_BINDINGS_AT91_USART_H__ */
--
2.17.1
^ permalink raw reply related
* [PATCH v5 3/6] mfd: at91-usart: added mfd driver for usart
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
This mfd driver is just a wrapper over atmel_serial driver and
spi-at91-usart driver. Selection of one of the drivers is based on a
property from device tree. If the property is not specified, the default
driver is atmel_serial.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Acked-by: Rob Herring <robh@kernel.org>
---
drivers/mfd/Kconfig | 9 ++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/at91-usart.c | 67 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 77 insertions(+)
create mode 100644 drivers/mfd/at91-usart.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5aa194..a886672b960d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -99,6 +99,15 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_AT91_USART
+ tristate "AT91 USART Driver"
+ select MFD_CORE
+ help
+ Select this to get support for AT91 USART IP. This is a wrapper
+ over at91-usart-serial driver and usart-spi-driver. Only one function
+ can be used at a time. The choice is done at boot time by the probe
+ function of this MFD driver according to a device tree property.
+
config MFD_ATMEL_FLEXCOM
tristate "Atmel Flexcom (Flexible Serial Communication Unit)"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d9d2cf0d32ef..db1332aa96db 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_AT91_USART) += at91-usart.o
obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c
new file mode 100644
index 000000000000..262e7affba22
--- /dev/null
+++ b/drivers/mfd/at91-usart.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for AT91 USART
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#include <dt-bindings/mfd/at91-usart.h>
+
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/property.h>
+
+static struct mfd_cell at91_usart_spi_subdev = {
+ .name = "at91_usart_spi",
+ .of_compatible = "microchip,at91sam9g45-usart-spi",
+ };
+
+static struct mfd_cell at91_usart_serial_subdev = {
+ .name = "atmel_usart_serial",
+ .of_compatible = "atmel,at91rm9200-usart-serial",
+ };
+
+static int at91_usart_mode_probe(struct platform_device *pdev)
+{
+ struct mfd_cell cell;
+ u32 opmode;
+ int err;
+
+ err = device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode);
+
+ switch (opmode) {
+ case AT91_USART_MODE_SPI:
+ cell = at91_usart_spi_subdev;
+ break;
+ case AT91_USART_MODE_SERIAL:
+ default:
+ cell = at91_usart_serial_subdev;
+ }
+
+ return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1,
+ NULL, 0, NULL);
+}
+
+static const struct of_device_id at91_usart_mode_of_match[] = {
+ { .compatible = "atmel,at91rm9200-usart" },
+ { .compatible = "atmel,at91sam9260-usart" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_flexcom_of_match);
+
+static struct platform_driver at91_usart_mfd = {
+ .probe = at91_usart_mode_probe,
+ .driver = {
+ .name = "at91_usart_mode",
+ .of_match_table = at91_usart_mode_of_match,
+ },
+};
+
+module_platform_driver(at91_usart_mfd);
+
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_DESCRIPTION("AT91 USART MFD driver");
+MODULE_LICENSE("GPL v2");
--
2.17.1
^ permalink raw reply related
* [PATCH v5 4/6] MAINTAINERS: add at91 usart spi driver
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
Added entry for at91 usart mfd driver.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 12203d07c6af..dae31df711fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9201,6 +9201,13 @@ F: drivers/mfd/at91-usart.c
F: include/dt-bindings/mfd/at91-usart.h
F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+MICROCHIP AT91 USART SPI DRIVER
+M: Radu Pirea <radu.pirea@microchip.com>
+L: linux-spi@vger.kernel.org
+S: Supported
+F: drivers/spi/spi-at91-usart.c
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
M: Woojung Huh <Woojung.Huh@microchip.com>
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
--
2.17.1
^ permalink raw reply related
* [PATCH v5 5/6] spi: at91-usart: add driver for at91-usart as spi
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
This is the driver for at91-usart in spi mode. The USART IP can be configured
to work in many modes and one of them is SPI.
The driver was tested on sama5d3-xplained and sama5d4-xplained boards with
enc28j60 ethernet controller as slave.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-at91-usart.c | 436 +++++++++++++++++++++++++++++++++++
3 files changed, 446 insertions(+)
create mode 100644 drivers/spi/spi-at91-usart.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6fb0347a24f2..1a002a32d7aa 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,6 +77,15 @@ config SPI_ATMEL
This selects a driver for the Atmel SPI Controller, present on
many AT91 (ARM) chips.
+config SPI_AT91_USART
+ tristate "Atmel USART Controller SPI driver"
+ depends on HAS_DMA
+ depends on (ARCH_AT91 || COMPILE_TEST)
+ select MFD_AT91_USART
+ help
+ This selects a driver for the AT91 USART Controller as SPI Master,
+ present on AT91 and SAMA5 SoC series.
+
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 34c5f2832ddf..fb6cb42f4eaa 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
+obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o
diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
new file mode 100644
index 000000000000..be7495527c72
--- /dev/null
+++ b/drivers/spi/spi-at91-usart.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AT91 USART Controllers as SPI
+ *
+ * Copyright (C) 2018 Microchip Technology Inc.
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+
+#define US_CR 0x00
+#define US_MR 0x04
+#define US_IER 0x08
+#define US_IDR 0x0C
+#define US_CSR 0x14
+#define US_RHR 0x18
+#define US_THR 0x1C
+#define US_BRGR 0x20
+#define US_VERSION 0xFC
+
+#define US_CR_RSTRX BIT(2)
+#define US_CR_RSTTX BIT(3)
+#define US_CR_RXEN BIT(4)
+#define US_CR_RXDIS BIT(5)
+#define US_CR_TXEN BIT(6)
+#define US_CR_TXDIS BIT(7)
+
+#define US_MR_SPI_MASTER 0x0E
+#define US_MR_CHRL GENMASK(7, 6)
+#define US_MR_CPHA BIT(8)
+#define US_MR_CPOL BIT(16)
+#define US_MR_CLKO BIT(18)
+#define US_MR_WRDBT BIT(20)
+#define US_MR_LOOP BIT(15)
+
+#define US_IR_RXRDY BIT(0)
+#define US_IR_TXRDY BIT(1)
+#define US_IR_OVRE BIT(5)
+
+#define US_BRGR_SIZE BIT(16)
+
+#define US_MIN_CLK_DIV 0x06
+#define US_MAX_CLK_DIV BIT(16)
+
+#define US_RESET (US_CR_RSTRX | US_CR_RSTTX)
+#define US_DISABLE (US_CR_RXDIS | US_CR_TXDIS)
+#define US_ENABLE (US_CR_RXEN | US_CR_TXEN)
+#define US_OVRE_RXRDY_IRQS (US_IR_OVRE | US_IR_RXRDY)
+
+#define US_INIT \
+ (US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT)
+
+/* Register access macros */
+#define at91_usart_spi_readl(port, reg) \
+ readl_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writel(port, reg, value) \
+ writel_relaxed((value), (port)->regs + US_##reg)
+
+#define at91_usart_spi_readb(port, reg) \
+ readb_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writeb(port, reg, value) \
+ writeb_relaxed((value), (port)->regs + US_##reg)
+
+struct at91_usart_spi {
+ struct spi_transfer *current_transfer;
+ void __iomem *regs;
+ struct device *dev;
+ struct clk *clk;
+
+ /*used in interrupt to protect data reading*/
+ spinlock_t lock;
+
+ int irq;
+ unsigned int current_tx_remaining_bytes;
+ unsigned int current_rx_remaining_bytes;
+ int done_status;
+
+ u32 spi_clk;
+ u32 status;
+
+ bool xfer_failed;
+};
+
+static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_TXRDY;
+}
+
+static inline u32 at91_usart_spi_rx_ready(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_RXRDY;
+}
+
+static inline u32 at91_usart_spi_check_overrun(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_OVRE;
+}
+
+static inline u32 at91_usart_spi_read_status(struct at91_usart_spi *aus)
+{
+ aus->status = at91_usart_spi_readl(aus, CSR);
+ return aus->status;
+}
+
+static inline void at91_usart_spi_tx(struct at91_usart_spi *aus)
+{
+ unsigned int len = aus->current_transfer->len;
+ unsigned int remaining = aus->current_tx_remaining_bytes;
+ const u8 *tx_buf = aus->current_transfer->tx_buf;
+
+ if (!remaining)
+ return;
+
+ if (at91_usart_spi_tx_ready(aus)) {
+ at91_usart_spi_writeb(aus, THR, tx_buf[len - remaining]);
+ aus->current_tx_remaining_bytes--;
+ }
+}
+
+static inline void at91_usart_spi_rx(struct at91_usart_spi *aus)
+{
+ int len = aus->current_transfer->len;
+ int remaining = aus->current_rx_remaining_bytes;
+ u8 *rx_buf = aus->current_transfer->rx_buf;
+
+ if (!remaining)
+ return;
+
+ rx_buf[len - remaining] = at91_usart_spi_readb(aus, RHR);
+ aus->current_rx_remaining_bytes--;
+}
+
+static inline void
+at91_usart_spi_set_xfer_speed(struct at91_usart_spi *aus,
+ struct spi_transfer *xfer)
+{
+ at91_usart_spi_writel(aus, BRGR,
+ DIV_ROUND_UP(aus->spi_clk, xfer->speed_hz));
+}
+
+static irqreturn_t at91_usart_spi_interrupt(int irq, void *dev_id)
+{
+ struct spi_controller *controller = dev_id;
+ struct at91_usart_spi *aus = spi_master_get_devdata(controller);
+
+ spin_lock(&aus->lock);
+ at91_usart_spi_read_status(aus);
+
+ if (at91_usart_spi_check_overrun(aus)) {
+ aus->xfer_failed = true;
+ aus->done_status = -EIO;
+ at91_usart_spi_writel(aus, IDR, US_IR_OVRE | US_IR_RXRDY);
+ spin_unlock(&aus->lock);
+ return IRQ_HANDLED;
+ }
+
+ if (at91_usart_spi_rx_ready(aus)) {
+ at91_usart_spi_rx(aus);
+ spin_unlock(&aus->lock);
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock(&aus->lock);
+
+ return IRQ_NONE;
+}
+
+static int at91_usart_spi_setup(struct spi_device *spi)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(spi->controller);
+ u32 *ausd = spi->controller_state;
+ unsigned int mr = at91_usart_spi_readl(aus, MR);
+ u8 bits = spi->bits_per_word;
+
+ if (bits != 8) {
+ dev_dbg(&spi->dev, "Only 8 bits per word are supported\n");
+ return -EINVAL;
+ }
+
+ if (spi->mode & SPI_CPOL)
+ mr |= US_MR_CPOL;
+ else
+ mr &= ~US_MR_CPOL;
+
+ if (spi->mode & SPI_CPHA)
+ mr |= US_MR_CPHA;
+ else
+ mr &= ~US_MR_CPHA;
+
+ if (spi->mode & SPI_LOOP)
+ mr |= US_MR_LOOP;
+ else
+ mr &= ~US_MR_LOOP;
+
+ if (!ausd) {
+ ausd = kzalloc(sizeof(*ausd), GFP_KERNEL);
+ if (!ausd)
+ return -ENOMEM;
+
+ spi->controller_state = ausd;
+ }
+
+ *ausd = mr;
+
+ dev_dbg(&spi->dev,
+ "setup: bpw %u mode 0x%x -> mr %d %08x\n",
+ bits, spi->mode, spi->chip_select, mr);
+
+ return 0;
+}
+
+int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+ at91_usart_spi_set_xfer_speed(aus, xfer);
+ aus->done_status = 0;
+ aus->xfer_failed = false;
+ aus->current_transfer = xfer;
+ aus->current_tx_remaining_bytes = xfer->len;
+ aus->current_rx_remaining_bytes = xfer->len;
+
+ while ((aus->current_tx_remaining_bytes ||
+ aus->current_rx_remaining_bytes) && !aus->xfer_failed) {
+ at91_usart_spi_read_status(aus);
+ at91_usart_spi_tx(aus);
+ cpu_relax();
+ }
+ if (aus->xfer_failed) {
+ dev_err(aus->dev, "Overrun!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int at91_usart_spi_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+ struct spi_device *spi = message->spi;
+ u32 *ausd = spi->controller_state;
+
+ at91_usart_spi_writel(aus, CR, US_ENABLE);
+ at91_usart_spi_writel(aus, IER, US_OVRE_RXRDY_IRQS);
+ at91_usart_spi_writel(aus, MR, *ausd);
+
+ return 0;
+}
+
+int at91_usart_spi_unprepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+ at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+ at91_usart_spi_writel(aus, IDR, US_OVRE_RXRDY_IRQS);
+
+ return 0;
+}
+
+static void at91_usart_spi_cleanup(struct spi_device *spi)
+{
+ struct at91_usart_spi_device *ausd = spi->controller_state;
+
+ spi->controller_state = NULL;
+ kfree(ausd);
+}
+
+static void at91_usart_spi_init(struct at91_usart_spi *aus)
+{
+ at91_usart_spi_writel(aus, MR, US_INIT);
+ at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+}
+
+static int at91_usart_gpio_setup(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ int i;
+ int ret;
+ int nb;
+
+ if (!np)
+ return -EINVAL;
+
+ nb = of_gpio_named_count(np, "cs-gpios");
+ for (i = 0; i < nb; i++) {
+ int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
+
+ if (cs_gpio < 0)
+ return cs_gpio;
+
+ if (gpio_is_valid(cs_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, cs_gpio,
+ GPIOF_DIR_OUT,
+ dev_name(&pdev->dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int at91_usart_spi_probe(struct platform_device *pdev)
+{
+ struct resource *regs;
+ struct spi_controller *controller;
+ struct at91_usart_spi *aus;
+ struct clk *clk;
+ int irq;
+ int ret;
+
+ regs = platform_get_resource(to_platform_device(pdev->dev.parent),
+ IORESOURCE_MEM, 0);
+ if (!regs)
+ return -EINVAL;
+
+ irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
+ if (irq < 0)
+ return irq;
+
+ clk = devm_clk_get(pdev->dev.parent, "usart");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = -ENOMEM;
+ controller = spi_alloc_master(&pdev->dev, sizeof(*aus));
+ if (!controller)
+ goto at91_usart_spi_probe_fail;
+
+ ret = at91_usart_gpio_setup(pdev);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
+ controller->dev.of_node = pdev->dev.parent->of_node;
+ controller->bits_per_word_mask = SPI_BPW_MASK(8);
+ controller->setup = at91_usart_spi_setup;
+ controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+ controller->transfer_one = at91_usart_spi_transfer_one;
+ controller->prepare_message = at91_usart_spi_prepare_message;
+ controller->unprepare_message = at91_usart_spi_unprepare_message;
+ controller->cleanup = at91_usart_spi_cleanup;
+ controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+ US_MIN_CLK_DIV);
+ controller->min_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+ US_MAX_CLK_DIV);
+ platform_set_drvdata(pdev, controller);
+
+ aus = spi_master_get_devdata(controller);
+
+ aus->dev = &pdev->dev;
+ aus->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(aus->regs)) {
+ ret = PTR_ERR(aus->regs);
+ goto at91_usart_spi_probe_fail;
+ }
+
+ aus->irq = irq;
+ aus->clk = clk;
+
+ ret = devm_request_irq(&pdev->dev, irq, at91_usart_spi_interrupt, 0,
+ dev_name(&pdev->dev), controller);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ aus->spi_clk = clk_get_rate(clk);
+ at91_usart_spi_init(aus);
+
+ spin_lock_init(&aus->lock);
+ ret = devm_spi_register_master(&pdev->dev, controller);
+ if (ret)
+ goto fail_register_master;
+
+ dev_info(&pdev->dev,
+ "Atmel USART SPI Controller version 0x%x at 0x%08x (irq %d)\n",
+ at91_usart_spi_readl(aus, VERSION),
+ regs->start, irq);
+
+ return 0;
+
+fail_register_master:
+ clk_disable_unprepare(clk);
+at91_usart_spi_probe_fail:
+ spi_master_put(controller);
+ return ret;
+}
+
+static int at91_usart_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct at91_usart_spi *aus = spi_master_get_devdata(master);
+
+ clk_disable_unprepare(aus->clk);
+
+ return 0;
+}
+
+static const struct of_device_id at91_usart_spi_dt_ids[] = {
+ { .compatible = "microchip,sama5d3-usart-spi"},
+ { .compatible = "microchip,sama5d4-usart-spi"},
+ { .compatible = "microchip,at91sam9g45-usart-spi"},
+ { /* sentinel */}
+};
+
+MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
+
+static struct platform_driver at91_usart_spi_driver = {
+ .driver = {
+ .name = "at91_usart_spi",
+ },
+ .probe = at91_usart_spi_probe,
+ .remove = at91_usart_spi_remove,
+};
+
+module_platform_driver(at91_usart_spi_driver);
+
+MODULE_DESCRIPTION("Microchip AT91 USART SPI Controller driver");
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:at91_usart_spi");
--
2.17.1
^ permalink raw reply related
* [PATCH v5 6/6] tty/serial: atmel: changed the driver to work under at91-usart mfd
From: Radu Pirea @ 2018-06-04 16:59 UTC (permalink / raw)
To: broonie, nicolas.ferre, alexandre.belloni, lee.jones,
richard.genoud, robh+dt, mark.rutland, gregkh
Cc: linux-spi, linux-arm-kernel, linux-kernel, devicetree,
linux-serial, Radu Pirea
In-Reply-To: <20180604165943.31381-1-radu.pirea@microchip.com>
This patch modifies the place where resources and device tree properties
are searched.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
drivers/tty/serial/Kconfig | 1 +
drivers/tty/serial/atmel_serial.c | 41 ++++++++++++++++++-------------
2 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3682fd3e960c..25e55332f8b1 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -119,6 +119,7 @@ config SERIAL_ATMEL
depends on ARCH_AT91 || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
+ select MFD_AT91_USART
help
This enables the driver for the on-chip UARTs of the Atmel
AT91 processors.
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index df46a9e88c34..5c74e03396ef 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -193,8 +193,8 @@ static struct console atmel_console;
#if defined(CONFIG_OF)
static const struct of_device_id atmel_serial_dt_ids[] = {
- { .compatible = "atmel,at91rm9200-usart" },
- { .compatible = "atmel,at91sam9260-usart" },
+ { .compatible = "atmel,at91rm9200-usart-serial" },
+ { .compatible = "atmel,at91sam9260-usart-serial" },
{ /* sentinel */ }
};
#endif
@@ -915,6 +915,7 @@ static void atmel_tx_dma(struct uart_port *port)
static int atmel_prepare_tx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
dma_cap_mask_t mask;
struct dma_slave_config config;
int ret, nent;
@@ -922,7 +923,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+ atmel_port->chan_tx = dma_request_slave_channel(mfd_dev, "tx");
if (atmel_port->chan_tx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for tx DMA transfers\n",
@@ -1093,6 +1094,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
static int atmel_prepare_rx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
struct dma_async_tx_descriptor *desc;
dma_cap_mask_t mask;
struct dma_slave_config config;
@@ -1104,7 +1106,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
dma_cap_zero(mask);
dma_cap_set(DMA_CYCLIC, mask);
- atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+ atmel_port->chan_rx = dma_request_slave_channel(mfd_dev, "rx");
if (atmel_port->chan_rx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for rx DMA transfers\n",
@@ -1631,7 +1633,7 @@ static void atmel_tasklet_tx_func(unsigned long data)
static void atmel_init_property(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = pdev->dev.parent->of_node;
/* DMA/PDC usage specification */
if (of_property_read_bool(np, "atmel,use-dma-rx")) {
@@ -2222,8 +2224,8 @@ static const char *atmel_type(struct uart_port *port)
*/
static void atmel_release_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
release_mem_region(port->mapbase, size);
@@ -2238,8 +2240,8 @@ static void atmel_release_port(struct uart_port *port)
*/
static int atmel_request_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
if (!request_mem_region(port->mapbase, size, "atmel_serial"))
return -EBUSY;
@@ -2341,27 +2343,28 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
{
int ret;
struct uart_port *port = &atmel_port->uart;
+ struct platform_device *mpdev = to_platform_device(pdev->dev.parent);
atmel_init_property(atmel_port, pdev);
atmel_set_ops(port);
- uart_get_rs485_mode(&pdev->dev, &port->rs485);
+ uart_get_rs485_mode(&mpdev->dev, &port->rs485);
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
port->ops = &atmel_pops;
port->fifosize = 1;
port->dev = &pdev->dev;
- port->mapbase = pdev->resource[0].start;
- port->irq = pdev->resource[1].start;
+ port->mapbase = mpdev->resource[0].start;
+ port->irq = mpdev->resource[1].start;
port->rs485_config = atmel_config_rs485;
- port->membase = NULL;
+ port->membase = NULL;
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
- atmel_port->clk = clk_get(&pdev->dev, "usart");
+ atmel_port->clk = clk_get(&mpdev->dev, "usart");
if (IS_ERR(atmel_port->clk)) {
ret = PTR_ERR(atmel_port->clk);
atmel_port->clk = NULL;
@@ -2652,11 +2655,13 @@ static int atmel_serial_resume(struct platform_device *pdev)
static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
+ struct device *dev = pdev->dev.parent;
+
atmel_port->fifo_size = 0;
atmel_port->rts_low = 0;
atmel_port->rts_high = 0;
- if (of_property_read_u32(pdev->dev.of_node,
+ if (of_property_read_u32(dev->of_node,
"atmel,fifo-size",
&atmel_port->fifo_size))
return;
@@ -2694,13 +2699,15 @@ static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port,
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *atmel_port;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = pdev->dev.parent->of_node;
void *data;
int ret = -ENODEV;
bool rs485_enabled;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
+ pdev->dev.of_node = np;
+
ret = of_alias_get_id(np, "serial");
if (ret < 0)
/* port id not found in platform data nor device-tree aliases:
@@ -2845,7 +2852,7 @@ static struct platform_driver atmel_serial_driver = {
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.driver = {
- .name = "atmel_usart",
+ .name = "atmel_usart_serial",
.of_match_table = of_match_ptr(atmel_serial_dt_ids),
},
};
--
2.17.1
^ permalink raw reply related
* [PATCH 1/1] arm64: dts: rockchip: correct voltage selector Firefly-RK3399
From: Heinrich Schuchardt @ 2018-06-04 17:15 UTC (permalink / raw)
To: Rob Herring, Mark Rutland
Cc: devicetree, Heiko Stuebner, Vagrant Cascadian, Catalin Marinas,
Shawn Lin, Will Deacon, Kever Yang, linux-kernel, linux-rockchip,
Heinrich Schuchardt, Enric Balletbo i Serra, Pierre-Hugues Husson,
Jianqun Xu, linux-arm-kernel
Without this patch the Firefly-RK3399 board boot process hangs after these
lines:
fan53555-regulator 0-0040: FAN53555 Option[8] Rev[1] Detected!
fan53555-reg: supplied by vcc_sys
vcc1v8_s3: supplied by vcc_1v8
Blacklisting driver fan53555 allows booting.
The device tree uses a value of fcs,suspend-voltage-selector different to
any other board.
Changing this setting to the usual value is sufficient to enable booting.
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
arch/arm64/boot/dts/rockchip/rk3399-firefly.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-firefly.dts b/arch/arm64/boot/dts/rockchip/rk3399-firefly.dts
index 4f28628aa091..50940ef844a7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly.dts
@@ -458,7 +458,7 @@
vdd_cpu_b: regulator@40 {
compatible = "silergy,syr827";
reg = <0x40>;
- fcs,suspend-voltage-selector = <0>;
+ fcs,suspend-voltage-selector = <1>;
regulator-name = "vdd_cpu_b";
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1500000>;
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v3 4/6] mtd: rawnand: add NVIDIA Tegra NAND Flash controller driver
From: Randolph Maaßen @ 2018-06-04 17:16 UTC (permalink / raw)
To: Stefan Agner, boris.brezillon, dwmw2, computersforpeace,
marek.vasut, robh+dt, mark.rutland, thierry.reding
Cc: benjamin.lindqvist, pgaikwad, dev, mirza.krak, richard,
pdeschrijver, linux-kernel, krzk, jonathanh, devicetree,
linux-mtd, marcel, miquel.raynal, linux-tegra, digetx
In-Reply-To: <20180531221637.6017-5-stefan@agner.ch>
Am Freitag, den 01.06.2018, 00:16 +0200 schrieb Stefan Agner:
> Add support for the NAND flash controller found on NVIDIA
> Tegra 2 SoCs. This implementation does not make use of the
> command queue feature. Regular operations/data transfers are
> done in PIO mode. Page read/writes with hardware ECC make
> use of the DMA for data transfer.
>
> Signed-off-by: Lucas Stach <dev@lynxeye.de>
> Signed-off-by: Stefan Agner <stefan@agner.ch>
> ---
> MAINTAINERS | 7 +
> drivers/mtd/nand/raw/Kconfig | 6 +
> drivers/mtd/nand/raw/Makefile | 1 +
> drivers/mtd/nand/raw/tegra_nand.c | 1143
> +++++++++++++++++++++++++++++
> 4 files changed, 1157 insertions(+)
> create mode 100644 drivers/mtd/nand/raw/tegra_nand.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 58b9861ccf99..c2e5571c85d4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13844,6 +13844,13 @@ M: Laxman Dewangan <ldewangan@nvidia.
> com>
> S: Supported
> F: drivers/input/keyboard/tegra-kbc.c
>
> +TEGRA NAND DRIVER
> +M: Stefan Agner <stefan@agner.ch>
> +M: Lucas Stach <dev@lynxeye.de>
> +S: Maintained
> +F: Documentation/devicetree/bindings/mtd/nvidia-tegra20-
> nand.txt
> +F: drivers/mtd/nand/raw/tegra_nand.c
> +
> TEGRA PWM DRIVER
> M: Thierry Reding <thierry.reding@gmail.com>
> S: Supported
> diff --git a/drivers/mtd/nand/raw/Kconfig
> b/drivers/mtd/nand/raw/Kconfig
> index 19a2b283fbbe..e9093f52371e 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -534,4 +534,10 @@ config MTD_NAND_MTK
> Enables support for NAND controller on MTK SoCs.
> This controller is found on mt27xx, mt81xx, mt65xx SoCs.
>
> +config MTD_NAND_TEGRA
> + tristate "Support for NAND controller on NVIDIA Tegra"
> + depends on ARCH_TEGRA || COMPILE_TEST
> + help
> + Enables support for NAND flash controller on NVIDIA Tegra
> SoC.
> +
> endif # MTD_NAND
> diff --git a/drivers/mtd/nand/raw/Makefile
> b/drivers/mtd/nand/raw/Makefile
> index 165b7ef9e9a1..d5a5f9832b88 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) +=
> hisi504_nand.o
> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
> obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
> obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
> +obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
>
> nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
> nand-objs += nand_amd.o
> diff --git a/drivers/mtd/nand/raw/tegra_nand.c
> b/drivers/mtd/nand/raw/tegra_nand.c
> new file mode 100644
> index 000000000000..e9664f2938a3
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/tegra_nand.c
> @@ -0,0 +1,1143 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Stefan Agner <stefan@agner.ch>
> + * Copyright (C) 2014-2015 Lucas Stach <dev@lynxeye.de>
> + * Copyright (C) 2012 Avionic Design GmbH
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mtd/rawnand.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#define CMD 0x00
> +#define CMD_GO BIT(31)
> +#define CMD_CLE BIT(30)
> +#define CMD_ALE BIT(29)
> +#define CMD_PIO BIT(28)
> +#define CMD_TX BIT(27)
> +#define CMD_RX BIT(26)
> +#define CMD_SEC_CMD BIT(25)
> +#define CMD_AFT_DAT BIT(24)
> +#define CMD_TRANS_SIZE(x) (((x - 1) & 0xf)
> << 20)
> +#define CMD_A_VALID BIT(19)
> +#define CMD_B_VALID BIT(18)
> +#define CMD_RD_STATUS_CHK BIT(17)
> +#define CMD_RBSY_CHK BIT(16)
> +#define CMD_CE(x) BIT((8 + ((x) &
> 0x7)))
> +#define CMD_CLE_SIZE(x) (((x - 1) & 0x3) <<
> 4)
> +#define CMD_ALE_SIZE(x) (((x - 1) & 0xf) <<
> 0)
> +
> +#define STATUS 0x04
> +
> +#define ISR 0x08
> +#define ISR_CORRFAIL_ERR BIT(24)
> +#define ISR_UND BIT(7)
> +#define ISR_OVR BIT(6)
> +#define ISR_CMD_DONE BIT(5)
> +#define ISR_ECC_ERR BIT(4)
> +
> +#define IER 0x0c
> +#define IER_ERR_TRIG_VAL(x) (((x) & 0xf) <<
> 16)
> +#define IER_UND BIT(7)
> +#define IER_OVR BIT(6)
> +#define IER_CMD_DONE BIT(5)
> +#define IER_ECC_ERR BIT(4)
> +#define IER_GIE BIT(0)
> +
> +#define CFG 0x10
> +#define CFG_HW_ECC BIT(31)
> +#define CFG_ECC_SEL BIT(30)
> +#define CFG_ERR_COR BIT(29)
> +#define CFG_PIPE_EN BIT(28)
> +#define CFG_TVAL_4 (0 << 24)
> +#define CFG_TVAL_6 (1 << 24)
> +#define CFG_TVAL_8 (2 << 24)
> +#define CFG_SKIP_SPARE BIT(23)
> +#define CFG_BUS_WIDTH_16 BIT(21)
> +#define CFG_COM_BSY BIT(20)
> +#define CFG_PS_256 (0 << 16)
> +#define CFG_PS_512 (1 << 16)
> +#define CFG_PS_1024 (2 << 16)
> +#define CFG_PS_2048 (3 << 16)
> +#define CFG_PS_4096 (4 << 16)
> +#define CFG_SKIP_SPARE_SIZE_4 (0 << 14)
> +#define CFG_SKIP_SPARE_SIZE_8 (1 << 14)
> +#define CFG_SKIP_SPARE_SIZE_12 (2 << 14)
> +#define CFG_SKIP_SPARE_SIZE_16 (3 << 14)
> +#define CFG_TAG_BYTE_SIZE(x) ((x) & 0xff)
> +
> +#define TIMING_1 0x14
> +#define TIMING_TRP_RESP(x) (((x) & 0xf) <<
> 28)
> +#define TIMING_TWB(x) (((x) & 0xf)
> << 24)
> +#define TIMING_TCR_TAR_TRR(x) (((x) & 0xf)
> << 20)
> +#define TIMING_TWHR(x) (((x) & 0xf) << 16)
> +#define TIMING_TCS(x) (((x) & 0x3)
> << 14)
> +#define TIMING_TWH(x) (((x) & 0x3)
> << 12)
> +#define TIMING_TWP(x) (((x) & 0xf)
> << 8)
> +#define TIMING_TRH(x) (((x) & 0x3)
> << 4)
> +#define TIMING_TRP(x) (((x) & 0xf)
> << 0)
> +
> +#define RESP 0x18
> +
> +#define TIMING_2 0x1c
> +#define TIMING_TADL(x) ((x) & 0xf)
> +
> +#define CMD_1 0x20
> +#define CMD_2 0x24
> +#define ADDR_1 0x28
> +#define ADDR_2 0x2c
> +
> +#define DMA_CTRL 0x30
> +#define DMA_CTRL_GO BIT(31)
> +#define DMA_CTRL_IN (0 << 30)
> +#define DMA_CTRL_OUT BIT(30)
> +#define DMA_CTRL_PERF_EN BIT(29)
> +#define DMA_CTRL_IE_DONE BIT(28)
> +#define DMA_CTRL_REUSE BIT(27)
> +#define DMA_CTRL_BURST_1 (2 << 24)
> +#define DMA_CTRL_BURST_4 (3 << 24)
> +#define DMA_CTRL_BURST_8 (4 << 24)
> +#define DMA_CTRL_BURST_16 (5 << 24)
> +#define DMA_CTRL_IS_DONE BIT(20)
> +#define DMA_CTRL_EN_A BIT(2)
> +#define DMA_CTRL_EN_B BIT(1)
> +
> +#define DMA_CFG_A 0x34
> +#define DMA_CFG_B 0x38
> +
> +#define FIFO_CTRL 0x3c
> +#define FIFO_CTRL_CLR_ALL BIT(3)
> +
> +#define DATA_PTR 0x40
> +#define TAG_PTR 0x44
> +#define ECC_PTR 0x48
> +
> +#define DEC_STATUS 0x4c
> +#define DEC_STATUS_A_ECC_FAIL BIT(1)
> +#define DEC_STATUS_ERR_COUNT_MASK 0x00ff0000
> +#define DEC_STATUS_ERR_COUNT_SHIFT 16
> +
> +#define HWSTATUS_CMD 0x50
> +#define HWSTATUS_MASK 0x54
> +#define HWSTATUS_RDSTATUS_MASK(x) (((x) & 0xff) <<
> 24)
> +#define HWSTATUS_RDSTATUS_VALUE(x) (((x) & 0xff) <<
> 16)
> +#define HWSTATUS_RBSY_MASK(x) (((x) & 0xff)
> << 8)
> +#define HWSTATUS_RBSY_VALUE(x) (((x) & 0xff) << 0)
> +
> +#define BCH_CONFIG 0xcc
> +#define BCH_ENABLE BIT(0)
> +#define BCH_TVAL_4 (0 << 4)
> +#define BCH_TVAL_8 (1 << 4)
> +#define BCH_TVAL_14 (2 << 4)
> +#define BCH_TVAL_16 (3 << 4)
> +
> +#define DEC_STAT_RESULT 0xd0
> +#define DEC_STAT_BUF 0xd4
> +#define DEC_STAT_BUF_FAIL_SEC_FLAG_MASK 0xff000000
> +#define DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT 24
> +#define DEC_STAT_BUF_CORR_SEC_FLAG_MASK 0x00ff0000
> +#define DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT 16
> +#define DEC_STAT_BUF_MAX_CORR_CNT_MASK 0x00001f00
> +#define DEC_STAT_BUF_MAX_CORR_CNT_SHIFT 8
> +
> +#define OFFSET(val, off) ((val) < (off) ? 0 : (val) -
> (off))
> +
> +#define SKIP_SPARE_BYTES 4
> +#define BITS_PER_STEP_RS 18
> +#define BITS_PER_STEP_BCH 13
> +
> +struct tegra_nand_controller {
> + struct nand_hw_control controller;
> + void __iomem *regs;
> + struct clk *clk;
> + struct device *dev;
> + struct completion command_complete;
> + struct completion dma_complete;
> + bool last_read_error;
> + int cur_chip;
> + struct nand_chip *chip;
> +};
> +
> +struct tegra_nand_chip {
> + struct nand_chip chip;
> + struct gpio_desc *wp_gpio;
> + struct mtd_oob_region tag;
> +};
> +
> +static inline struct tegra_nand_controller *to_tegra_ctrl(
> + struct
> nand_hw_control *hw_ctrl)
> +{
> + return container_of(hw_ctrl, struct tegra_nand_controller,
> controller);
> +}
> +
> +static inline struct tegra_nand_chip *to_tegra_chip(struct nand_chip
> *chip)
> +{
> + return container_of(chip, struct tegra_nand_chip, chip);
> +}
> +
> +static int tegra_nand_ooblayout_rs_ecc(struct mtd_info *mtd, int
> section,
> + struct mtd_oob_region
> *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_RS * chip-
> >ecc.strength,
> + BITS_PER_BYTE);
> +
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = SKIP_SPARE_BYTES;
> + oobregion->length = round_up(bytes_per_step * chip-
> >ecc.steps, 4);
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_rs_free(struct mtd_info *mtd, int
> section,
> + struct mtd_oob_region
> *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_RS * chip-
> >ecc.strength,
> + BITS_PER_BYTE);
> +
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = SKIP_SPARE_BYTES +
> + round_up(bytes_per_step * chip-
> >ecc.steps, 4);
> + oobregion->length = mtd->oobsize - oobregion->offset;
> +
> + return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops tegra_nand_oob_rs_ops = {
> + .ecc = tegra_nand_ooblayout_rs_ecc,
> + .free = tegra_nand_ooblayout_rs_free,
> +};
> +
> +static int tegra_nand_ooblayout_bch_ecc(struct mtd_info *mtd, int
> section,
> + struct mtd_oob_region
> *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH * chip-
> >ecc.strength,
> + BITS_PER_BYTE);
> +
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = SKIP_SPARE_BYTES;
> + oobregion->length = round_up(bytes_per_step * chip-
> >ecc.steps, 4);
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_bch_free(struct mtd_info *mtd, int
> section,
> + struct mtd_oob_region
> *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH * chip-
> >ecc.strength,
> + BITS_PER_BYTE);
> +
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = SKIP_SPARE_BYTES +
> + round_up(bytes_per_step * chip-
> >ecc.steps, 4);
> + oobregion->length = mtd->oobsize - oobregion->offset;
> +
> + return 0;
> +}
> +
> +/*
> + * Layout with tag bytes is
> + *
> + * ---------------------------------------------------------------
> -----------
> + * | main area | skip bytes | tag bytes |
> parity | .. |
> + * ---------------------------------------------------------------
> -----------
> + *
> + * If not tag bytes are written, parity moves right after skip
> bytes!
> + */
> +static const struct mtd_ooblayout_ops tegra_nand_oob_bch_ops = {
> + .ecc = tegra_nand_ooblayout_bch_ecc,
> + .free = tegra_nand_ooblayout_bch_free,
> +};
> +
> +static irqreturn_t tegra_nand_irq(int irq, void *data)
> +{
> + struct tegra_nand_controller *ctrl = data;
> + u32 isr, dma;
> +
> + isr = readl_relaxed(ctrl->regs + ISR);
> + dma = readl_relaxed(ctrl->regs + DMA_CTRL);
> + dev_dbg(ctrl->dev, "isr %08x\n", isr);
> +
> + if (!isr && !(dma & DMA_CTRL_IS_DONE))
> + return IRQ_NONE;
> +
> + /*
> + * The bit name is somewhat missleading: This is also set
> when
> + * HW ECC was successful. The data sheet states:
> + * Correctable OR Un-correctable errors occurred in the DMA
> transfer...
> + */
> + if (isr & ISR_CORRFAIL_ERR)
> + ctrl->last_read_error = true;
> +
> + if (isr & ISR_CMD_DONE)
> + complete(&ctrl->command_complete);
> +
> + if (isr & ISR_UND)
> + dev_err(ctrl->dev, "FIFO underrun\n");
> +
> + if (isr & ISR_OVR)
> + dev_err(ctrl->dev, "FIFO overrun\n");
> +
> + /* handle DMA interrupts */
> + if (dma & DMA_CTRL_IS_DONE) {
> + writel_relaxed(dma, ctrl->regs + DMA_CTRL);
> + complete(&ctrl->dma_complete);
> + }
> +
> + /* clear interrupts */
> + writel_relaxed(isr, ctrl->regs + ISR);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const char * const tegra_nand_reg_names[] = {
> + "COMMAND",
> + "STATUS",
> + "ISR",
> + "IER",
> + "CONFIG",
> + "TIMING",
> + NULL,
> + "TIMING2",
> + "CMD_REG1",
> + "CMD_REG2",
> + "ADDR_REG1",
> + "ADDR_REG2",
> + "DMA_MST_CTRL",
> + "DMA_CFG_A",
> + "DMA_CFG_B",
> + "FIFO_CTRL",
> +};
> +
> +static void tegra_nand_dump_reg(struct tegra_nand_controller *ctrl)
> +{
> + u32 reg;
> + int i;
> +
> + dev_err(ctrl->dev, "Tegra NAND controller register dump\n");
> + for (i = 0; i < ARRAY_SIZE(tegra_nand_reg_names); i++) {
> + const char *reg_name = tegra_nand_reg_names[i];
> +
> + if (!reg_name)
> + continue;
> +
> + reg = readl_relaxed(ctrl->regs + (i * 4));
> + dev_err(ctrl->dev, "%s: 0x%08x\n", reg_name, reg);
> + }
> +}
> +
> +static int tegra_nand_cmd(struct nand_chip *chip,
> + const struct nand_subop *subop)
> +{
> + const struct nand_op_instr *instr;
> + const struct nand_op_instr *instr_data_in = NULL;
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> + unsigned int op_id, size = 0, offset = 0;
> + bool first_cmd = true;
> + u32 reg, cmd = 0;
> + int ret;
> +
> + for (op_id = 0; op_id < subop->ninstrs; op_id++) {
> + unsigned int naddrs, i;
> + const u8 *addrs;
> + u32 addr1 = 0, addr2 = 0;
> +
> + instr = &subop->instrs[op_id];
> +
> + switch (instr->type) {
> + case NAND_OP_CMD_INSTR:
> + if (first_cmd) {
> + cmd |= CMD_CLE;
> + writel_relaxed(instr-
> >ctx.cmd.opcode,
> + ctrl->regs + CMD_1);
> + } else {
> + cmd |= CMD_SEC_CMD;
> + writel_relaxed(instr-
> >ctx.cmd.opcode,
> + ctrl->regs + CMD_2);
> + }
> + first_cmd = false;
> + break;
> + case NAND_OP_ADDR_INSTR:
> + offset =
> nand_subop_get_addr_start_off(subop, op_id);
> + naddrs = nand_subop_get_num_addr_cyc(subop,
> op_id);
> + addrs = &instr->ctx.addr.addrs[offset];
> +
> + cmd |= CMD_ALE | CMD_ALE_SIZE(naddrs);
> + for (i = 0; i < min_t(unsigned int, 4,
> naddrs); i++)
> + addr1 |= *addrs++ << (BITS_PER_BYTE
> * i);
> + naddrs -= i;
> + for (i = 0; i < min_t(unsigned int, 4,
> naddrs); i++)
> + addr2 |= *addrs++ << (BITS_PER_BYTE
> * i);
> + writel_relaxed(addr1, ctrl->regs + ADDR_1);
> + writel_relaxed(addr2, ctrl->regs + ADDR_2);
> + break;
> +
> + case NAND_OP_DATA_IN_INSTR:
> + size = nand_subop_get_data_len(subop,
> op_id);
> + offset =
> nand_subop_get_data_start_off(subop, op_id);
> +
> + cmd |= CMD_TRANS_SIZE(size) | CMD_PIO |
> CMD_RX |
> + CMD_A_VALID;
> +
> + instr_data_in = instr;
> + break;
> +
> + case NAND_OP_DATA_OUT_INSTR:
> + size = nand_subop_get_data_len(subop,
> op_id);
> + offset =
> nand_subop_get_data_start_off(subop, op_id);
> +
> + cmd |= CMD_TRANS_SIZE(size) | CMD_PIO |
> CMD_TX |
> + CMD_A_VALID;
> +
> + memcpy(®, instr->ctx.data.buf.out +
> offset, size);
> + writel_relaxed(reg, ctrl->regs + RESP);
> +
> + break;
> + case NAND_OP_WAITRDY_INSTR:
> + cmd |= CMD_RBSY_CHK;
> + break;
> +
> + }
> + }
> +
> + cmd |= CMD_GO | CMD_CE(ctrl->cur_chip);
> + writel_relaxed(cmd, ctrl->regs + CMD);
> + ret = wait_for_completion_timeout(&ctrl->command_complete,
> + msecs_to_jiffies(500));
> + if (!ret) {
> + dev_err(ctrl->dev, "CMD timeout\n");
> + tegra_nand_dump_reg(ctrl);
> + return -ETIMEDOUT;
> + }
> +
> + if (instr_data_in) {
> + reg = readl_relaxed(ctrl->regs + RESP);
> + memcpy(instr_data_in->ctx.data.buf.in + offset,
> ®, size);
> + }
> +
> + return 0;
> +}
> +
> +static const struct nand_op_parser tegra_nand_op_parser =
> NAND_OP_PARSER(
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 4)),
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
> + NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)),
> + );
> +
> +static int tegra_nand_exec_op(struct nand_chip *chip,
> + const struct nand_operation *op,
> + bool check_only)
> +{
> + return nand_op_parser_exec_op(chip, &tegra_nand_op_parser,
> op,
> + check_only);
> +}
> +static void tegra_nand_select_chip(struct mtd_info *mtd, int
> chip_nr)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> +
> + ctrl->cur_chip = chip_nr;
> +}
> +
> +static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl,
> + struct nand_chip *chip, bool enable)
> +{
> + u32 reg;
> +
> + switch (chip->ecc.algo) {
> + case NAND_ECC_RS:
> + reg = readl_relaxed(ctrl->regs + CFG);
> + if (enable)
> + reg |= CFG_HW_ECC | CFG_ERR_COR;
> + else
> + reg &= ~(CFG_HW_ECC | CFG_ERR_COR);
> + writel_relaxed(reg, ctrl->regs + CFG);
> + break;
> + case NAND_ECC_BCH:
> + reg = readl_relaxed(ctrl->regs + BCH_CONFIG);
> + if (enable)
> + reg |= BCH_ENABLE;
> + else
> + reg &= ~BCH_ENABLE;
> + writel_relaxed(reg, ctrl->regs + BCH_CONFIG);
> + break;
> + default:
> + dev_err(ctrl->dev, "Unsupported hardware ECC
> algorithm\n");
> + break;
> + }
> +}
> +
> +static int tegra_nand_page_xfer(struct mtd_info *mtd, struct
> nand_chip *chip,
> + void *buf, int oob_required, int
> page,
> + bool read)
> +{
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> + struct tegra_nand_chip *nand = to_tegra_chip(chip);
> + enum dma_data_direction dir = read ? DMA_FROM_DEVICE :
> DMA_TO_DEVICE;
> + dma_addr_t dma_addr;
> + u32 cmd, dma_ctrl;
> + int ret, dma_len;
> +
> + if (read) {
> + writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_1);
> + writel_relaxed(NAND_CMD_READSTART, ctrl->regs +
> CMD_2);
> + } else {
> + writel_relaxed(NAND_CMD_SEQIN, ctrl->regs + CMD_1);
> + writel_relaxed(NAND_CMD_PAGEPROG, ctrl->regs +
> CMD_2);
> + }
> + cmd = CMD_CLE | CMD_SEC_CMD;
> +
> + /* Lower 16-bits are column, always 0 */
> + writel_relaxed(page << 16, ctrl->regs + ADDR_1);
> +
> + if (chip->options & NAND_ROW_ADDR_3) {
> + writel_relaxed(page >> 16, ctrl->regs + ADDR_2);
> + cmd |= CMD_ALE | CMD_ALE_SIZE(5);
> + } else {
> + cmd |= CMD_ALE | CMD_ALE_SIZE(4);
> + }
> +
> + dma_len = mtd->writesize + (oob_required ? mtd->oobsize :
> 0);
> + dma_addr = dma_map_single(ctrl->dev, buf, dma_len, dir);
> + ret = dma_mapping_error(ctrl->dev, dma_addr);
> + if (ret) {
> + dev_err(ctrl->dev, "dma mapping error\n");
> + return -EINVAL;
> + }
> +
> + writel_relaxed(mtd->writesize - 1, ctrl->regs + DMA_CFG_A);
> + writel_relaxed(dma_addr, ctrl->regs + DATA_PTR);
> +
> + if (oob_required) {
> + dma_addr_t dma_addr_tag = dma_addr + mtd->writesize;
> +
> + writel_relaxed(nand->tag.length - 1, ctrl->regs +
> DMA_CFG_B);
> + writel_relaxed(dma_addr_tag + nand->tag.offset,
> + ctrl->regs + TAG_PTR);
> + } else {
> + writel_relaxed(0, ctrl->regs + DMA_CFG_B);
> + writel_relaxed(0, ctrl->regs + TAG_PTR);
> + }
> +
> + dma_ctrl = DMA_CTRL_GO | DMA_CTRL_PERF_EN |
> + DMA_CTRL_IE_DONE | DMA_CTRL_IS_DONE |
> + DMA_CTRL_BURST_16 | DMA_CTRL_EN_A;
> + if (oob_required)
> + dma_ctrl |= DMA_CTRL_EN_B;
> + if (read)
> + dma_ctrl |= DMA_CTRL_IN | DMA_CTRL_REUSE;
> + else
> + dma_ctrl |= DMA_CTRL_OUT;
> +
> + writel_relaxed(dma_ctrl, ctrl->regs + DMA_CTRL);
> +
> + cmd |= CMD_GO | CMD_RBSY_CHK | CMD_TRANS_SIZE(9) |
> + CMD_CE(ctrl->cur_chip) | CMD_A_VALID;
> + if (oob_required)
> + cmd |= CMD_B_VALID;
> + if (read)
> + cmd |= CMD_RX;
> + else
> + cmd |= CMD_TX | CMD_AFT_DAT;
> +
> + writel_relaxed(cmd, ctrl->regs + CMD);
> +
> + ret = wait_for_completion_timeout(&ctrl->command_complete,
> + msecs_to_jiffies(500));
> + if (!ret) {
> + dev_err(ctrl->dev, "CMD timeout\n");
> + tegra_nand_dump_reg(ctrl);
> + ret = -ETIMEDOUT;
> + goto err_unmap_dma;
> + }
> +
> + ret = wait_for_completion_timeout(&ctrl->dma_complete,
> + msecs_to_jiffies(500));
> + if (!ret) {
> + dev_err(ctrl->dev, "DMA timeout\n");
> + tegra_nand_dump_reg(ctrl);
> + ret = -ETIMEDOUT;
> + goto err_unmap_dma;
> + }
> + ret = 0;
> +
> +err_unmap_dma:
> + dma_unmap_single(ctrl->dev, dma_addr, dma_len, dir);
> +
> + return ret;
> +}
> +
> +static int tegra_nand_read_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip,
> + uint8_t *buf, int
> oob_required, int page)
> +{
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> + u32 dec_stat, max_corr_cnt;
> + unsigned long fail_sec_flag;
> + int ret;
> +
> + tegra_nand_hw_ecc(ctrl, chip, true);
> + ret = tegra_nand_page_xfer(mtd, chip, buf, oob_required,
> page, true);
> + tegra_nand_hw_ecc(ctrl, chip, false);
> + if (ret)
> + return ret;
> +
> + /* No correctable or un-correctable errors, page must have 0
> bitflips */
> + if (!ctrl->last_read_error)
> + return 0;
> +
> + /*
> + * Correctable or un-correctable errors occurred. Use
> DEC_STAT_BUF
> + * which contains information for all ECC selections.
> + *
> + * Note that since we do not use Command Queues DEC_RESULT
> does not
> + * state the number of pages we can read from the
> DEC_STAT_BUF. But
> + * since CORRFAIL_ERR did occur during page read we do have
> a valid
> + * result in DEC_STAT_BUF.
> + */
> + ctrl->last_read_error = false;
> + dec_stat = readl_relaxed(ctrl->regs + DEC_STAT_BUF);
> +
> + fail_sec_flag = (dec_stat & DEC_STAT_BUF_FAIL_SEC_FLAG_MASK)
> >>
> + DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT;
> +
> + max_corr_cnt = (dec_stat & DEC_STAT_BUF_MAX_CORR_CNT_MASK)
> >>
> + DEC_STAT_BUF_MAX_CORR_CNT_SHIFT;
> +
> + if (fail_sec_flag) {
> + int bit, max_bitflips = 0;
> +
> + /*
> + * Check if all sectors in a page failed. If only
> some failed
> + * its definitly not an erased page and we can
> return error
> + * stats right away.
> + *
> + * E.g. controller might return fail_sec_flag with
> 0x4, which
> + * would mean only the third sector failed to
> correct.
> + */
> + if (fail_sec_flag ^ GENMASK(chip->ecc.steps - 1, 0))
> {
> + mtd->ecc_stats.failed +=
> hweight8(fail_sec_flag);
> + return max_corr_cnt;
> + }
> +
> + /*
> + * All sectors failed to correct, but the ECC isn't
> smart
> + * enough to figure out if a page is really
> completely erased.
> + * We check the read data here to figure out if it's
> a
> + * legitimate ECC error or only an erased page.
> + */
> + for_each_set_bit(bit, &fail_sec_flag, chip-
> >ecc.steps) {
> + u8 *data = buf + (chip->ecc.size * bit);
> +
> + ret = nand_check_erased_ecc_chunk(data,
> chip->ecc.size,
> + NULL, 0,
> + NULL, 0,
> + chip-
> >ecc.strength);
> + if (ret < 0)
> + mtd->ecc_stats.failed++;
> + else
> + max_bitflips = max(ret,
> max_bitflips);
> + }
> +
> + return max_t(unsigned int, max_corr_cnt,
> max_bitflips);
> + } else {
> + int corr_sec_flag;
> +
> + corr_sec_flag = (dec_stat &
> DEC_STAT_BUF_CORR_SEC_FLAG_MASK) >>
> + DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT;
> +
> + /*
> + * The value returned in the register is the maximum
> of
> + * bitflips encountered in any of the ECC regions.
> As there is
> + * no way to get the number of bitflips in a
> specific regions
> + * we are not able to deliver correct stats but
> instead
> + * overestimate the number of corrected bitflips by
> assuming
> + * that all regions where errors have been corrected
> + * encountered the maximum number of bitflips.
> + */
> + mtd->ecc_stats.corrected += max_corr_cnt *
> hweight8(corr_sec_flag);
> +
> + return max_corr_cnt;
> + }
> +
> +}
> +
> +static int tegra_nand_write_page_hwecc(struct mtd_info *mtd,
> + struct nand_chip *chip,
> + const uint8_t *buf, int
> oob_required,
> + int page)
> +{
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> + int ret;
> +
> + tegra_nand_hw_ecc(ctrl, chip, true);
> + ret = tegra_nand_page_xfer(mtd, chip, (void *)buf,
> oob_required, page,
> + false);
> + tegra_nand_hw_ecc(ctrl, chip, false);
> +
> + return ret;
> +}
> +
> +static void tegra_nand_setup_timing(struct tegra_nand_controller
> *ctrl,
> + const struct nand_sdr_timings
> *timings)
> +{
> + /*
> + * The period (and all other timings in this function) is in
> ps,
> + * so need to take care here to avoid integer overflows.
> + */
> + unsigned int rate = clk_get_rate(ctrl->clk) / 1000000;
> + unsigned int period = DIV_ROUND_UP(1000000, rate);
> + u32 val, reg = 0;
> +
> + val = DIV_ROUND_UP(max3(timings->tAR_min, timings->tRR_min,
> + timings->tRC_min), period);
> + reg |= TIMING_TCR_TAR_TRR(OFFSET(val, 3));
> +
> + val = DIV_ROUND_UP(max(max(timings->tCS_min, timings-
> >tCH_min),
> + max(timings->tALS_min, timings-
> >tALH_min)),
> + period);
> + reg |= TIMING_TCS(OFFSET(val, 2));
> +
> + val = DIV_ROUND_UP(max(timings->tRP_min, timings->tREA_max)
> + 6000,
> + period);
> + reg |= TIMING_TRP(OFFSET(val, 1)) |
> TIMING_TRP_RESP(OFFSET(val, 1));
> +
> + reg |= TIMING_TWB(OFFSET(DIV_ROUND_UP(timings->tWB_max,
> period), 1));
> + reg |= TIMING_TWHR(OFFSET(DIV_ROUND_UP(timings->tWHR_min,
> period), 1));
> + reg |= TIMING_TWH(OFFSET(DIV_ROUND_UP(timings->tWH_min,
> period), 1));
> + reg |= TIMING_TWP(OFFSET(DIV_ROUND_UP(timings->tWP_min,
> period), 1));
> + reg |= TIMING_TRH(OFFSET(DIV_ROUND_UP(timings->tREH_min,
> period), 1));
> +
> + writel_relaxed(reg, ctrl->regs + TIMING_1);
> +
> + val = DIV_ROUND_UP(timings->tADL_min, period);
> + reg = TIMING_TADL(OFFSET(val, 3));
> +
> + writel_relaxed(reg, ctrl->regs + TIMING_2);
> +}
> +
> +static int tegra_nand_setup_data_interface(struct mtd_info *mtd, int
> csline,
> + const struct
> nand_data_interface *conf)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip-
> >controller);
> + const struct nand_sdr_timings *timings;
> +
> + timings = nand_get_sdr_timings(conf);
> + if (IS_ERR(timings))
> + return PTR_ERR(timings);
> +
> + if (csline == NAND_DATA_IFACE_CHECK_ONLY)
> + return 0;
> +
> + tegra_nand_setup_timing(ctrl, timings);
> +
> + return 0;
> +}
> +
> +
> +const int rs_strength_bootable[] = { 4 };
> +const int rs_strength[] = { 4, 6, 8 };
> +const int bch_strength_bootable[] = { 8, 16 };
> +const int bch_strength[] = { 4, 8, 14, 16 };
> +
> +static int tegra_nand_get_strength(struct nand_chip *chip, const int
> *strength,
> + int strength_len, int oobsize)
> +{
> + bool maximize = chip->ecc.options & NAND_ECC_MAXIMIZE;
> + int i;
> +
> + /*
> + * Loop through available strengths. Backwards in case we
> try to
> + * maximize the BCH strength.
> + */
> + for (i = 0; i < strength_len; i++) {
> + int strength_sel, bytes_per_step, bytes_per_page;
> +
> + if (maximize) {
> + strength_sel = strength[strength_len - i -
> 1];
> + } else {
> + strength_sel = strength[i];
> +
> + if (strength_sel < chip->ecc_strength_ds)
> + continue;
> + }
> +
> + bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH *
> strength_sel,
> + BITS_PER_BYTE);
> + bytes_per_page = round_up(bytes_per_step * chip-
> >ecc.steps, 4);
> +
> + /* Check whether strength fits OOB */
> + if (bytes_per_page < (oobsize - SKIP_SPARE_BYTES))
> + return strength_sel;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int tegra_nand_select_strength(struct nand_chip *chip, int
> oobsize)
> +{
> + const int *strength;
> + int strength_len;
> +
> + switch (chip->ecc.algo) {
> + case NAND_ECC_RS:
> + if (chip->options & NAND_IS_BOOT_MEDIUM) {
> + strength = rs_strength_bootable;
> + strength_len =
> ARRAY_SIZE(rs_strength_bootable);
> + } else {
> + strength = rs_strength;
> + strength_len = ARRAY_SIZE(rs_strength);
> + }
> + break;
> + case NAND_ECC_BCH:
> + if (chip->options & NAND_IS_BOOT_MEDIUM) {
> + strength = bch_strength_bootable;
> + strength_len =
> ARRAY_SIZE(bch_strength_bootable);
> + } else {
> + strength = bch_strength;
> + strength_len = ARRAY_SIZE(bch_strength);
> + }
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return tegra_nand_get_strength(chip, strength, strength_len,
> oobsize);
> +}
> +
> +static int tegra_nand_chips_init(struct device *dev,
> + struct tegra_nand_controller *ctrl)
> +{
> + struct device_node *np = dev->of_node;
> + struct device_node *np_nand;
> + int nchips = of_get_child_count(np);
> + struct tegra_nand_chip *nand;
> + struct mtd_info *mtd;
> + struct nand_chip *chip;
> + unsigned long config, bch_config = 0;
> + int bits_per_step;
> + int ret;
> +
> + if (nchips != 1) {
> + dev_err(dev, "Currently only one NAND chip
> supported\n");
> + return -EINVAL;
> + }
> +
> + np_nand = of_get_next_child(np, NULL);
> +
> + nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
> + if (!nand)
> + return -ENOMEM;
> +
> + nand->wp_gpio = devm_gpiod_get_optional(dev, "wp",
> GPIOD_OUT_LOW);
> +
> + if (IS_ERR(nand->wp_gpio)) {
> + ret = PTR_ERR(nand->wp_gpio);
> + dev_err(dev, "Failed to request WP GPIO: %d\n",
> ret);
> + return ret;
> + }
> +
> + chip = &nand->chip;
> + chip->controller = &ctrl->controller;
> +
> + mtd = nand_to_mtd(chip);
> +
> + mtd->dev.parent = dev;
> + if (!mtd->name)
> + mtd->name = "tegra_nand";
> + mtd->owner = THIS_MODULE;
> +
> + nand_set_flash_node(chip, np_nand);
Hi,
i just tried this driver and it works great so far, thanks.
I just found, that assigning the of node after setting the mtd->name
makes it impossible to assign a name via devicetree label. I have read
the discussion about the label on this list, so I'm curious if this is
intentional? Setting mtd->name after nand_set_flash_node() enables the
label parameter.
> +
> + chip->options = NAND_NO_SUBPAGE_WRITE |
> NAND_USE_BOUNCE_BUFFER;
> + chip->exec_op = tegra_nand_exec_op;
> + chip->select_chip = tegra_nand_select_chip;
> + chip->setup_data_interface =
> tegra_nand_setup_data_interface;
> +
> + ret = nand_scan_ident(mtd, 1, NULL);
> + if (ret)
> + return ret;
> +
> + if (chip->bbt_options & NAND_BBT_USE_FLASH)
> + chip->bbt_options |= NAND_BBT_NO_OOB;
> +
> + chip->ecc.mode = NAND_ECC_HW;
> + chip->ecc.size = 512;
> + chip->ecc.steps = mtd->writesize / chip->ecc.size;
> + if (chip->ecc_step_ds != 512) {
> + dev_err(dev, "Unsupported step size %d\n", chip-
> >ecc_step_ds);
> + return -EINVAL;
> + }
> +
> + chip->ecc.read_page = tegra_nand_read_page_hwecc;
> + chip->ecc.write_page = tegra_nand_write_page_hwecc;
> +
> + config = readl_relaxed(ctrl->regs + CFG);
> + config |= CFG_PIPE_EN | CFG_SKIP_SPARE |
> CFG_SKIP_SPARE_SIZE_4;
> +
> + if (chip->options & NAND_BUSWIDTH_16)
> + config |= CFG_BUS_WIDTH_16;
> +
> + if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
> + if (mtd->writesize < 2048)
> + chip->ecc.algo = NAND_ECC_RS;
> + else
> + chip->ecc.algo = NAND_ECC_BCH;
> + }
> +
> + if (chip->ecc.algo == NAND_ECC_BCH && mtd->writesize < 2048)
> {
> + dev_err(dev, "BCH supportes 2K or 4K page size
> only\n");
> + return -EINVAL;
> + }
> +
> + if (!chip->ecc.strength) {
> + ret = tegra_nand_select_strength(chip, mtd-
> >oobsize);
> + if (ret < 0) {
> + dev_err(dev, "No valid strenght found,
> minimum %d\n",
> + chip->ecc_strength_ds);
> + return ret;
> + }
> +
> + chip->ecc.strength = ret;
> + }
> +
> + switch (chip->ecc.algo) {
> + case NAND_ECC_RS:
> + bits_per_step = BITS_PER_STEP_RS * chip-
> >ecc.strength;
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops);
> + switch (chip->ecc.strength) {
> + case 4:
> + config |= CFG_ECC_SEL | CFG_TVAL_4;
> + break;
> + case 6:
> + config |= CFG_ECC_SEL | CFG_TVAL_6;
> + break;
> + case 8:
> + config |= CFG_ECC_SEL | CFG_TVAL_8;
> + break;
> + default:
> + dev_err(dev, "ECC strength %d not
> supported\n",
> + chip->ecc.strength);
> + return -EINVAL;
> + }
> + break;
> + case NAND_ECC_BCH:
> + bits_per_step = BITS_PER_STEP_BCH * chip-
> >ecc.strength;
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops);
> + switch (chip->ecc.strength) {
> + case 4:
> + bch_config = BCH_TVAL_4;
> + break;
> + case 8:
> + bch_config = BCH_TVAL_8;
> + break;
> + case 14:
> + bch_config = BCH_TVAL_14;
> + break;
> + case 16:
> + bch_config = BCH_TVAL_16;
> + break;
> + default:
> + dev_err(dev, "ECC strength %d not
> supported\n",
> + chip->ecc.strength);
> + return -EINVAL;
> + }
> + break;
> + default:
> + dev_err(dev, "ECC algorithm not supported\n");
> + return -EINVAL;
> + }
> +
> + dev_info(dev, "Using %s with strength %d per 512 byte
> step\n",
> + chip->ecc.algo == NAND_ECC_BCH ? "BCH" :
> "RS",
> + chip->ecc.strength);
> +
> + chip->ecc.bytes = DIV_ROUND_UP(bits_per_step,
> BITS_PER_BYTE);
> +
> + switch (mtd->writesize) {
> + case 256:
> + config |= CFG_PS_256;
> + break;
> + case 512:
> + config |= CFG_PS_512;
> + break;
> + case 1024:
> + config |= CFG_PS_1024;
> + break;
> + case 2048:
> + config |= CFG_PS_2048;
> + break;
> + case 4096:
> + config |= CFG_PS_4096;
> + break;
> + default:
> + dev_err(dev, "Unsupported writesize %d\n", mtd-
> >writesize);
> + return -ENODEV;
> + }
> +
> + writel_relaxed(config, ctrl->regs + CFG);
> + writel_relaxed(bch_config, ctrl->regs + BCH_CONFIG);
> +
> + ret = nand_scan_tail(mtd);
> + if (ret)
> + return ret;
> +
> + mtd_ooblayout_free(mtd, 0, &nand->tag);
> +
> + config |= CFG_TAG_BYTE_SIZE(nand->tag.length - 1);
> + writel_relaxed(config, ctrl->regs + CFG);
> +
> + ret = mtd_device_register(mtd, NULL, 0);
> + if (ret) {
> + dev_err(dev, "Failed to register mtd device: %d\n",
> ret);
> + nand_cleanup(chip);
> + return ret;
> + }
> +
> + ctrl->chip = chip;
> +
> + return 0;
> +}
> +
> +static int tegra_nand_probe(struct platform_device *pdev)
> +{
> + struct reset_control *rst;
> + struct tegra_nand_controller *ctrl;
> + struct resource *res;
> + unsigned long reg;
> + int irq, err = 0;
> +
> + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
> + if (!ctrl)
> + return -ENOMEM;
> +
> + ctrl->dev = &pdev->dev;
> + nand_hw_control_init(&ctrl->controller);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ctrl->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(ctrl->regs))
> + return PTR_ERR(ctrl->regs);
> +
> + rst = devm_reset_control_get(&pdev->dev, "nand");
> + if (IS_ERR(rst))
> + return PTR_ERR(rst);
> +
> + ctrl->clk = devm_clk_get(&pdev->dev, "nand");
> + if (IS_ERR(ctrl->clk))
> + return PTR_ERR(ctrl->clk);
> +
> + err = clk_prepare_enable(ctrl->clk);
> + if (err)
> + return err;
> +
> + err = reset_control_reset(rst);
> + if (err)
> + goto err_disable_clk;
> +
> + reg = HWSTATUS_RDSTATUS_MASK(1) | HWSTATUS_RDSTATUS_VALUE(0)
> |
> + HWSTATUS_RBSY_MASK(NAND_STATUS_READY) |
> + HWSTATUS_RBSY_VALUE(NAND_STATUS_READY);
> + writel_relaxed(NAND_CMD_STATUS, ctrl->regs + HWSTATUS_CMD);
> + writel_relaxed(reg, ctrl->regs + HWSTATUS_MASK);
> +
> + init_completion(&ctrl->command_complete);
> + init_completion(&ctrl->dma_complete);
> +
> + /* clear interrupts */
> + reg = readl_relaxed(ctrl->regs + ISR);
> + writel_relaxed(reg, ctrl->regs + ISR);
> +
> + irq = platform_get_irq(pdev, 0);
> + err = devm_request_irq(&pdev->dev, irq, tegra_nand_irq, 0,
> + dev_name(&pdev->dev), ctrl);
> + if (err)
> + goto err_disable_clk;
> +
> + writel_relaxed(DMA_CTRL_IS_DONE, ctrl->regs + DMA_CTRL);
> +
> + /* enable interrupts */
> + reg = IER_UND | IER_OVR | IER_CMD_DONE | IER_GIE;
> + writel_relaxed(reg, ctrl->regs + IER);
> +
> + /* reset config */
> + writel_relaxed(0, ctrl->regs + CFG);
> +
> + err = tegra_nand_chips_init(ctrl->dev, ctrl);
> + if (err)
> + goto err_disable_clk;
> +
> + platform_set_drvdata(pdev, ctrl);
> +
> + return 0;
> +
> +err_disable_clk:
> + clk_disable_unprepare(ctrl->clk);
> + return err;
> +}
> +
> +static int tegra_nand_remove(struct platform_device *pdev)
> +{
> + struct tegra_nand_controller *ctrl =
> platform_get_drvdata(pdev);
> +
> + nand_release(nand_to_mtd(ctrl->chip));
> +
> + clk_disable_unprepare(ctrl->clk);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id tegra_nand_of_match[] = {
> + { .compatible = "nvidia,tegra20-nand" },
> + { /* sentinel */ }
> +};
> +
> +static struct platform_driver tegra_nand_driver = {
> + .driver = {
> + .name = "tegra-nand",
> + .of_match_table = tegra_nand_of_match,
> + },
> + .probe = tegra_nand_probe,
> + .remove = tegra_nand_remove,
> +};
> +module_platform_driver(tegra_nand_driver);
> +
> +MODULE_DESCRIPTION("NVIDIA Tegra NAND driver");
> +MODULE_AUTHOR("Thierry Reding <thierry.reding@nvidia.com>");
> +MODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>");
> +MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DEVICE_TABLE(of, tegra_nand_of_match);
Sorry for any noise/mistake, I'm new to kernel development
Greetings
Randolph Maaßen
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCH v2] arm: sun4i: Add support for Pengpod 1000 tablet
From: Bob Ham @ 2018-06-04 17:33 UTC (permalink / raw)
To: Maxime Ripard
Cc: Chen-Yu Tsai, Rob Herring, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20180604081302.hcn7j36gt6xeyquq@flea>
[-- Attachment #1.1: Type: text/plain, Size: 2290 bytes --]
On 04/06/18 09:13, Maxime Ripard wrote:
> On Sat, Jun 02, 2018 at 05:03:13PM +0100, Bob Ham wrote:
>> + * This file is dual-licensed: you can use it either under the terms
>> + * of the GPL or the X11 license, at your option. Note that this dual
>> + * licensing only applies to this file, and not this project as a
>> + * whole.
>> + * The above copyright notice and this permission notice shall be
>> + * included in all copies or substantial portions of the Software.
> And this is redundant with the SPDX header.
The X11 license notice states explicitly that the notice has to be
included in the file. Wouldn't removing it be a violation of the license?
>> + brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;
>
> Each step should increase the perceived brightness by roughly 1/Nth, N
> being the number of steps. Usually PWM backlights don't work like that.
FYI, this was copied from another .dts file. All of the other
brightness-levels settings in sun{4,5,7}i .dts files follow similar
patterns:
sun4i-a10-dserve-dsrv9703c.dts: brightness-levels = <0 10
20 30 40 50 60 70 80 90 100>;
sun4i-a10-inet1.dts: brightness-levels = <0 10 20 30 40 50 60
70 80 90 100>;
sun4i-a10-pov-protab2-ips9.dts: brightness-levels = <0 10
20 30 40 50 60 70 80 90 100>;
sun5i-a13-empire-electronix-d709.dts: brightness-levels = <0 10
20 30 40 50 60 70 80 90 100>;
sun5i-a13-utoo-p66.dts: brightness-levels = <0 30 40 50 60 70 80
90 100>;
sun5i-gr8-evb.dts: brightness-levels = <0 10 20 30 40 50 60
70 80 90 100>;
sun7i-a20-wexler-tab7200.dts: brightness-levels = <0 10 20 30 40
50 60 70 80 90 100>;
I'll take the brightness-levels from sun8i-a83t-tbs-a711.dts which
follows a more appropriate pattern:
sun8i-a83t-tbs-a711.dts: brightness-levels = <0 1 2 4 8 16
32 64 128 255>;
Thanks,
Bob
--
Bob Ham <rah-2mWpNWY8JZLk1uMJSBkQmQ@public.gmane.org>
for (;;) { ++pancakes; }
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 2913 bytes --]
^ permalink raw reply
* [PATCH v9 0/7] i2c: Add FSI-attached I2C master algorithm
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
This series adds an algorithm for an I2C master physically located on an FSI
slave device. The I2C master has multiple ports, each of which may be connected
to an I2C slave. Access to the I2C master registers is achieved over FSI bus.
Due to the multi-port nature of the I2C master, the driver instantiates a new
I2C adapter for each port connected to a slave. The connected ports should be
defined in the device tree under the I2C master device.
Changes since v8
- Drop unecessary else statements
- Use i++ instead of ++i
- Use kzalloc/kfree instead of devm_kzalloc/devm_kfree for port structure
- Drop the list_empty check in remove
Changes since v7
- Fix grammer in Kconfig (a -> an)
- Change I2C registers to use BIT and GENMASK
- Remove custom macros and use FIELD_PREP and FIELD_GET
- Fix a few unecessary initializations and "return rc" that are always zero
- Clean up the read/write fifo functions a bit
- Few other clean-up items
Changes since v6
- Remove spinlock for reset functionality; it's unecessary and doesn't work
with the latest FSI core.
- Use a mutex instead of a semaphore, and don't wait for timeout to get the
lock.
- Use usleeps instead of schedule_timeout; it's not worth the overhead when
the wait should be very short in between sending the command and receiving
the response.
Changes since v5
- Fix reset functionality and do a reset after every transfer failure
Eddie James (7):
dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
i2c: Add FSI-attached I2C master algorithm
i2c: fsi: Add port structures
i2c: fsi: Add abort and hardware reset procedures
i2c: fsi: Add transfer implementation
i2c: fsi: Add I2C master locking
i2c: fsi: Add bus recovery
Documentation/devicetree/bindings/i2c/i2c-fsi.txt | 40 ++
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-fsi.c | 721 ++++++++++++++++++++++
4 files changed, 773 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
create mode 100644 drivers/i2c/busses/i2c-fsi.c
--
1.8.3.1
^ permalink raw reply
* [PATCH v9 1/7] dt-bindings: i2c: Add FSI-attached I2C master dt binding documentation
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Document the bindings.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/i2c/i2c-fsi.txt | 40 +++++++++++++++++++++++
1 file changed, 40 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-fsi.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-fsi.txt b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
new file mode 100644
index 0000000..b1be2ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-fsi.txt
@@ -0,0 +1,40 @@
+Device-tree bindings for FSI-attached I2C master and busses
+-----------------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,i2c-fsi";
+ - reg = < address size >; : The FSI CFAM address and address
+ space size.
+ - #address-cells = <1>; : Number of address cells in child
+ nodes.
+ - #size-cells = <0>; : Number of size cells in child nodes.
+ - child nodes : Nodes to describe busses off the I2C
+ master.
+
+Child node required properties:
+ - reg = < port number > : The port number on the I2C master.
+
+Child node optional properties:
+ - child nodes : Nodes to describe devices on the I2C
+ bus.
+
+Examples:
+
+ i2c@1800 {
+ compatible = "ibm,i2c-fsi";
+ reg = < 0x1800 0x400 >;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c-bus@0 {
+ reg = <0>;
+ };
+
+ i2c-bus@1 {
+ reg = <1>;
+
+ eeprom@50 {
+ compatible = "vendor,dev-name";
+ };
+ };
+ };
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 2/7] i2c: Add FSI-attached I2C master algorithm
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Add register definitions for FSI-attached I2C master and functions to
access those registers over FSI. Add an FSI driver so that our I2C bus
is probed up during an FSI scan.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/Kconfig | 11 ++
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-fsi.c | 234 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 246 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-fsi.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2e..cddd159 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1330,4 +1330,15 @@ config I2C_ZX2967
This driver can also be built as a module. If so, the module will be
called i2c-zx2967.
+config I2C_FSI
+ tristate "FSI I2C driver"
+ depends on FSI
+ help
+ Driver for FSI bus attached I2C masters. These are I2C masters that
+ are connected to the system over an FSI bus, instead of the more
+ common PCI or MMIO interface.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-fsi.
+
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a86914..4909fd6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -137,5 +137,6 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
+obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
new file mode 100644
index 0000000..e1b183c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSI-attached I2C master algorithm
+ *
+ * Copyright 2018 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fsi.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define FSI_ENGID_I2C 0x7
+
+#define I2C_DEFAULT_CLK_DIV 6
+
+/* i2c registers */
+#define I2C_FSI_FIFO 0x00
+#define I2C_FSI_CMD 0x04
+#define I2C_FSI_MODE 0x08
+#define I2C_FSI_WATER_MARK 0x0C
+#define I2C_FSI_INT_MASK 0x10
+#define I2C_FSI_INT_COND 0x14
+#define I2C_FSI_OR_INT_MASK 0x14
+#define I2C_FSI_INTS 0x18
+#define I2C_FSI_AND_INT_MASK 0x18
+#define I2C_FSI_STAT 0x1C
+#define I2C_FSI_RESET_I2C 0x1C
+#define I2C_FSI_ESTAT 0x20
+#define I2C_FSI_RESET_ERR 0x20
+#define I2C_FSI_RESID_LEN 0x24
+#define I2C_FSI_SET_SCL 0x24
+#define I2C_FSI_PORT_BUSY 0x28
+#define I2C_FSI_RESET_SCL 0x2C
+#define I2C_FSI_SET_SDA 0x30
+#define I2C_FSI_RESET_SDA 0x34
+
+/* cmd register */
+#define I2C_CMD_WITH_START BIT(31)
+#define I2C_CMD_WITH_ADDR BIT(30)
+#define I2C_CMD_RD_CONT BIT(29)
+#define I2C_CMD_WITH_STOP BIT(28)
+#define I2C_CMD_FORCELAUNCH BIT(27)
+#define I2C_CMD_ADDR GENMASK(23, 17)
+#define I2C_CMD_READ BIT(16)
+#define I2C_CMD_LEN GENMASK(15, 0)
+
+/* mode register */
+#define I2C_MODE_CLKDIV GENMASK(31, 16)
+#define I2C_MODE_PORT GENMASK(15, 10)
+#define I2C_MODE_ENHANCED BIT(3)
+#define I2C_MODE_DIAG BIT(2)
+#define I2C_MODE_PACE_ALLOW BIT(1)
+#define I2C_MODE_WRAP BIT(0)
+
+/* watermark register */
+#define I2C_WATERMARK_HI GENMASK(15, 12)
+#define I2C_WATERMARK_LO GENMASK(7, 4)
+
+#define I2C_FIFO_HI_LVL 4
+#define I2C_FIFO_LO_LVL 4
+
+/* interrupt register */
+#define I2C_INT_INV_CMD BIT(15)
+#define I2C_INT_PARITY BIT(14)
+#define I2C_INT_BE_OVERRUN BIT(13)
+#define I2C_INT_BE_ACCESS BIT(12)
+#define I2C_INT_LOST_ARB BIT(11)
+#define I2C_INT_NACK BIT(10)
+#define I2C_INT_DAT_REQ BIT(9)
+#define I2C_INT_CMD_COMP BIT(8)
+#define I2C_INT_STOP_ERR BIT(7)
+#define I2C_INT_BUSY BIT(6)
+#define I2C_INT_IDLE BIT(5)
+
+#define I2C_INT_ENABLE 0x0000ff80
+#define I2C_INT_ERR 0x0000fcc0
+
+/* status register */
+#define I2C_STAT_INV_CMD BIT(31)
+#define I2C_STAT_PARITY BIT(30)
+#define I2C_STAT_BE_OVERRUN BIT(29)
+#define I2C_STAT_BE_ACCESS BIT(28)
+#define I2C_STAT_LOST_ARB BIT(27)
+#define I2C_STAT_NACK BIT(26)
+#define I2C_STAT_DAT_REQ BIT(25)
+#define I2C_STAT_CMD_COMP BIT(24)
+#define I2C_STAT_STOP_ERR BIT(23)
+#define I2C_STAT_MAX_PORT GENMASK(19, 16)
+#define I2C_STAT_ANY_INT BIT(15)
+#define I2C_STAT_SCL_IN BIT(11)
+#define I2C_STAT_SDA_IN BIT(10)
+#define I2C_STAT_PORT_BUSY BIT(9)
+#define I2C_STAT_SELF_BUSY BIT(8)
+#define I2C_STAT_FIFO_COUNT GENMASK(7, 0)
+
+#define I2C_STAT_ERR 0xfc800000
+#define I2C_STAT_ANY_RESP 0xff800000
+
+/* extended status register */
+#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24)
+#define I2C_ESTAT_SCL_IN_SY BIT(15)
+#define I2C_ESTAT_SDA_IN_SY BIT(14)
+#define I2C_ESTAT_S_SCL BIT(13)
+#define I2C_ESTAT_S_SDA BIT(12)
+#define I2C_ESTAT_M_SCL BIT(11)
+#define I2C_ESTAT_M_SDA BIT(10)
+#define I2C_ESTAT_HI_WATER BIT(9)
+#define I2C_ESTAT_LO_WATER BIT(8)
+#define I2C_ESTAT_PORT_BUSY BIT(7)
+#define I2C_ESTAT_SELF_BUSY BIT(6)
+#define I2C_ESTAT_VERSION GENMASK(4, 0)
+
+struct fsi_i2c_master {
+ struct fsi_device *fsi;
+ u8 fifo_size;
+};
+
+static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
+ u32 *data)
+{
+ int rc;
+ __be32 data_be;
+
+ rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be));
+ if (rc)
+ return rc;
+
+ *data = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
+ u32 *data)
+{
+ __be32 data_be = cpu_to_be32p(data);
+
+ return fsi_device_write(fsi, reg, &data_be, sizeof(data_be));
+}
+
+static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
+{
+ int rc;
+ u32 mode = I2C_MODE_ENHANCED, extended_status, watermark;
+ u32 interrupt = 0;
+
+ /* since we use polling, disable interrupts */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+ if (rc)
+ return rc;
+
+ mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status);
+ if (rc)
+ return rc;
+
+ i2c->fifo_size = FIELD_GET(I2C_ESTAT_FIFO_SZ, extended_status);
+ watermark = FIELD_PREP(I2C_WATERMARK_HI,
+ i2c->fifo_size - I2C_FIFO_HI_LVL);
+ watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL);
+
+ return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
+}
+
+static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ return -EOPNOTSUPP;
+}
+
+static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_10BIT_ADDR
+ | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm fsi_i2c_algorithm = {
+ .master_xfer = fsi_i2c_xfer,
+ .functionality = fsi_i2c_functionality,
+};
+
+static int fsi_i2c_probe(struct device *dev)
+{
+ struct fsi_i2c_master *i2c;
+ int rc;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->fsi = to_fsi_dev(dev);
+
+ rc = fsi_i2c_dev_init(i2c);
+ if (rc)
+ return rc;
+
+ dev_set_drvdata(dev, i2c);
+
+ return 0;
+}
+
+static const struct fsi_device_id fsi_i2c_ids[] = {
+ { FSI_ENGID_I2C, FSI_VERSION_ANY },
+ { 0 }
+};
+
+static struct fsi_driver fsi_i2c_driver = {
+ .id_table = fsi_i2c_ids,
+ .drv = {
+ .name = "i2c-fsi",
+ .bus = &fsi_bus_type,
+ .probe = fsi_i2c_probe,
+ },
+};
+
+module_fsi_driver(fsi_i2c_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("FSI attached I2C master");
+MODULE_LICENSE("GPL");
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 3/7] i2c: fsi: Add port structures
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Add and initialize I2C adapters for each port on the FSI-attached I2C
master. Ports for each master are defined in the devicetree.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/i2c-fsi.c | 90 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index e1b183c..12130c3 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -17,7 +17,10 @@
#include <linux/fsi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
#define FSI_ENGID_I2C 0x7
@@ -123,6 +126,14 @@
struct fsi_i2c_master {
struct fsi_device *fsi;
u8 fifo_size;
+ struct list_head ports;
+};
+
+struct fsi_i2c_port {
+ struct list_head list;
+ struct i2c_adapter adapter;
+ struct fsi_i2c_master *master;
+ u16 port;
};
static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -176,9 +187,38 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
}
+static int fsi_i2c_set_port(struct fsi_i2c_port *port)
+{
+ int rc;
+ struct fsi_device *fsi = port->master->fsi;
+ u32 mode, dummy = 0;
+
+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
+ return 0;
+
+ mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ /* reset engine when port is changed */
+ return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+}
+
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
+ int rc;
+ struct fsi_i2c_port *port = adap->algo_data;
+
+ rc = fsi_i2c_set_port(port);
+ if (rc)
+ return rc;
+
return -EOPNOTSUPP;
}
@@ -196,23 +236,72 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
static int fsi_i2c_probe(struct device *dev)
{
struct fsi_i2c_master *i2c;
+ struct fsi_i2c_port *port;
+ struct device_node *np;
int rc;
+ u32 port_no;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->fsi = to_fsi_dev(dev);
+ INIT_LIST_HEAD(&i2c->ports);
rc = fsi_i2c_dev_init(i2c);
if (rc)
return rc;
+ /* Add adapter for each i2c port of the master. */
+ for_each_available_child_of_node(dev->of_node, np) {
+ rc = of_property_read_u32(np, "reg", &port_no);
+ if (rc || port_no > USHRT_MAX)
+ continue;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ break;
+
+ port->master = i2c;
+ port->port = port_no;
+
+ port->adapter.owner = THIS_MODULE;
+ port->adapter.dev.of_node = np;
+ port->adapter.dev.parent = dev;
+ port->adapter.algo = &fsi_i2c_algorithm;
+ port->adapter.algo_data = port;
+
+ snprintf(port->adapter.name, sizeof(port->adapter.name),
+ "i2c_bus-%u", port_no);
+
+ rc = i2c_add_adapter(&port->adapter);
+ if (rc < 0) {
+ dev_err(dev, "Failed to register adapter: %d\n", rc);
+ kfree(port);
+ continue;
+ }
+
+ list_add(&port->list, &i2c->ports);
+ }
+
dev_set_drvdata(dev, i2c);
return 0;
}
+static int fsi_i2c_remove(struct device *dev)
+{
+ struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
+ struct fsi_i2c_port *port;
+
+ list_for_each_entry(port, &i2c->ports, list) {
+ i2c_del_adapter(&port->adapter);
+ kfree(port);
+ }
+
+ return 0;
+}
+
static const struct fsi_device_id fsi_i2c_ids[] = {
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
{ 0 }
@@ -224,6 +313,7 @@ static int fsi_i2c_probe(struct device *dev)
.name = "i2c-fsi",
.bus = &fsi_bus_type,
.probe = fsi_i2c_probe,
+ .remove = fsi_i2c_remove,
},
};
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 4/7] i2c: fsi: Add abort and hardware reset procedures
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Add abort procedure for failed transfers. Add engine and bus reset
procedures to recover from as many faults as possible.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/i2c-fsi.c | 179 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 179 insertions(+)
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 12130c3..101b9c5 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -12,10 +12,12 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fsi.h>
#include <linux/i2c.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -123,6 +125,20 @@
#define I2C_ESTAT_SELF_BUSY BIT(6)
#define I2C_ESTAT_VERSION GENMASK(4, 0)
+/* port busy register */
+#define I2C_PORT_BUSY_RESET BIT(31)
+
+/* wait for command complete or data request */
+#define I2C_CMD_SLEEP_MAX_US 500
+#define I2C_CMD_SLEEP_MIN_US 50
+
+/* wait after reset; choose time from legacy driver */
+#define I2C_RESET_SLEEP_MAX_US 2000
+#define I2C_RESET_SLEEP_MIN_US 1000
+
+/* choose timeout length from legacy driver; it's well tested */
+#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100)
+
struct fsi_i2c_master {
struct fsi_device *fsi;
u8 fifo_size;
@@ -209,6 +225,169 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
}
+static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
+{
+ int i, rc;
+ u32 mode, stat, ext, dummy = 0;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ mode |= I2C_MODE_DIAG;
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < 9; i++) {
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+ if (rc)
+ return rc;
+ }
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+ if (rc)
+ return rc;
+
+ mode &= ~I2C_MODE_DIAG;
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ if (rc)
+ return rc;
+
+ /* check for hardware fault */
+ if (!(stat & I2C_STAT_SCL_IN) || !(stat & I2C_STAT_SDA_IN)) {
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &ext);
+ if (rc)
+ return rc;
+
+ dev_err(&i2c->fsi->dev, "bus stuck status[%08X] ext[%08X]\n",
+ stat, ext);
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_reset(struct fsi_i2c_master *i2c, u16 port)
+{
+ int rc;
+ u32 mode, stat, dummy = 0;
+
+ /* reset engine */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ if (rc)
+ return rc;
+
+ /* re-init engine */
+ rc = fsi_i2c_dev_init(i2c);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+
+ /* set port; default after reset is 0 */
+ if (port) {
+ mode &= ~I2C_MODE_PORT;
+ mode |= FIELD_PREP(I2C_MODE_PORT, port);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ if (rc)
+ return rc;
+ }
+
+ /* reset busy register; hw workaround */
+ dummy = I2C_PORT_BUSY_RESET;
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+ if (rc)
+ return rc;
+
+ /* force bus reset */
+ rc = fsi_i2c_reset_bus(i2c);
+ if (rc)
+ return rc;
+
+ /* reset errors */
+ dummy = 0;
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+ if (rc)
+ return rc;
+
+ /* wait for command complete */
+ usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
+
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ if (rc)
+ return rc;
+
+ if (stat & I2C_STAT_CMD_COMP)
+ return rc;
+
+ /* failed to get command complete; reset engine again */
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ if (rc)
+ return rc;
+
+ /* re-init engine again */
+ return fsi_i2c_dev_init(i2c);
+}
+
+static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
+{
+ int rc;
+ unsigned long start;
+ u32 cmd = I2C_CMD_WITH_STOP;
+ struct fsi_device *fsi = port->master->fsi;
+
+ rc = fsi_i2c_reset(port->master, port->port);
+ if (rc)
+ return rc;
+
+ /* skip final stop command for these errors */
+ if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
+ return 0;
+
+ /* write stop command */
+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd);
+ if (rc)
+ return rc;
+
+ /* wait until we see command complete in the master */
+ start = jiffies;
+
+ do {
+ rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_CMD_COMP)
+ return 0;
+
+ usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
+ } while (time_after(start + I2C_ABORT_TIMEOUT, jiffies));
+
+ return -ETIMEDOUT;
+}
+
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 5/7] i2c: fsi: Add transfer implementation
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Execute I2C transfers from the FSI-attached I2C master. Use polling
instead of interrupts as we have no hardware IRQ over FSI.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/i2c-fsi.c | 195 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 193 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 101b9c5..4e5964e 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -150,6 +150,7 @@ struct fsi_i2c_port {
struct i2c_adapter adapter;
struct fsi_i2c_master *master;
u16 port;
+ u16 xfrd;
};
static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
@@ -225,6 +226,99 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
}
+static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ bool stop)
+{
+ struct fsi_i2c_master *i2c = port->master;
+ u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+
+ port->xfrd = 0;
+
+ if (msg->flags & I2C_M_RD)
+ cmd |= I2C_CMD_READ;
+
+ if (stop || msg->flags & I2C_M_STOP)
+ cmd |= I2C_CMD_WITH_STOP;
+
+ cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);
+ cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
+
+ return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+}
+
+static int fsi_i2c_get_op_bytes(int op_bytes)
+{
+ /* fsi is limited to max 4 byte aligned ops */
+ if (op_bytes > 4)
+ return 4;
+ else if (op_bytes == 3)
+ return 2;
+ return op_bytes;
+}
+
+static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+ int write;
+ int rc;
+ struct fsi_i2c_master *i2c = port->master;
+ int bytes_to_write = i2c->fifo_size - fifo_count;
+ int bytes_remaining = msg->len - port->xfrd;
+
+ bytes_to_write = min(bytes_to_write, bytes_remaining);
+
+ while (bytes_to_write) {
+ write = fsi_i2c_get_op_bytes(bytes_to_write);
+
+ rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], write);
+ if (rc)
+ return rc;
+
+ port->xfrd += write;
+ bytes_to_write -= write;
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ u8 fifo_count)
+{
+ int read;
+ int rc;
+ struct fsi_i2c_master *i2c = port->master;
+ int bytes_to_read;
+ int xfr_remaining = msg->len - port->xfrd;
+ u32 dummy;
+
+ bytes_to_read = min_t(int, fifo_count, xfr_remaining);
+
+ while (bytes_to_read) {
+ read = fsi_i2c_get_op_bytes(bytes_to_read);
+
+ if (xfr_remaining) {
+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+ &msg->buf[port->xfrd], read);
+ if (rc)
+ return rc;
+
+ port->xfrd += read;
+ xfr_remaining -= read;
+ } else {
+ /* no more buffer but data in fifo, need to clear it */
+ rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
+ read);
+ if (rc)
+ return rc;
+ }
+
+ bytes_to_read -= read;
+ }
+
+ return 0;
+}
+
static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
{
int i, rc;
@@ -388,17 +482,114 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
return -ETIMEDOUT;
}
+static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
+ struct i2c_msg *msg, u32 status)
+{
+ int rc;
+ u8 fifo_count;
+
+ if (status & I2C_STAT_ERR) {
+ rc = fsi_i2c_abort(port, status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_INV_CMD)
+ return -EINVAL;
+
+ if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
+ I2C_STAT_BE_ACCESS))
+ return -EPROTO;
+
+ if (status & I2C_STAT_NACK)
+ return -ENXIO;
+
+ if (status & I2C_STAT_LOST_ARB)
+ return -EAGAIN;
+
+ if (status & I2C_STAT_STOP_ERR)
+ return -EBADMSG;
+
+ return -EIO;
+ }
+
+ if (status & I2C_STAT_DAT_REQ) {
+ fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status);
+
+ if (msg->flags & I2C_M_RD)
+ return fsi_i2c_read_fifo(port, msg, fifo_count);
+
+ return fsi_i2c_write_fifo(port, msg, fifo_count);
+ }
+
+ if (status & I2C_STAT_CMD_COMP) {
+ if (port->xfrd < msg->len)
+ return -ENODATA;
+
+ return msg->len;
+ }
+
+ return 0;
+}
+
+static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
+ unsigned long timeout)
+{
+ u32 status = 0;
+ int rc;
+ unsigned long start = jiffies;
+
+ do {
+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
+ &status);
+ if (rc)
+ return rc;
+
+ if (status & I2C_STAT_ANY_RESP) {
+ rc = fsi_i2c_handle_status(port, msg, status);
+ if (rc < 0)
+ return rc;
+
+ /* cmd complete and all data xfrd */
+ if (rc == msg->len)
+ return 0;
+
+ /* need to xfr more data, but maybe don't need wait */
+ continue;
+ }
+
+ usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
+ } while (time_after(start + timeout, jiffies));
+
+ return -ETIMEDOUT;
+}
+
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
- int rc;
+ int i, rc;
+ unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
+ struct i2c_msg *msg;
rc = fsi_i2c_set_port(port);
if (rc)
return rc;
- return -EOPNOTSUPP;
+ for (i = 0; i < num; i++) {
+ msg = msgs + i;
+ start_time = jiffies;
+
+ rc = fsi_i2c_start(port, msg, i == num - 1);
+ if (rc)
+ return rc;
+
+ rc = fsi_i2c_wait(port, msg,
+ adap->timeout - (jiffies - start_time));
+ if (rc)
+ return rc;
+ }
+
+ return 0;
}
static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 6/7] i2c: fsi: Add I2C master locking
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Since there are many ports per master, each with it's own adapter and
chardev, we need some locking to prevent transfers from changing the
master state while other transfers are in progress.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/i2c-fsi.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 4e5964e..6e6e2b1 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/slab.h>
@@ -143,6 +144,7 @@ struct fsi_i2c_master {
struct fsi_device *fsi;
u8 fifo_size;
struct list_head ports;
+ struct mutex lock;
};
struct fsi_i2c_port {
@@ -569,11 +571,14 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int i, rc;
unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *master = port->master;
struct i2c_msg *msg;
+ mutex_lock(&master->lock);
+
rc = fsi_i2c_set_port(port);
if (rc)
- return rc;
+ goto unlock;
for (i = 0; i < num; i++) {
msg = msgs + i;
@@ -581,15 +586,17 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
rc = fsi_i2c_start(port, msg, i == num - 1);
if (rc)
- return rc;
+ goto unlock;
rc = fsi_i2c_wait(port, msg,
adap->timeout - (jiffies - start_time));
if (rc)
- return rc;
+ goto unlock;
}
- return 0;
+unlock:
+ mutex_unlock(&master->lock);
+ return rc;
}
static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
@@ -615,6 +622,7 @@ static int fsi_i2c_probe(struct device *dev)
if (!i2c)
return -ENOMEM;
+ mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
INIT_LIST_HEAD(&i2c->ports);
--
1.8.3.1
^ permalink raw reply related
* [PATCH v9 7/7] i2c: fsi: Add bus recovery
From: Eddie James @ 2018-06-04 19:00 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, devicetree, wsa, robh+dt, benh, joel, mark.rutland,
gregkh, rdunlap, andy.shevchenko, Eddie James
In-Reply-To: <1528138850-18259-1-git-send-email-eajames@linux.vnet.ibm.com>
Bus recovery should reset the engine and force clock the bus 9 times
to recover most situations.
Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
drivers/i2c/busses/i2c-fsi.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 6e6e2b1..21aa50d 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -605,6 +605,24 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
| I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
}
+static int fsi_i2c_recover_bus(struct i2c_adapter *adap)
+{
+ int rc;
+ struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_master *master = port->master;
+
+ mutex_lock(&master->lock);
+
+ rc = fsi_i2c_reset(master, port->port);
+
+ mutex_unlock(&master->lock);
+ return rc;
+}
+
+static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
+ .recover_bus = fsi_i2c_recover_bus,
+};
+
static const struct i2c_algorithm fsi_i2c_algorithm = {
.master_xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality,
@@ -647,6 +665,7 @@ static int fsi_i2c_probe(struct device *dev)
port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm;
+ port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
port->adapter.algo_data = port;
snprintf(port->adapter.name, sizeof(port->adapter.name),
--
1.8.3.1
^ permalink raw reply related
* [PATCH] dt-bindings: display: renesas: du: document R8A77980 bindings
From: Sergei Shtylyov @ 2018-06-04 19:04 UTC (permalink / raw)
To: Laurent Pinchart, David Airlie, Rob Herring, dri-devel,
linux-renesas-soc, devicetree
Cc: Mark Rutland
Document the R-Car V3H (R8A77980) SoC in the R-Car DU bindings; the DU
hardware seems the same as in the R-Car V3M (R8A77970).
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
---
The patch is against the 'drm-next' branch of David Airlie's 'linux.git' repo.
Documentation/devicetree/bindings/display/renesas,du.txt | 2 ++
1 file changed, 2 insertions(+)
Index: linux/Documentation/devicetree/bindings/display/renesas,du.txt
===================================================================
--- linux.orig/Documentation/devicetree/bindings/display/renesas,du.txt
+++ linux/Documentation/devicetree/bindings/display/renesas,du.txt
@@ -14,6 +14,7 @@ Required Properties:
- "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
- "renesas,du-r8a7796" for R8A7796 (R-Car M3-W) compatible DU
- "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU
+ - "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU
- "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU
- reg: the memory-mapped I/O registers base address and length
@@ -60,6 +61,7 @@ corresponding to each DU output.
R8A7795 (R-Car H3) DPAD 0 HDMI 0 HDMI 1 LVDS 0
R8A7796 (R-Car M3-W) DPAD 0 HDMI 0 LVDS 0 -
R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - -
+ R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - -
R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 -
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH 01/13] dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips
From: Marcel Holtmann @ 2018-06-04 19:11 UTC (permalink / raw)
To: Hans de Goede
Cc: Rob Herring, Johan Hedberg, Martin Blumenstingl, Jeremy Cline,
BlueZ development, linux-serial, linux-acpi, devicetree
In-Reply-To: <94b57c4a-985e-250a-f91f-e0f0cd97be1e@redhat.com>
Hi Hans,
>>>>>>> This adds the documentation for Bluetooth functionality of the Realtek
>>>>>>> RTL8723BS and RTL8723DS.
>>>>>>> Both are SDIO wifi chips with an additional Bluetooth module which is
>>>>>>> connected via UART to the host.
>>>>>>>
>>>>>>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>>>>>> Signed-off-by: Jeremy Cline <jeremy@jcline.org>
>>>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>>>>> ---
>>>>>>> .../bindings/net/realtek-bluetooth.txt | 41 +++++++++++++++++++
>>>>>>> 1 file changed, 41 insertions(+)
>>>>>>> create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>>>>>>
>>>>>>> diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.txt b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>>>>>> new file mode 100644
>>>>>>> index 000000000000..1491329c4cd1
>>>>>>> --- /dev/null
>>>>>>> +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>>>>>> @@ -0,0 +1,41 @@
>>>>>>> +Realtek Bluetooth Chips
>>>>>>> +-----------------------
>>>>>>> +
>>>>>>> +This documents the binding structure and common properties for serial
>>>>>>> +attached Realtek devices.
>>>>>>> +
>>>>>>> +Serial attached Realtek devices shall be a child node of the host UART
>>>>>>> +device the slave device is attached to. See ../serial/slave-device.txt
>>>>>>> +for more information
>>>>>>> +
>>>>>>> +Required properties:
>>>>>>> +- compatible: should contain one of the following:
>>>>>>> + * "realtek,rtl8723bs-bluetooth"
>>>>>>> + * "realtek,rtl8723ds-bluetooth"
>>>>>>> +
>>>>>>> +Optional properties:
>>>>>>> +- realtek,config-data: Bluetooth chipset configuration data which is
>>>>>>> + needed for communication (it typically contains
>>>>>>> + board specific settings like the baudrate and
>>>>>>> + whether flow-control is used).
>>>>>>> + This is an array of u8 values.
>>>>>>> +- enable-gpios: GPIO specifier, used to enable/disable the BT module
>>>>>>> +- reset-gpios: GPIO specifier, used to reset the BT module
>>>>>>> +
>>>>>>> +
>>>>>>> +Example:
>>>>>>> +
>>>>>>> +&uart {
>>>>>>> + ...
>>>>>>> +
>>>>>>> + bluetooth {
>>>>>>> + compatible = "realtek,rtl8723bs-bluetooth";
>>>>>>> + enable-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
>>>>>>> + reset-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
>>>>>>> + realtek,config-data = /bits/ 8 <
>>>>>>> + 0x55 0xab 0x23 0x87 0x29 0x00 0xf4 0x00 0x01 0x01 0xf6 0x00
>>>>>>> + 0x02 0x81 0x00 0xfa 0x00 0x02 0x12 0x80 0x0c 0x00 0x10 0x02
>>>>>>> + 0x80 0x92 0x04 0x50 0xc5 0xea 0x19 0xe1 0x1b 0xfd 0xaf 0x5f
>>>>>>> + 0x01 0xa4 0x0b 0xd9 0x00 0x01 0x0f 0xe4 0x00 0x01 0x08>;
>>>>>>> + };
>>>>>>> +};
>>>>>>
>>>>>> we actually need to agree on this config-data. As I wrote in an earlier email some time ago, the actual config-data files are just memory portions that will overload the default config area. I wrote tools/rtlfw.c to parse these.
>>>>>>
>>>>>> So now I wonder if we can just read the current configuration and run with that when no extra blob is provided. That way it would always work and we might just give the config-data filename here. Mostly this is the for PCM and UART settings anyway.
>>>>>
>>>>> I've been thinking about this too and 2 different solutions come to mind.
>>>>>
>>>>> Note this is all x86 specific, I think it best to solve this for x86 first
>>>>> and then we can apply the lessons learned there to ARM/devicetree when
>>>>> someone comes along who wants to hook-up things on ARM.
>>>>>
>>>>> My first idea was to stick with a config blob using the firmware_load
>>>>> mechanism, but to add a postfix to the filename based on the ACPI
>>>>> HID (hardware-id) of the serdev device, so in practice this means
>>>>> we would try to load:
>>>>>
>>>>> /lib/firmware/rtl_bt/rtl8723bs_config-OBDA8723.bin
>>>>>
>>>>> On most x86 devices, so far all my testing on a bunch of different
>>>>> devices has shown that the same config works for all x86 devices
>>>>> (I took the config from a Chuwi Vi8 tablet which uses 3000000bps
>>>>> + hardware flowcontrol).
>>>>>
>>>>> This way we can put the config in linux-firmware, without it
>>>>> getting loaded on ARM boards where it might very well be wrong.
>>>>>
>>>>>
>>>>> My second idea is to include a default config inside the driver,
>>>>> which can be optionally overridden by a file in
>>>>> /lib/firmware/rtl_bt and/or dt. Combined with module-options to
>>>>> override the baudrate and flowcontrol setting (and patch the
>>>>> builtin config accordingly).
>>>>>
>>>>>
>>>>> Your idea to read back the config from the device is also
>>>>> interesting, but I don't think that will gain us anything because
>>>>> AFAIK the whole purpose of the board specific config file
>>>>> bits is that the chip lack eeprom space to store this info,
>>>>> so we will just always get some generic defaults.
>>>>>
>>>>>
>>>>> I've run your rtlfw tool on a bunch of different config files and
>>>>> there are a lot of unknown fields, and even of the known fields
>>>>> I don't think we understand all the bits. So all in all the
>>>>> config file is a bit of a black box and as such I believe it
>>>>> would be best to just treat it as such and I personally
>>>>> prefer my first idea, which is to add a postfix to the
>>>>> firmware filename request, so on x86 load:
>>>>>
>>>>> /lib/firmware/rtl_bt/rtl8723bs_config-OBDA8723.bin
>>>>>
>>>>> And on devicetree we could postfix things with the machine
>>>>> model string (as returned by of_flat_dt_get_machine_name())
>>>> If the firmware file is going to depend on the DT, then you might as
>>>> well just put the data into the DT.
>>>
>>> Ok, note we don't need the entire firmware in DT, just a (binary
>>> format) config file for the firmware. The question is if we are
>>> going to try and break up the info from the config-file and then
>>> regenerate it inside the kernel driver. Or if we are just going
>>> to put the say 60 bytes in DT as an u8 array as the original
>>> binding proposed by Martin Blumenstingl suggests.
>>>
>>> Since we only know the meaning for a couple of the bytes in
>>> the file, which is typically 40 - 60 bytes big, I think it
>>> is probably best to just treat it as a blob.
>> wouldn’t it be still better to then just reference the firmware file or an identifier to build it? Putting the pure binary blob into the DT seems kinda counterproductive and hardware to update. After all it most likely depends on the firmware loaded since it is the config ram space that this changes.
>
> Ah I see, so you suggest simply putting an identifier
> in the DT which we then use to build the config-file
> filename to request. That pretty much matches with what
> I have in mind for the x86 code-paths as well as for
> the somewhat related broadcom wifi nvram files.
>
> So yes that sounds like a good idea.
>
> With that said, given that I'm only testing this on x86
> I think that for v2 of the patch-set it is probably best
> to just drop the DT specific bits. Then when someone
> comes along who wants to add the necessary glue for DT
> based platforms they can brush the dust of the DT specific
> patches and submit an actually tested version of them.
>
> So for v2 I plan to just drop the DT bits and add a
> patch to use the ACPI HID as an identifier in the
> config-file filename requested on ACPI platforms.
that might be a good idea. We can also discuss to actually load a mapping file via request firmware. Something that I have been working on for Broadcom devices. So for companies that are not so Linux friendly, you can easily grab the Windows driver and extract the firmware files.
>> As a note, there are some hints that the config file is required for some firmware since it changes some memory around to make it successful boot.
>>>> There's a similar issue on QCom Dragonboards. The WiFi (and BT?)
>>>> firmware files are supposedly board specific, so folks don't want to put
>>>> them into linux-firmware. But it also seems to be unknown as to which
>>>> parts are board specific are not.
>>>
>>> In my experience with broadcom and now the rtl8723bs the
>>> actual firmware and the needed config data are separate.
>>>
>>> Hmm that is actually no entirely true, for broadcom the
>>> bluetooth patchram file depends on the clockcrystal freq
>>> used on the board, so there are 2 versions of it for a
>>> single chip, 1 for each of the 2 different freqs used.
>> are you using .hex or .hcd files for Broadcom? The .hex files are pure patchram, while the .hcd can actually contain extra HCI commands. I remember that some .hcd files contain also HCI commands to do extra settings. Maybe we need a tool that shows the details of these files. We have this for nokfw and rtlfw now.
>
> I'm using hcd files for broadcom.
so I added tools/bcmfw to analyze HCD files. We might also need a compare HCD files option. However you might be able to compare the HCD files that you think are different for some of your hardware.
Regards
Marcel
^ permalink raw reply
* Re: [PATCH v9 5/7] i2c: fsi: Add transfer implementation
From: Andy Shevchenko @ 2018-06-04 19:14 UTC (permalink / raw)
To: Eddie James
Cc: linux-i2c, Linux Kernel Mailing List, devicetree, Wolfram Sang,
Rob Herring, Benjamin Herrenschmidt, Joel Stanley, Mark Rutland,
Greg Kroah-Hartman, Randy Dunlap
In-Reply-To: <1528138850-18259-6-git-send-email-eajames@linux.vnet.ibm.com>
On Mon, Jun 4, 2018 at 10:00 PM, Eddie James <eajames@linux.vnet.ibm.com> wrote:
> Execute I2C transfers from the FSI-attached I2C master. Use polling
> instead of interrupts as we have no hardware IRQ over FSI.
>
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---
> drivers/i2c/busses/i2c-fsi.c | 195 ++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 193 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
> index 101b9c5..4e5964e 100644
> --- a/drivers/i2c/busses/i2c-fsi.c
> +++ b/drivers/i2c/busses/i2c-fsi.c
> @@ -150,6 +150,7 @@ struct fsi_i2c_port {
> struct i2c_adapter adapter;
> struct fsi_i2c_master *master;
> u16 port;
> + u16 xfrd;
> };
>
> static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
> @@ -225,6 +226,99 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
> return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
> }
>
> +static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
> + bool stop)
> +{
> + struct fsi_i2c_master *i2c = port->master;
> + u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
> +
> + port->xfrd = 0;
> +
> + if (msg->flags & I2C_M_RD)
> + cmd |= I2C_CMD_READ;
> +
> + if (stop || msg->flags & I2C_M_STOP)
> + cmd |= I2C_CMD_WITH_STOP;
> +
> + cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr >> 1);
Did you consider to unify READ + ADDR as ADDR_8BIT?
> + cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
> +
> + return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
> +}
> +
> +static int fsi_i2c_get_op_bytes(int op_bytes)
> +{
> + /* fsi is limited to max 4 byte aligned ops */
> + if (op_bytes > 4)
> + return 4;
> + else if (op_bytes == 3)
Since you dropped one of 'else', another also is not needed.
> + return 2;
> + return op_bytes;
> +}
> +
> +static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
> + u8 fifo_count)
> +{
> + int write;
> + int rc;
> + struct fsi_i2c_master *i2c = port->master;
> + int bytes_to_write = i2c->fifo_size - fifo_count;
> + int bytes_remaining = msg->len - port->xfrd;
> +
> + bytes_to_write = min(bytes_to_write, bytes_remaining);
> +
> + while (bytes_to_write) {
> + write = fsi_i2c_get_op_bytes(bytes_to_write);
> +
> + rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
> + &msg->buf[port->xfrd], write);
> + if (rc)
> + return rc;
> +
> + port->xfrd += write;
> + bytes_to_write -= write;
> + }
Just to be sure, if by some reason write == 0 in the loop for a while,
would it become infinite loop?
> +
> + return 0;
> +}
> +
> +static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
> + u8 fifo_count)
> +{
> + int read;
> + int rc;
> + struct fsi_i2c_master *i2c = port->master;
> + int bytes_to_read;
> + int xfr_remaining = msg->len - port->xfrd;
> + u32 dummy;
> +
> + bytes_to_read = min_t(int, fifo_count, xfr_remaining);
> +
> + while (bytes_to_read) {
> + read = fsi_i2c_get_op_bytes(bytes_to_read);
> +
> + if (xfr_remaining) {
> + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
> + &msg->buf[port->xfrd], read);
> + if (rc)
> + return rc;
> +
> + port->xfrd += read;
> + xfr_remaining -= read;
> + } else {
> + /* no more buffer but data in fifo, need to clear it */
> + rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
> + read);
> + if (rc)
> + return rc;
> + }
> +
> + bytes_to_read -= read;
> + }
Ditto for read == 0.
> +
> + return 0;
> +}
> +
> static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c)
> {
> int i, rc;
> @@ -388,17 +482,114 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
> return -ETIMEDOUT;
> }
>
> +static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
> + struct i2c_msg *msg, u32 status)
> +{
> + int rc;
> + u8 fifo_count;
> +
> + if (status & I2C_STAT_ERR) {
> + rc = fsi_i2c_abort(port, status);
> + if (rc)
> + return rc;
> +
> + if (status & I2C_STAT_INV_CMD)
> + return -EINVAL;
> +
> + if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
> + I2C_STAT_BE_ACCESS))
> + return -EPROTO;
> +
> + if (status & I2C_STAT_NACK)
> + return -ENXIO;
> +
> + if (status & I2C_STAT_LOST_ARB)
> + return -EAGAIN;
> +
> + if (status & I2C_STAT_STOP_ERR)
> + return -EBADMSG;
> +
> + return -EIO;
> + }
> +
> + if (status & I2C_STAT_DAT_REQ) {
> + fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status);
> +
> + if (msg->flags & I2C_M_RD)
> + return fsi_i2c_read_fifo(port, msg, fifo_count);
> +
> + return fsi_i2c_write_fifo(port, msg, fifo_count);
> + }
> +
> + if (status & I2C_STAT_CMD_COMP) {
> + if (port->xfrd < msg->len)
> + return -ENODATA;
> +
> + return msg->len;
> + }
> +
> + return 0;
> +}
> +
> +static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
> + unsigned long timeout)
> +{
> + u32 status = 0;
> + int rc;
> + unsigned long start = jiffies;
> +
> + do {
> + rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
> + &status);
> + if (rc)
> + return rc;
> +
> + if (status & I2C_STAT_ANY_RESP) {
> + rc = fsi_i2c_handle_status(port, msg, status);
> + if (rc < 0)
> + return rc;
> +
> + /* cmd complete and all data xfrd */
> + if (rc == msg->len)
> + return 0;
> +
> + /* need to xfr more data, but maybe don't need wait */
> + continue;
> + }
> +
> + usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
> + } while (time_after(start + timeout, jiffies));
> +
> + return -ETIMEDOUT;
> +}
> +
> static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> int num)
> {
> - int rc;
> + int i, rc;
> + unsigned long start_time;
> struct fsi_i2c_port *port = adap->algo_data;
> + struct i2c_msg *msg;
>
> rc = fsi_i2c_set_port(port);
> if (rc)
> return rc;
>
> - return -EOPNOTSUPP;
> + for (i = 0; i < num; i++) {
> + msg = msgs + i;
> + start_time = jiffies;
> +
> + rc = fsi_i2c_start(port, msg, i == num - 1);
> + if (rc)
> + return rc;
> +
> + rc = fsi_i2c_wait(port, msg,
> + adap->timeout - (jiffies - start_time));
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> }
>
> static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
> --
> 1.8.3.1
>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v6 0/2] regulator: add QCOM RPMh regulator driver
From: David Collins @ 2018-06-04 19:15 UTC (permalink / raw)
To: broonie, lgirdwood, robh+dt, mark.rutland
Cc: David Collins, linux-arm-msm, linux-arm-kernel, devicetree,
linux-kernel, rnayak, sboyd, dianders
This patch series adds a driver and device tree binding documentation for
PMIC regulator control via Resource Power Manager-hardened (RPMh) on some
Qualcomm Technologies, Inc. SoCs such as SDM845. RPMh is a hardware block
which contains several accelerators which are used to manage various
hardware resources that are shared between the processors of the SoC. The
final hardware state of a regulator is determined within RPMh by performing
max aggregation of the requests made by all of the processors.
The RPMh regulator driver depends upon the RPMh driver [1] and command DB
driver [2] which are both still undergoing review. It also depends upon
three recent regulator changes: [3], [4], and [5].
Changes since v5 [6]:
- Removed unused constants
- Added Reviewed-by tag for DT patch 1/2
Changes since v4 [7]:
- Removed support for DT properties qcom,regulator-drms-modes and
qcom,drms-mode-max-microamps
- Specified fixed DRMS high power mode minimum limits for LDO type
regulators
- Removed DRMS support for SMPS and BOB type regulators
- Simplified voltage caching logic
Changes since v3 [8]:
- Removed support for DT properties qcom,regulator-initial-microvolt
and qcom,headroom-microvolt
- Renamed DT property qcom,allowed-drms-modes to be
qcom,regulator-drms-modes
- Updated DT binding documentation to mention which common regulator
bindings can be used for qcom-rpmh-regulator devices
- Added voltage caching so that voltage requests are only sent to RPMh
after the regulator has been enabled at least once
- Changed 'voltage_selector' default value to be -ENOTRECOVERABLE to
interact with [5]
- Initialized 'enabled' to -EINVAL so that unused regulators are
disabled by regulator_late_cleanup()
- Removed rpmh_regulator_load_default_parameters() as it is no longer
needed
- Updated the mode usage description in qcom,rpmh-regulator.h
Changes since v2 [9]:
- Replaced '_' with '-' in device tree supply property names
- Renamed qcom_rpmh-regulator.c to be qcom-rpmh-regulator.c
- Updated various DT property names to use "microvolt" and "microamp"
- Moved allowed modes constraint specification out of the driver [4]
- Replaced rpmh_client with device pointer to match new RPMh API [1]
- Corrected drms mode threshold checking
- Initialized voltage_selector to -EINVAL when not specified in DT
- Added constants for PMIC regulator hardware modes
- Corrected type sign of mode mapping tables
- Made variable names for mode arrays plural
- Simplified Kconfig depends on
- Removed unnecessary constants and struct fields
- Added some descriptive comments
Changes since v1 [10]:
- Addressed review feedback from Doug, Mark, and Stephen
- Replaced set_voltage()/get_voltage() callbacks with set_voltage_sel()/
get_voltage_sel()
- Added set_bypass()/get_bypass() callbacks for BOB pass-through mode
control
- Removed top-level PMIC data structures
- Removed initialization variables from structs and passed them as
function parameters
- Removed various comments and error messages
- Simplified mode handling
- Refactored per-PMIC rpmh-regulator data specification
- Simplified probe function
- Moved header into DT patch
- Removed redundant property listings from DT binding documentation
[1]: https://lkml.org/lkml/2018/5/9/729
[2]: https://lkml.org/lkml/2018/4/10/714
[3]: https://lkml.org/lkml/2018/4/18/556
[4]: https://lkml.org/lkml/2018/5/11/696
[5]: https://lkml.org/lkml/2018/5/15/1005
[6]: https://lkml.org/lkml/2018/6/1/895
[7]: https://lkml.org/lkml/2018/5/22/1168
[8]: https://lkml.org/lkml/2018/5/11/701
[9]: https://lkml.org/lkml/2018/4/13/687
[10]: https://lkml.org/lkml/2018/3/16/1431
David Collins (2):
regulator: dt-bindings: add QCOM RPMh regulator bindings
regulator: add QCOM RPMh regulator driver
.../bindings/regulator/qcom,rpmh-regulator.txt | 160 +++++
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/qcom-rpmh-regulator.c | 767 +++++++++++++++++++++
.../dt-bindings/regulator/qcom,rpmh-regulator.h | 36 +
5 files changed, 973 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
create mode 100644 drivers/regulator/qcom-rpmh-regulator.c
create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ 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