* Re: [PATCH 2/4] dt-bindings: sram: add documentation for reserved-only flag
From: Mian Yousaf Kaukab @ 2020-05-13 10:41 UTC (permalink / raw)
To: Stephen Warren
Cc: robh+dt, robin.murphy, devicetree, talho, thierry.reding,
jonathanh, linux-tegra, linux-kernel, linux-arm-kernel, afaerber,
arnd, gregkh
In-Reply-To: <52f099e4-5c03-2141-f049-cd3adeb04c5b@wwwdotorg.org>
On Tue, May 12, 2020 at 01:45:28PM -0600, Stephen Warren wrote:
> On 5/12/20 8:48 AM, Mian Yousaf Kaukab wrote:
> > Add documentation for the new optional flag added for SRAM driver.
>
> > diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml
>
> > + reserved-only:
> > + description:
> > + The flag indicating, that only SRAM reserved regions have to be remapped.
> > + remapping type is selected depending upon no-memory-wc as usual.
> > + type: boolean
>
> This feels a bit like a SW flag rather than a HW description, so I'm not
> sure it's appropriate to put it into DT.
Reserved regions themselves are software descriptions, no? Then we have 'pool'
flag which is again a software flag and so on. This flag falls into same
category and nothing out of ordinary.
>
> Are there any cases where the SW should map all of the SRAM, i.e. where
> we wouldn't expect to set reserved-only? [...]
Yes, here are a few examples:
arch/arm/boot/dts/aspeed-g*.dtsi
arch/arm/boot/dts/at91*.dtsi
arch/arm/boot/dts/bcm7445.dtsi
Then arch/arm/boot/dts/dra7.dtsi is an example where we should map everything
except the reserved region.
> [...] I'd expect reserved-only to be
> the default, and perhaps only, mode of operation for the SRAM driver.
It will break compatibility with existing dtbs.
> If we can't do that because some SW currently expects to be able to map
> arbitrary portions of the SRAM, shouldn't that SW be fixed to tell the
> SRAM driver which parts it's using, hence still allowing the driver to
> only map in-use portions?
User doesn’t need sram driver in that case. It can use genalloc api directly.
BR,
Yousaf
^ permalink raw reply
* Re: [RFC][PATCH 3/4] dma-buf: cma_heap: Extend logic to export CMA regions tagged with "linux,cma-heap"
From: Brian Starkey @ 2020-05-13 10:44 UTC (permalink / raw)
To: Rob Herring
Cc: John Stultz, Robin Murphy, lkml, Sumit Semwal, Andrew F. Davis,
Benjamin Gaignard, Liam Mark, Pratik Patel, Laura Abbott,
Chenbo Feng, Alistair Strachan, Sandeep Patil, Hridya Valsaraju,
Christoph Hellwig, Marek Szyprowski, Andrew Morton,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
dri-devel, linux-mm, nd
In-Reply-To: <20200512163714.GA22577@bogus>
Hi Rob,
On Tue, May 12, 2020 at 11:37:14AM -0500, Rob Herring wrote:
> On Mon, May 04, 2020 at 10:06:28AM +0100, Brian Starkey wrote:
> > On Fri, May 01, 2020 at 12:01:40PM -0700, John Stultz wrote:
> > > On Fri, May 1, 2020 at 4:08 AM Robin Murphy <robin.murphy@arm.com> wrote:
> > > >
> > > > On 2020-05-01 11:21 am, Brian Starkey wrote:
> > > > > Hi John,
> > > > >
> > > > > On Fri, May 01, 2020 at 07:39:48AM +0000, John Stultz wrote:
> > > > >> This patch reworks the cma_heap initialization so that
> > > > >> we expose both the default CMA region and any CMA regions
> > > > >> tagged with "linux,cma-heap" in the device-tree.
> > > > >>
> > > > >> Cc: Rob Herring <robh+dt@kernel.org>
> > > > >> Cc: Sumit Semwal <sumit.semwal@linaro.org>
> > > > >> Cc: "Andrew F. Davis" <afd@ti.com>
> > > > >> Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> > > > >> Cc: Liam Mark <lmark@codeaurora.org>
> > > > >> Cc: Pratik Patel <pratikp@codeaurora.org>
> > > > >> Cc: Laura Abbott <labbott@redhat.com>
> > > > >> Cc: Brian Starkey <Brian.Starkey@arm.com>
> > > > >> Cc: Chenbo Feng <fengc@google.com>
> > > > >> Cc: Alistair Strachan <astrachan@google.com>
> > > > >> Cc: Sandeep Patil <sspatil@google.com>
> > > > >> Cc: Hridya Valsaraju <hridya@google.com>
> > > > >> Cc: Christoph Hellwig <hch@lst.de>
> > > > >> Cc: Marek Szyprowski <m.szyprowski@samsung.com>
> > > > >> Cc: Robin Murphy <robin.murphy@arm.com>
> > > > >> Cc: Andrew Morton <akpm@linux-foundation.org>
> > > > >> Cc: devicetree@vger.kernel.org
> > > > >> Cc: dri-devel@lists.freedesktop.org
> > > > >> Cc: linux-mm@kvack.org
> > > > >> Signed-off-by: John Stultz <john.stultz@linaro.org>
> > > > >> ---
> > > > >> drivers/dma-buf/heaps/cma_heap.c | 18 +++++++++---------
> > > > >> 1 file changed, 9 insertions(+), 9 deletions(-)
> > > > >>
> > > > >> diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c
> > > > >> index 626cf7fd033a..dd154e2db101 100644
> > > > >> --- a/drivers/dma-buf/heaps/cma_heap.c
> > > > >> +++ b/drivers/dma-buf/heaps/cma_heap.c
> > > > >> @@ -141,6 +141,11 @@ static int __add_cma_heap(struct cma *cma, void *data)
> > > > >> {
> > > > >> struct cma_heap *cma_heap;
> > > > >> struct dma_heap_export_info exp_info;
> > > > >> + struct cma *default_cma = dev_get_cma_area(NULL);
> > > > >> +
> > > > >> + /* We only add the default heap and explicitly tagged heaps */
> > > > >> + if (cma != default_cma && !cma_dma_heap_enabled(cma))
> > > > >> + return 0;
> > > > >
> > > > > Thinking about the pl111 thread[1], I'm wondering if we should also
> > > > > let drivers call this directly to expose their CMA pools, even if they
> > > > > aren't tagged for dma-heaps in DT. But perhaps that's too close to
> > > > > policy.
> > > >
> > > > That sounds much like what my first thoughts were - apologies if I'm
> > > > wildly off-base here, but as far as I understand:
> > > >
> > > > - Device drivers know whether they have their own "memory-region" or not.
> > > > - Device drivers already have to do *something* to participate in dma-buf.
> > > > - Device drivers know best how they make use of both the above.
> > > > - Therefore couldn't it be left to drivers to choose whether to register
> > > > their CMA regions as heaps, without having to mess with DT at all?
>
> +1, but I'm biased toward any solution not using DT. :)
>
> > > I guess I'm not opposed to this. But I guess I'd like to see some more
> > > details? You're thinking the pl111 driver would add the
> > > "memory-region" node itself?
> > >
> > > Assuming that's the case, my only worry is what if that memory-region
> > > node isn't a CMA area, but instead something like a carveout? Does the
> > > driver need to parse enough of the dt to figure out where to register
> > > the region as a heap?
> >
> > My thinking was more like there would already be a reserved-memory
> > node in DT for the chunk of memory, appropriately tagged so that it
> > gets added as a CMA region.
> >
> > The device's node would have "memory-region=<&blah>;" and would use
> > of_reserved_mem_device_init() to link up dev->cma_area to the
> > corresponding cma region.
> >
> > So far, that's all in-place already. The bit that's missing is
> > exposing that dev->cma_area to userspace as a dma_heap - so we could
> > just have "int cma_heap_add(struct cma *cma)" or "int
> > cma_heap_dev_add(struct device *dev)" or something exported for
> > drivers to expose their device-assigned cma region if they wanted to.
> >
> > I don't think this runs into the lifetime problems of generalised
> > heaps-as-modules either, because the CMA region will never go away
> > even if the driver does.
> >
> > Alongside that, I do think the completely DT-driven approach can be
> > useful too - because there may be regions which aren't associated with
> > any specific device driver, that we want exported as heaps.
>
> And they are associated with the hardware description rather than the
> userspace environment?
I'm not sure how to answer that. We already have CMA regions being
created from device-tree, so we're only talking about explicitly
exposing those to userspace.
Are you thinking that userspace should be deciding whether they get
exposed or not? I don't know how userspace would discover them in
order to make that decision.
Thanks,
-Brian
>
> Rob
^ permalink raw reply
* [PATCH v6 0/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-13 10:46 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree
Cc: miquel.raynal, richard, vigneshr, arnd, brendanhiggins, tglx,
boris.brezillon, anders.roxell, masonccyang, robh+dt, linux-mips,
hauke.mehrtens, andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar,Vadivel MuruganX
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller also supports in-built HW ECC engine.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Thank you very much Boris for the reviews, suggestions and valuable inputs.
---
v6:
- update EBU_ADDR_SELx register base value build it from DT
- Add tabs in in Kconfig
- Rob's review comments addressed in YAML file
- add addr_sel0 and addr_sel1 reg-names in YAML example
v5:
- replace by 'HSNAND_CLE_OFFS | HSNAND_CS_OFFS' to NAND_WRITE_CMD and NAND_WRITE_ADDR
- remove the unused macros
- update EBU_ADDR_MASK(x) macro
- update the EBU_ADDR_SELx register values to be written
- add the example in YAML file
v4:
- add ebu_nand_cs structure for multiple-CS support
- mask/offset encoding for 0x51 value
- update macro HSNAND_CTL_ENABLE_ECC
- drop the op argument and un-used macros.
- updated the datatype and macros
- add function disable nand module
- remove ebu_host->dma_rx = NULL;
- rename MMIO address range variables to ebu and hsnand
- implement ->setup_data_interface()
- update label err_cleanup_nand and err_cleanup_dma
- add return value check in the nand_remove function
- add/remove tabs and spaces as per coding standard
- encoded CS ids by reg property
v3:
- Add depends on MACRO in Kconfig
- file name update in Makefile
- file name update to intel-nand-controller
- modification of MACRO divided like EBU, HSNAND and NAND
- add NAND_ALE_OFFS, NAND_CLE_OFFS and NAND_CS_OFFS
- rename lgm_ to ebu_ and _va suffix is removed in the whole file
- rename structure and varaibles as per review comments.
- remove lgm_read_byte(), lgm_dev_ready() and cmd_ctrl() un-used function
- update in exec_op() as per review comments
- rename function lgm_dma_exit() by lgm_dma_cleanup()
- hardcoded magic value for base and offset replaced by MACRO defined
- mtd_device_unregister() + nand_cleanup() instead of nand_release()
v2:
- implement the ->exec_op() to replaces the legacy hook-up.
- update the commit message
- YAML compatible string update to intel, lgm-nand-controller
- add MIPS maintainers and xway_nand driver author in CC
v1:
- initial version
Ramuthevar Vadivel Murugan (2):
dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
mtd: rawnand: Add NAND controller support on Intel LGM SoC
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 83 +++
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++
4 files changed, 839 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
--
2.11.0
^ permalink raw reply
* [PATCH v6 1/2] dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-13 10:46 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree
Cc: miquel.raynal, richard, vigneshr, arnd, brendanhiggins, tglx,
boris.brezillon, anders.roxell, masonccyang, robh+dt, linux-mips,
hauke.mehrtens, andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200513104615.7905-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
Add YAML file for dt-bindings to support NAND Flash Controller
on Intel's Lightning Mountain SoC.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 83 ++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
diff --git a/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
new file mode 100644
index 000000000000..d9e0df8553fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/intel,lgm-nand.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel LGM SoC NAND Controller Device Tree Bindings
+
+allOf:
+ - $ref: "nand-controller.yaml"
+
+maintainers:
+ - Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
+
+properties:
+ compatible:
+ const: intel,lgm-nand-controller
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ enum:
+ - rx
+ - tx
+
+ pinctrl-names: true
+
+patternProperties:
+ "^nand@[a-f0-9]+$":
+ type: object
+ properties:
+ reg:
+ minimum: 0
+ maximum: 7
+
+ nand-ecc-mode: true
+
+ nand-ecc-algo:
+ const: hw
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - dmas
+
+additionalProperties: false
+
+examples:
+ - |
+ nand-controller@e0f00000 {
+ compatible = "intel,nand-controller";
+ reg = <0xe0f00000 0x100>,
+ <0xe1000000 0x300>,
+ <0xe1400000 0x8000>,
+ <0xe1c00000 0x1000>;
+ reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
+ "addr_sel0","addr_sel1";
+ clocks = <&cgu0 125>;
+ dma-names = "tx", "rx";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+
+ nand@0 {
+ reg = <0>;
+ nand-on-flash-bbt;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+ };
+
+...
--
2.11.0
^ permalink raw reply related
* [PATCH v6 2/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-13 10:46 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree
Cc: miquel.raynal, richard, vigneshr, arnd, brendanhiggins, tglx,
boris.brezillon, anders.roxell, masonccyang, robh+dt, linux-mips,
hauke.mehrtens, andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200513104615.7905-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++++++++
3 files changed, 756 insertions(+)
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index a80a46bb5b8b..75ab2afb78cf 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -457,6 +457,14 @@ config MTD_NAND_CADENCE
Enable the driver for NAND flash on platforms using a Cadence NAND
controller.
+config MTD_NAND_INTEL_LGM
+ tristate "Support for NAND controller on Intel LGM SoC"
+ depends on OF || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Enables support for NAND Flash chips on Intel's LGM SoC.
+ NAND flash controller interfaced through the External Bus Unit.
+
comment "Misc"
config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2d136b158fb7..bfc8fe4d2cb0 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o
+obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c
new file mode 100644
index 000000000000..a66a0861420d
--- /dev/null
+++ b/drivers/mtd/nand/raw/intel-nand-controller.c
@@ -0,0 +1,747 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2020 Intel Corporation. */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand.h>
+#include <linux/resource.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+#define EBU_CLC 0x000
+#define EBU_CLC_RST 0x00000000u
+
+#define EBU_ADDR_SEL(n) (0x20 + (n) * 4)
+/* 5 bits 26:22 included for comparison in the ADDR_SELx */
+#define EBU_ADDR_MASK(x) ((x) << 4)
+#define EBU_ADDR_SEL_REGEN 0x1
+
+#define EBU_BUSCON(n) (0x60 + (n) * 4)
+#define EBU_BUSCON_CMULT_V4 0x1
+#define EBU_BUSCON_RECOVC(n) ((n) << 2)
+#define EBU_BUSCON_HOLDC(n) ((n) << 4)
+#define EBU_BUSCON_WAITRDC(n) ((n) << 6)
+#define EBU_BUSCON_WAITWRC(n) ((n) << 8)
+#define EBU_BUSCON_BCGEN_CS 0x0
+#define EBU_BUSCON_SETUP_EN BIT(22)
+#define EBU_BUSCON_ALEC 0xC000
+
+#define EBU_CON 0x0B0
+#define EBU_CON_NANDM_EN BIT(0)
+#define EBU_CON_NANDM_DIS 0x0
+#define EBU_CON_CSMUX_E_EN BIT(1)
+#define EBU_CON_ALE_P_LOW BIT(2)
+#define EBU_CON_CLE_P_LOW BIT(3)
+#define EBU_CON_CS_P_LOW BIT(4)
+#define EBU_CON_SE_P_LOW BIT(5)
+#define EBU_CON_WP_P_LOW BIT(6)
+#define EBU_CON_PRE_P_LOW BIT(7)
+#define EBU_CON_IN_CS_S(n) ((n) << 8)
+#define EBU_CON_OUT_CS_S(n) ((n) << 10)
+#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18)
+
+#define EBU_WAIT 0x0B4
+#define EBU_WAIT_RDBY BIT(0)
+#define EBU_WAIT_WR_C BIT(3)
+
+#define HSNAND_CTL1 0x110
+#define HSNAND_CTL1_ADDR_SHIFT 24
+
+#define HSNAND_CTL2 0x114
+#define HSNAND_CTL2_ADDR_SHIFT 8
+#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16)
+
+#define HSNAND_INT_MSK_CTL 0x124
+#define HSNAND_INT_MSK_CTL_WR_C BIT(4)
+
+#define HSNAND_INT_STA 0x128
+#define HSNAND_INT_STA_WR_C BIT(4)
+
+#define HSNAND_CTL 0x130
+#define HSNAND_CTL_ENABLE_ECC BIT(0)
+#define HSNAND_CTL_GO BIT(2)
+#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n))
+#define HSNAND_CTL_RW_READ 0x0
+#define HSNAND_CTL_RW_WRITE BIT(10)
+#define HSNAND_CTL_ECC_OFF_V8TH BIT(11)
+#define HSNAND_CTL_CKFF_EN 0x0
+#define HSNAND_CTL_MSG_EN BIT(17)
+
+#define HSNAND_PARA0 0x13c
+#define HSNAND_PARA0_PAGE_V8192 0x3
+#define HSNAND_PARA0_PIB_V256 (0x3 << 4)
+#define HSNAND_PARA0_BYP_EN_NP 0x0
+#define HSNAND_PARA0_BYP_DEC_NP 0x0
+#define HSNAND_PARA0_TYPE_ONFI BIT(18)
+#define HSNAND_PARA0_ADEP_EN BIT(21)
+
+#define HSNAND_CMSG_0 0x150
+#define HSNAND_CMSG_1 0x154
+
+#define HSNAND_ALE_OFFS BIT(2)
+#define HSNAND_CLE_OFFS BIT(3)
+#define HSNAND_CS_OFFS BIT(4)
+
+#define HSNAND_ECC_OFFSET 0x008
+
+#define NAND_DATA_IFACE_CHECK_ONLY -1
+
+#define MAX_CS 2
+
+struct ebu_nand_cs {
+ void __iomem *chipaddr;
+ dma_addr_t nand_pa;
+ u32 addr_sel;
+};
+
+struct ebu_nand_controller {
+ struct nand_controller controller;
+ struct nand_chip chip;
+ struct device *dev;
+ void __iomem *ebu;
+ void __iomem *hsnand;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ struct completion dma_access_complete;
+ unsigned long clk_rate;
+ struct clk *clk;
+ u32 nd_para0;
+ u8 cs_num;
+ struct ebu_nand_cs cs[MAX_CS];
+};
+
+static inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip)
+{
+ return container_of(chip, struct ebu_nand_controller, chip);
+}
+
+static u8 ebu_nand_readb(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+ u8 val;
+
+ val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+
+ return val;
+}
+
+static void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+
+ writeb(value, ebu_host->cs[cs_num].chipaddr + offset);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+}
+
+static void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = ebu_nand_readb(chip);
+}
+
+static void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]);
+}
+
+static void ebu_nand_disable(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+
+ writel(0, ebu_host->ebu + EBU_CON);
+}
+
+static void ebu_select_chip(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_con = ebu_host->ebu + EBU_CON;
+ u32 cs = ebu_host->cs_num;
+
+ writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW |
+ EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW |
+ EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) |
+ EBU_CON_LAT_EN_CS_P, nand_con);
+}
+
+static void ebu_nand_setup_timing(struct ebu_nand_controller *ctrl,
+ const struct nand_sdr_timings *timings)
+{
+ unsigned int rate = clk_get_rate(ctrl->clk) / 1000000;
+ unsigned int period = DIV_ROUND_UP(1000000, rate);
+ u32 trecov, thold, twrwait, trdwait;
+ u32 reg = 0;
+
+ trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_RECOVC(trecov);
+
+ thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period);
+ reg |= EBU_BUSCON_HOLDC(thold);
+
+ trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_WAITRDC(trdwait);
+
+ twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period);
+ reg |= EBU_BUSCON_WAITWRC(twrwait);
+
+ reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC |
+ EBU_BUSCON_SETUP_EN;
+
+ writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num));
+}
+
+static int ebu_nand_setup_data_interface(struct nand_chip *chip, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ 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;
+
+ ebu_nand_setup_timing(ctrl, timings);
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = HSNAND_ECC_OFFSET;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = {
+ .ecc = ebu_nand_ooblayout_ecc,
+ .free = ebu_nand_ooblayout_free,
+};
+
+static void ebu_dma_rx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_rx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static void ebu_dma_tx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_tx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
+ const u8 *buf, u32 len)
+{
+ struct dma_async_tx_descriptor *tx;
+ struct completion *dma_completion;
+ dma_async_tx_callback callback;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ dma_addr_t buf_dma;
+ int ret;
+ u32 timeout;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ chan = ebu_host->dma_rx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_rx_callback;
+ } else {
+ chan = ebu_host->dma_tx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_tx_callback;
+ }
+
+ buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
+ if (dma_mapping_error(chan->device->dev, buf_dma)) {
+ dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags);
+ if (!tx)
+ return -ENXIO;
+
+ tx->callback = callback;
+ tx->callback_param = ebu_host;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie);
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ init_completion(dma_completion);
+ dma_async_issue_pending(chan);
+
+ /* Wait DMA to finish the data transfer.*/
+ timeout =
+ wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
+ if (!timeout) {
+ dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n",
+ dmaengine_tx_status(chan, cookie, NULL));
+ dmaengine_terminate_sync(chan);
+ ret = -ETIMEDOUT;
+ goto err_unmap;
+ }
+
+ return 0;
+
+err_unmap:
+ dma_unmap_single(ebu_host->dev, buf_dma, len, dir);
+
+ return ret;
+}
+
+static void ebu_nand_trigger(struct ebu_nand_controller *ebu_host,
+ int page, u32 cmd)
+{
+ unsigned int val;
+
+ val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT;
+ writel(val, ebu_host->hsnand + HSNAND_CTL1);
+ val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5;
+ writel(val, ebu_host->hsnand + HSNAND_CTL2);
+
+ writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0);
+
+ /* clear first, will update later */
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0);
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1);
+
+ writel(HSNAND_INT_MSK_CTL_WR_C,
+ ebu_host->hsnand + HSNAND_INT_MSK_CTL);
+
+ val = cmd == NAND_CMD_READ0 ? HSNAND_CTL_RW_READ : HSNAND_CTL_RW_WRITE;
+
+ writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN |
+ HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) |
+ HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val,
+ ebu_host->hsnand + HSNAND_CTL);
+}
+
+static int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ int ret, x;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0);
+
+ ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ chip->ecc.read_oob(chip, page);
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA;
+ int ret, val, x;
+ __be32 reg;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN);
+
+ ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ const u8 *pdata;
+
+ pdata = chip->oob_poi;
+ reg = cpu_to_be32(*pdata++);
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_0);
+
+ reg = cpu_to_be32(*pdata);
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_1);
+ }
+
+ ret = readl_poll_timeout_atomic(int_sta, val,
+ !(val & HSNAND_INT_STA_WR_C), 10, 1000);
+ if (ret)
+ return -EIO;
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, };
+
+static int ebu_nand_attach_chip(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ u32 eccsize, eccsteps, eccbytes, ecctotal, pagesize, pg_per_blk;
+ u32 eccstrength = chip->ecc.strength;
+ u32 writesize = mtd->writesize;
+ u32 blocksize = mtd->erasesize;
+ int start, val, i;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ /* Check whether eccsize is 0x0 or wrong. assign eccsize = 512 if YES */
+ if (!chip->ecc.size)
+ chip->ecc.size = 512;
+ eccsize = chip->ecc.size;
+
+ switch (eccsize) {
+ case 512:
+ start = 1;
+ if (!eccstrength)
+ eccstrength = 4;
+ break;
+ case 1024:
+ start = 4;
+ if (!eccstrength)
+ eccstrength = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i = round_up(start + 1, 4);
+ for (val = start; val < i; val++) {
+ if (eccstrength == ecc_strength[val])
+ break;
+ }
+ if (val == i)
+ return -EINVAL;
+
+ if (eccstrength == 8)
+ eccbytes = 14;
+ else
+ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
+
+ eccsteps = writesize / eccsize;
+ ecctotal = eccsteps * eccbytes;
+ if ((ecctotal + 8) > mtd->oobsize)
+ return -ERANGE;
+
+ chip->ecc.total = ecctotal;
+ pagesize = fls(writesize >> 11);
+ if (pagesize > HSNAND_PARA0_PAGE_V8192)
+ return -ERANGE;
+
+ pg_per_blk = fls((blocksize / writesize) >> 6) << 4;
+ if (pg_per_blk > HSNAND_PARA0_PIB_V256)
+ return -ERANGE;
+
+ ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP |
+ HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN |
+ HSNAND_PARA0_TYPE_ONFI | (val << 29);
+
+ mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops);
+ chip->ecc.read_page = ebu_nand_read_page_hwecc;
+ chip->ecc.write_page = ebu_nand_write_page_hwecc;
+
+ return 0;
+}
+
+static int ebu_nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op, bool check_only)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ const struct nand_op_instr *instr = NULL;
+ unsigned int op_id;
+ int i, time_out, ret = 0;
+ u32 stat;
+
+ ebu_select_chip(chip);
+
+ for (op_id = 0; op_id < op->ninstrs; op_id++) {
+ instr = &op->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.cmd.opcode);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ ebu_nand_writeb(chip,
+ HSNAND_ALE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.addr.addrs[i]);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ebu_read_buf(chip, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ebu_write_buf(chip, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ time_out = instr->ctx.waitrdy.timeout_ms * 1000;
+ ret = readl_poll_timeout(ctrl->ebu + EBU_WAIT,
+ stat, stat & EBU_WAIT_RDBY,
+ 20, time_out);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct nand_controller_ops ebu_nand_controller_ops = {
+ .attach_chip = ebu_nand_attach_chip,
+ .exec_op = ebu_nand_exec_op,
+ .setup_data_interface = ebu_nand_setup_data_interface,
+};
+
+static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
+{
+ if (ebu_host->dma_rx)
+ dma_release_channel(ebu_host->dma_rx);
+
+ if (ebu_host->dma_tx)
+ dma_release_channel(ebu_host->dma_tx);
+}
+
+static int ebu_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ebu_nand_controller *ebu_host;
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ struct resource *res;
+ char *resname;
+ int ret, i;
+ u32 reg;
+
+ ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
+ if (!ebu_host)
+ return -ENOMEM;
+
+ ebu_host->dev = dev;
+ nand_controller_init(&ebu_host->controller);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand");
+ ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->ebu))
+ return PTR_ERR(ebu_host->ebu);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand");
+ ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->hsnand))
+ return PTR_ERR(ebu_host->hsnand);
+
+ ret = device_property_read_u32(dev, "nand,cs", ®);
+ if (ret) {
+ dev_err(dev, "failed to get chip select: %d\n", ret);
+ return ret;
+ }
+ ebu_host->cs_num = reg;
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ ebu_host->cs[i].chipaddr = devm_ioremap_resource(dev, res);
+ ebu_host->cs[i].nand_pa = res->start;
+ if (IS_ERR(ebu_host->cs[i].chipaddr))
+ return PTR_ERR(ebu_host->cs[i].chipaddr);
+ }
+
+ ebu_host->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ebu_host->clk)) {
+ ret = PTR_ERR(ebu_host->clk);
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ebu_host->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+ ebu_host->clk_rate = clk_get_rate(ebu_host->clk);
+
+ ebu_host->dma_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(ebu_host->dma_tx)) {
+ ret = PTR_ERR(ebu_host->dma_tx);
+ dev_err(dev, "DMA tx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ ebu_host->dma_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(ebu_host->dma_rx)) {
+ ret = PTR_ERR(ebu_host->dma_rx);
+ dev_err(dev, "DMA tx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ ebu_host->cs[i].addr_sel = res->start;
+ writel(ebu_host->cs[i].addr_sel | EBU_ADDR_MASK(5) |
+ EBU_ADDR_SEL_REGEN, ebu_host->ebu + EBU_ADDR_SEL(i));
+ }
+
+ nand_set_flash_node(&ebu_host->chip, dev->of_node);
+ mtd = nand_to_mtd(&ebu_host->chip);
+ mtd->dev.parent = dev;
+ ebu_host->dev = dev;
+
+ platform_set_drvdata(pdev, ebu_host);
+ nand_set_controller_data(&ebu_host->chip, ebu_host);
+
+ nand = &ebu_host->chip;
+ nand->controller = &ebu_host->controller;
+ nand->controller->ops = &ebu_nand_controller_ops;
+
+ /* Scan to find existence of the device */
+ ret = nand_scan(&ebu_host->chip, 1);
+ if (ret)
+ goto err_cleanup_dma;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_clean_nand;
+
+ return 0;
+
+err_clean_nand:
+ nand_cleanup(&ebu_host->chip);
+err_cleanup_dma:
+ ebu_dma_cleanup(ebu_host);
+ clk_disable_unprepare(ebu_host->clk);
+
+ return ret;
+}
+
+static int ebu_nand_remove(struct platform_device *pdev)
+{
+ struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev);
+
+ if (ebu_host) {
+ mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
+ nand_cleanup(&ebu_host->chip);
+ ebu_nand_disable(&ebu_host->chip);
+
+ if (ebu_host->dma_rx || ebu_host->dma_tx)
+ ebu_dma_cleanup(ebu_host);
+
+ clk_disable_unprepare(ebu_host->clk);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ebu_nand_match[] = {
+ { .compatible = "intel,nand-controller", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ebu_nand_match);
+
+static struct platform_driver ebu_nand_driver = {
+ .probe = ebu_nand_probe,
+ .remove = ebu_nand_remove,
+ .driver = {
+ .name = "intel-nand-controller",
+ .of_match_table = ebu_nand_match,
+ },
+
+};
+module_platform_driver(ebu_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>");
+MODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver");
--
2.11.0
^ permalink raw reply related
* Re: [PATCH 00/17] spi: dw: Add generic DW DMA controller support
From: Serge Semin @ 2020-05-13 11:04 UTC (permalink / raw)
To: Mark Brown
Cc: Serge Semin, Georgy Vlasov, Ramil Zaripov, Alexey Malahov,
Maxim Kaurkin, Pavel Parkhomenko, Ekaterina Skachko, Vadim Vlasov,
Alexey Kolotnikov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Allison Randal, Andy Shevchenko, Gareth Williams,
Rob Herring, linux-mips, linux-spi, devicetree, linux-kernel
In-Reply-To: <20200513102324.GB4803@sirena.org.uk>
On Wed, May 13, 2020 at 11:23:24AM +0100, Mark Brown wrote:
> On Tue, May 12, 2020 at 11:07:33PM +0300, Serge Semin wrote:
> > On Fri, May 08, 2020 at 02:33:36PM +0100, Mark Brown wrote:
>
> > > Please don't make new feature development dependent on conversion to the
> > > new schema format, there's quite a backlog of reviews of schema
> > > conversions so it can slow things down. It's good to do the conversions
> > > but please do them after adding any new stuff to the binding rather than
> > > before.
>
> > So by saying this do you want me to revert an order of the first two patches
> > in the series, right? So the series would first add the DMA properties support
> > to the binding, then would convert the binding file to DT schema.
>
> The conversion to YAML format should be the very last thing in the patch
> series,
Hm, haven't heard about this requirement. Could you point me out to a doc or
some discussion concerning this for future reference? It's not a first DT
conversion patch I've submitted and non of them were addressed with such
request. I do understand that the order of DT concerning patches can be
important and agree to fix it by updating the original legacy binding first,
then perform a conversion. But placing the conversion in a tail of the series
just seems unnecessary. The patch can be dropped from any place of the series
if for some reason Rob would be late with review.
Personally I prefer placing all DT changes in the head of the series, so Rob
wouldn't need to search through the whole patchset looking for the DT-related
patches.
-Sergey
> and as Andy says there's another patch in flight also doing this
> conversion which you should coordinate with.
^ permalink raw reply
* Re: [RFC PATCH] dt-bindings: display: ti,tfp410.txt: convert to yaml
From: Ricardo Cañuelo @ 2020-05-13 11:09 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Tomi Valkeinen, robh+dt, kernel, devicetree, dri-devel,
linux-arm-kernel, jason
In-Reply-To: <20200506155320.GC15206@pendragon.ideasonboard.com>
Hi Laurent,
On mié 06-05-2020 18:53:20, Laurent Pinchart wrote:
> I didn't if I remember correctly, I just mapped it to the hardware
> features. The hardware register indeed takes a value between 0 and 7,
> and that is mapped to [-4,3] x t(STEP). I don't mind either option.
I was taking a look at the ti-tfp410.c driver to see if it needs any
changes to support the updated deskew property ranges [0-7], but I don't
fully understand what this does (line 276):
/* Get the setup and hold time from vendor-specific properties. */
of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew);
if (deskew < -4 || deskew > 3)
return -EINVAL;
timings->setup_time_ps = min(0, 1200 - 350 * deskew);
timings->hold_time_ps = min(0, 1300 + 350 * deskew);
It looks like that the driver doesn't really apply the deskew settings
to the device and that this has not been really tested, so it's probably
not a big deal.
I guess what you wanted to do was to adjust the setup and hold times
around 1200 and 1300 ps respectively in increments/decrements of 350 ps
depending on the deskew value, as the datasheet describes. But this code
would set timings->setup_time_ps to 0 regardless of the deskew value,
and timings->hold_time_ps would be either 0 or a very big integer value
if deskew is -4 (both setup_time_ps and hold_time_ps are u32).
Am I missing something? Was this intentional?
Thanks,
Ricardo
^ permalink raw reply
* Re: [PATCH v4 00/12] PCI: aardvark: Fix support for Turris MOX and Compex wifi cards
From: Pali Rohár @ 2020-05-13 11:16 UTC (permalink / raw)
To: Lorenzo Pieralisi, Bjorn Helgaas
Cc: Jason Cooper, Andrew Lunn, Gregory Clement, Sebastian Hesselbarth,
Rob Herring, Thomas Petazzoni, Andrew Murray, Remi Pommarel,
Marek Behún, Tomasz Maciej Nowak, Xogium, devicetree,
linux-kernel, linux-pci
In-Reply-To: <20200430080625.26070-1-pali@kernel.org>
On Thursday 30 April 2020 10:06:13 Pali Rohár wrote:
> Hello,
>
> this is the fourth version of the patch series for Armada 3720 PCIe
> controller (aardvark). It's main purpose is to fix some bugs regarding
> buggy ath10k cards, but we also found out some suspicious stuff about
> the driver and the SOC itself, which we try to address.
>
> Patches are available also in my git branch pci-aardvark:
> https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/log/?h=pci-aardvark
Hello! Thanks everybody for review and testing of this patch series.
I would like to ask, is there something needed to fix / modify in this
patch series? If everything is OK, would you Bjorn or Lorenzo take this
patch series into your tree?
^ permalink raw reply
* Re: [PATCH 00/17] spi: dw: Add generic DW DMA controller support
From: Mark Brown @ 2020-05-13 11:21 UTC (permalink / raw)
To: Serge Semin
Cc: Serge Semin, Georgy Vlasov, Ramil Zaripov, Alexey Malahov,
Maxim Kaurkin, Pavel Parkhomenko, Ekaterina Skachko, Vadim Vlasov,
Alexey Kolotnikov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Allison Randal, Andy Shevchenko, Gareth Williams,
Rob Herring, linux-mips, linux-spi, devicetree, linux-kernel
In-Reply-To: <20200513110407.uaosfxebsgnllsf4@mobilestation>
[-- Attachment #1: Type: text/plain, Size: 1866 bytes --]
On Wed, May 13, 2020 at 02:04:07PM +0300, Serge Semin wrote:
> On Wed, May 13, 2020 at 11:23:24AM +0100, Mark Brown wrote:
> > The conversion to YAML format should be the very last thing in the patch
> > series,
> Hm, haven't heard about this requirement. Could you point me out to a doc or
> some discussion concerning this for future reference? It's not a first DT
> conversion patch I've submitted and non of them were addressed with such
> request. I do understand that the order of DT concerning patches can be
> important and agree to fix it by updating the original legacy binding first,
> then perform a conversion. But placing the conversion in a tail of the series
> just seems unnecessary. The patch can be dropped from any place of the series
> if for some reason Rob would be late with review.
This is a practical observation based on the fact that there is a huge
backlog of reviews of DT binding conversions and that those conversions
typically go through several review cycles and that not everyone who's
sending patches to the kernel is fully up to speed on processes or has
strong English. By telling people (including other people who find
instructions on the list) to put the conversion right at the end of the
series I am avoiding any ambiguity or confusion about ordering with
regard to any other patches to the DT, including any new patches that
get added to the series.
> Personally I prefer placing all DT changes in the head of the series, so Rob
> wouldn't need to search through the whole patchset looking for the DT-related
> patches.
Ideally the YAML conversions would be done entirely separately to other
development rather than as part of a bigger series, they're pretty much
orthogonal anyway. Sadly there's obvious content collisions with any
new development that adds DT stuff so that's not always the most
practical thing.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v4 00/12] PCI: aardvark: Fix support for Turris MOX and Compex wifi cards
From: Lorenzo Pieralisi @ 2020-05-13 11:33 UTC (permalink / raw)
To: Pali Rohár, thomas.petazzoni
Cc: Bjorn Helgaas, Jason Cooper, Andrew Lunn, Gregory Clement,
Sebastian Hesselbarth, Rob Herring, Andrew Murray, Remi Pommarel,
Marek Behún, Tomasz Maciej Nowak, Xogium, devicetree,
linux-kernel, linux-pci
In-Reply-To: <20200513111651.q62dqauatryh6xd6@pali>
On Wed, May 13, 2020 at 01:16:51PM +0200, Pali Rohár wrote:
> On Thursday 30 April 2020 10:06:13 Pali Rohár wrote:
> > Hello,
> >
> > this is the fourth version of the patch series for Armada 3720 PCIe
> > controller (aardvark). It's main purpose is to fix some bugs regarding
> > buggy ath10k cards, but we also found out some suspicious stuff about
> > the driver and the SOC itself, which we try to address.
> >
> > Patches are available also in my git branch pci-aardvark:
> > https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/log/?h=pci-aardvark
>
> Hello! Thanks everybody for review and testing of this patch series.
>
> I would like to ask, is there something needed to fix / modify in this
> patch series? If everything is OK, would you Bjorn or Lorenzo take this
> patch series into your tree?
We need Thomas' ACK on the series. We don't have this HW and
we comment on the generic code, Thomas owns it and must check that
what you are changing is sound.
On patch 5 I share Rob's concerns - it does not make much sense
to have something driver specific there, need to look further.
Lorenzo
^ permalink raw reply
* Re: [PATCH 07/17] spi: dw: Add Tx/Rx finish wait methods to DMA
From: Mark Brown @ 2020-05-13 11:36 UTC (permalink / raw)
To: Serge Semin
Cc: Serge Semin, Georgy Vlasov, Ramil Zaripov, Alexey Malahov,
Thomas Bogendoerfer, Paul Burton, Ralf Baechle, Arnd Bergmann,
Allison Randal, Andy Shevchenko, Gareth Williams, Rob Herring,
linux-mips, devicetree, Thomas Gleixner, wuxu.wu, Linus Walleij,
linux-spi, linux-kernel
In-Reply-To: <20200513113555.mjivjk374giopnea@mobilestation>
[-- Attachment #1: Type: text/plain, Size: 437 bytes --]
On Wed, May 13, 2020 at 02:35:55PM +0300, Serge Semin wrote:
> On Fri, May 08, 2020 at 06:30:23PM +0100, Mark Brown wrote:
> > > + while (dw_spi_dma_tx_busy(dws) && retry--)
> > > + ndelay(ns);
> > How deep can the FIFO be with this IP - could we end up ndelay()ing for
> > non-trivial amounts of time?
> According to the DW APB SSI db it can be up to 256 transfer words. So the delay
> should be trivial.
Yes, that should be fine.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v3 09/11] PCI: qcom: add ipq8064 rev2 variant and set tx term offset
From: Stanimir Varbanov @ 2020-05-13 11:37 UTC (permalink / raw)
To: Ansuel Smith, Bjorn Andersson
Cc: Sham Muthayyan, Andy Gross, Bjorn Helgaas, Rob Herring,
Mark Rutland, Lorenzo Pieralisi, Andrew Murray, Philipp Zabel,
linux-arm-msm, linux-pci, devicetree, linux-kernel
In-Reply-To: <20200430220619.3169-10-ansuelsmth@gmail.com>
Hi Ansuel,
On 5/1/20 1:06 AM, Ansuel Smith wrote:
> From: Sham Muthayyan <smuthayy@codeaurora.org>
>
> Add tx term offset support to pcie qcom driver need in some revision of
> the ipq806x SoC.
> Ipq8064 have tx term offset set to 7.
> Ipq8064-v2 revision and ipq8065 have the tx term offset set to 0.
>
> Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org>
> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
> ---
> drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index da8058fd1925..372d2c8508b5 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -45,6 +45,9 @@
> #define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10
>
> #define PCIE20_PARF_PHY_CTRL 0x40
> +#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(12, 16)
The mask definition is not correct. Should be GENMASK(20, 16)
> +#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) ((x) << 16)
> +
> #define PCIE20_PARF_PHY_REFCLK 0x4C
> #define PHY_REFCLK_SSP_EN BIT(16)
> #define PHY_REFCLK_USE_PAD BIT(12)
> @@ -118,6 +121,7 @@ struct qcom_pcie_resources_2_1_0 {
> u32 tx_swing_full;
> u32 tx_swing_low;
> u32 rx0_eq;
> + u8 phy_tx0_term_offset;
> };
>
> struct qcom_pcie_resources_1_0_0 {
> @@ -318,6 +322,11 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)
> if (IS_ERR(res->ext_reset))
> return PTR_ERR(res->ext_reset);
>
> + if (of_device_is_compatible(dev->of_node, "qcom,pcie-ipq8064"))
> + res->phy_tx0_term_offset = 7;
Before your change the phy_tx0_term_offser was 0 for apq8064, but here
you change it to 7, why?
> + else
> + res->phy_tx0_term_offset = 0;
> +
> res->phy_reset = devm_reset_control_get_exclusive(dev, "phy");
> return PTR_ERR_OR_ZERO(res->phy_reset);
> }
> @@ -402,6 +411,11 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)
> /* enable PCIe clocks and resets */
> qcom_clear_and_set_dword(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
>
> + /* set TX termination offset */
> + qcom_clear_and_set_dword(pcie->parf + PCIE20_PARF_PHY_CTRL,
> + PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
As the mask definition is incorrect you actually clear 12 to 16 bit in
the register where is another PHY parameter. Is that was intentional?
> + PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
> +
> writel(PCS_DEEMPH_TX_DEEMPH_GEN1(res->tx_deemph_gen1) |
> PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(res->tx_deemph_gen2_3p5db) |
> PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(res->tx_deemph_gen2_6db),
> @@ -1485,6 +1499,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
> static const struct of_device_id qcom_pcie_match[] = {
> { .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 },
> { .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 },
> + { .compatible = "qcom,pcie-ipq8064-v2", .data = &ops_2_1_0 },
> { .compatible = "qcom,pcie-apq8064", .data = &ops_2_1_0 },
> { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 },
> { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 },
>
--
regards,
Stan
^ permalink raw reply
* Re: [PATCH 00/17] spi: dw: Add generic DW DMA controller support
From: Serge Semin @ 2020-05-13 11:42 UTC (permalink / raw)
To: Mark Brown
Cc: Serge Semin, Georgy Vlasov, Ramil Zaripov, Alexey Malahov,
Maxim Kaurkin, Pavel Parkhomenko, Ekaterina Skachko, Vadim Vlasov,
Alexey Kolotnikov, Thomas Bogendoerfer, Paul Burton, Ralf Baechle,
Arnd Bergmann, Allison Randal, Andy Shevchenko, Gareth Williams,
Rob Herring, linux-mips, linux-spi, devicetree, linux-kernel
In-Reply-To: <20200513112116.GE4803@sirena.org.uk>
On Wed, May 13, 2020 at 12:21:16PM +0100, Mark Brown wrote:
> On Wed, May 13, 2020 at 02:04:07PM +0300, Serge Semin wrote:
> > On Wed, May 13, 2020 at 11:23:24AM +0100, Mark Brown wrote:
>
> > > The conversion to YAML format should be the very last thing in the patch
> > > series,
>
> > Hm, haven't heard about this requirement. Could you point me out to a doc or
> > some discussion concerning this for future reference? It's not a first DT
> > conversion patch I've submitted and non of them were addressed with such
> > request. I do understand that the order of DT concerning patches can be
> > important and agree to fix it by updating the original legacy binding first,
> > then perform a conversion. But placing the conversion in a tail of the series
> > just seems unnecessary. The patch can be dropped from any place of the series
> > if for some reason Rob would be late with review.
>
> This is a practical observation based on the fact that there is a huge
> backlog of reviews of DT binding conversions and that those conversions
> typically go through several review cycles and that not everyone who's
> sending patches to the kernel is fully up to speed on processes or has
> strong English. By telling people (including other people who find
> instructions on the list) to put the conversion right at the end of the
> series I am avoiding any ambiguity or confusion about ordering with
> regard to any other patches to the DT, including any new patches that
> get added to the series.
>
> > Personally I prefer placing all DT changes in the head of the series, so Rob
> > wouldn't need to search through the whole patchset looking for the DT-related
> > patches.
>
> Ideally the YAML conversions would be done entirely separately to other
> development rather than as part of a bigger series, they're pretty much
> orthogonal anyway. Sadly there's obvious content collisions with any
> new development that adds DT stuff so that's not always the most
> practical thing.
Ok. I see your point. I'll move the conversion patch to the tail of the series
after rebasing the patchset on top of the spi/for-next branch. Thanks for
clarification.
-Sergey
^ permalink raw reply
* Re: R: [PATCH v3 08/11] devicetree: bindings: pci: document PARF params bindings
From: Stanimir Varbanov @ 2020-05-13 11:43 UTC (permalink / raw)
To: Rob Herring, ansuelsmth
Cc: 'Bjorn Andersson', 'Andy Gross',
'Bjorn Helgaas', 'Mark Rutland',
'Lorenzo Pieralisi', 'Andrew Murray',
'Philipp Zabel', linux-arm-msm, linux-pci, devicetree,
linux-kernel
In-Reply-To: <20200512154544.GA823@bogus>
On 5/12/20 6:45 PM, Rob Herring wrote:
> On Thu, May 07, 2020 at 09:34:35PM +0200, ansuelsmth@gmail.com wrote:
>>> On Fri, May 01, 2020 at 12:06:15AM +0200, Ansuel Smith wrote:
>>>> It is now supported the editing of Tx De-Emphasis, Tx Swing and
>>>> Rx equalization params on ipq8064. Document this new optional params.
>>>>
>>>> Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
>>>> ---
>>>> .../devicetree/bindings/pci/qcom,pcie.txt | 36 +++++++++++++++++++
>>>> 1 file changed, 36 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
>>> b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
>>>> index 6efcef040741..8cc5aea8a1da 100644
>>>> --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
>>>> +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
>>>> @@ -254,6 +254,42 @@
>>>> - "perst-gpios" PCIe endpoint reset signal line
>>>> - "wake-gpios" PCIe endpoint wake signal line
>>>>
>>>> +- qcom,tx-deemph-gen1:
>>>> + Usage: optional (available for ipq/apq8064)
>>>> + Value type: <u32>
>>>> + Definition: Gen1 De-emphasis value.
>>>> + For ipq806x should be set to 24.
>>>
>>> Unless these need to be tuned per board, then the compatible string for
>>> ipq806x should imply all these settings.
>>>
>>
>> It was requested by v2 to make this settings tunable. These don't change are
>> all the same for every ipq806x SoC. The original implementation had this
>> value hardcoded for ipq806x. Should I restore this and drop this patch?
>
> Yes, please.
I still think that the values for tx deemph and tx swing should be
tunable. But I can live with them in the driver if they not break
support for apq8064.
The default values in the registers for apq8064 and ipq806x are:
default your change
TX_DEEMPH_GEN1 21 24
TX_DEEMPH_GEN2_3_5DB 21 24
TX_DEEMPH_GEN2_6DB 32 34
TX_SWING_FULL 121 120
TX_SWING_LOW 121 120
So until now (without your change) apq8064 worked with default values.
>
> Rob
>
--
regards,
Stan
^ permalink raw reply
* revised product enquiry
From: Franziska Glas @ 2020-05-13 19:51 UTC (permalink / raw)
To: devicetree
[-- Attachment #1: Type: text/plain, Size: 433 bytes --]
Hope you are keeping safe,
Please find attached a new purchase request for May
Review the order and let us know the prices, delivery date, item
specification and revert to us
immediately.
I hope this time production will be faster.
I´ll be waiting for your confirmation
Best Regards
(Ms) Franziska Glas
Hosmed Oy
Ennekuja 1
02270 ESPOO
Tel. +358 20 7755 330
Fax +358 20 7755 339
sales@datacom.com.tw
[-- Attachment #2: scan_004768.pdf.zip --]
[-- Type: application/zip, Size: 30677 bytes --]
^ permalink raw reply
* [PATCHv1 2/2] dt-bindings: power: supply: gpio-charger: convert to yaml
From: Sebastian Reichel @ 2020-05-13 11:56 UTC (permalink / raw)
To: Sebastian Reichel, Rob Herring
Cc: linux-pm, devicetree, linux-kernel, kernel, Sebastian Reichel
In-Reply-To: <20200513115601.360642-1-sebastian.reichel@collabora.com>
Convert the gpio-charger bindings from text format to
new YAML based representation.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
.../bindings/power/supply/gpio-charger.txt | 38 ----------
.../bindings/power/supply/gpio-charger.yaml | 75 +++++++++++++++++++
2 files changed, 75 insertions(+), 38 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/power/supply/gpio-charger.txt
create mode 100644 Documentation/devicetree/bindings/power/supply/gpio-charger.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt b/Documentation/devicetree/bindings/power/supply/gpio-charger.txt
deleted file mode 100644
index dbfd29029f69..000000000000
--- a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-gpio-charger
-
-Required properties :
- - compatible : "gpio-charger"
- - charger-type : power supply type, one of
- unknown
- battery
- ups
- mains
- usb-sdp (USB standard downstream port)
- usb-dcp (USB dedicated charging port)
- usb-cdp (USB charging downstream port)
- usb-aca (USB accessory charger adapter)
-
-Optional properties:
- - gpios : GPIO indicating the charger presence.
- See GPIO binding in bindings/gpio/gpio.txt .
- - charge-status-gpios: GPIO indicating whether a battery is charging.
- - charge-current-limit-gpios: Output GPIOs specifiers for limiting the charge current
- - charge-current-limit-mapping: List of touples with current in uA and a GPIO bitmap (in this order).
- The GPIOs are encoded in the same order as specified in charge-current-limit-gpios.
- The touples must be provided in descending order of the current limit.
-
-Example:
-
- usb_charger: charger {
- compatible = "gpio-charger";
- charger-type = "usb-sdp";
- gpios = <&gpd 28 GPIO_ACTIVE_LOW>;
- charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>;
-
- charge-current-limit-gpios = <&gpioA 11 GPIO_ACTIVE_HIGH>, <&gpioA 12 GPIO_ACTIVE_HIGH>;
- charge-current-limit-mapping = <2500000 0x00>, <700000 0x01>, <0 0x02>;
- };
-
- battery {
- power-supplies = <&usb_charger>;
- };
diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml
new file mode 100644
index 000000000000..14fb3e54f861
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/gpio-charger.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: simple battery chargers only communicating through GPIOs
+
+maintainers:
+ - Sebastian Reichel <sre@kernel.org>
+
+description: |
+ This binding is for all chargers, which are working more
+ or less autonomously, only providing some status GPIOs
+ and possibly some GPIOs for limited control over the
+ charging process.
+
+properties:
+ compatible:
+ const: gpio-charger
+
+ charger-type:
+ oneOf:
+ - const: unknown
+ - const: battery
+ - const: ups
+ - const: mains
+ - const: usb-sdp # USB standard downstream port
+ - const: usb-dcp # USB dedicated charging port
+ - const: usb-cdp # USB charging downstream port
+ - const: usb-aca # USB accessory charger adapter
+
+ gpios:
+ maxItems: 1
+ description: GPIO indicating the charger presence
+
+ charge-status-gpios:
+ maxItems: 1
+ description: GPIO indicating the charging status
+
+ charge-current-limit-gpios:
+ minItems: 1
+ maxItems: 32
+ description: GPIOs used for current limiting
+
+ charge-current-limit-mapping:
+ description: List of touples with current in uA and a GPIO bitmap (in
+ this order). The GPIOs are encoded in the same order as specified in
+ charge-current-limit-gpios. The touples must be provided in descending
+ order of the current limit.
+ $ref: "/meta-schemas/cell.yaml#array"
+
+required:
+ - compatible
+additionalProperties: false
+
+dependencies:
+ charge-current-limit-gpios: [ charge-current-limit-mapping ]
+ charge-current-limit-mapping: [ charge-current-limit-gpios ]
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ charger {
+ compatible = "gpio-charger";
+ charger-type = "usb-sdp";
+
+ gpios = <&gpd 28 GPIO_ACTIVE_LOW>;
+ charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>;
+
+ charge-current-limit-gpios = <&gpioA 11 GPIO_ACTIVE_HIGH>,
+ <&gpioA 12 GPIO_ACTIVE_HIGH>;
+ charge-current-limit-mapping = <2500000 0x00>, <700000 0x01>, <0 0x02>;
+ };
--
2.26.2
^ permalink raw reply related
* [PATCHv1 1/2] power: supply: gpio-charger: add charge-current-limit feature
From: Sebastian Reichel @ 2020-05-13 11:56 UTC (permalink / raw)
To: Sebastian Reichel, Rob Herring
Cc: linux-pm, devicetree, linux-kernel, kernel, Sebastian Reichel
Add new charge-current-limit feature to gpio-charger. This also
makes the online status GPIO optional, since hardware might only
expose the charge-current-limit feature and there is no good reason
to have it mandatory now that different GPIOs are supported.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
.../bindings/power/supply/gpio-charger.txt | 11 +-
drivers/power/supply/gpio-charger.c | 176 ++++++++++++++++--
2 files changed, 174 insertions(+), 13 deletions(-)
diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt b/Documentation/devicetree/bindings/power/supply/gpio-charger.txt
index 0fb33b2c62a6..dbfd29029f69 100644
--- a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/gpio-charger.txt
@@ -2,8 +2,6 @@ gpio-charger
Required properties :
- compatible : "gpio-charger"
- - gpios : GPIO indicating the charger presence.
- See GPIO binding in bindings/gpio/gpio.txt .
- charger-type : power supply type, one of
unknown
battery
@@ -15,7 +13,13 @@ Required properties :
usb-aca (USB accessory charger adapter)
Optional properties:
+ - gpios : GPIO indicating the charger presence.
+ See GPIO binding in bindings/gpio/gpio.txt .
- charge-status-gpios: GPIO indicating whether a battery is charging.
+ - charge-current-limit-gpios: Output GPIOs specifiers for limiting the charge current
+ - charge-current-limit-mapping: List of touples with current in uA and a GPIO bitmap (in this order).
+ The GPIOs are encoded in the same order as specified in charge-current-limit-gpios.
+ The touples must be provided in descending order of the current limit.
Example:
@@ -24,6 +28,9 @@ Example:
charger-type = "usb-sdp";
gpios = <&gpd 28 GPIO_ACTIVE_LOW>;
charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>;
+
+ charge-current-limit-gpios = <&gpioA 11 GPIO_ACTIVE_HIGH>, <&gpioA 12 GPIO_ACTIVE_HIGH>;
+ charge-current-limit-mapping = <2500000 0x00>, <700000 0x01>, <0 0x02>;
};
battery {
diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c
index 1b959c7f8b0e..4a5eac7cc36c 100644
--- a/drivers/power/supply/gpio-charger.c
+++ b/drivers/power/supply/gpio-charger.c
@@ -18,7 +18,13 @@
#include <linux/power/gpio-charger.h>
+struct gpio_mapping {
+ u32 limit_ua;
+ u32 gpiodata;
+} __packed;
+
struct gpio_charger {
+ struct device *dev;
unsigned int irq;
unsigned int charge_status_irq;
bool wakeup_enabled;
@@ -27,6 +33,11 @@ struct gpio_charger {
struct power_supply_desc charger_desc;
struct gpio_desc *gpiod;
struct gpio_desc *charge_status;
+
+ struct gpio_descs *current_limit_gpios;
+ struct gpio_mapping *current_limit_map;
+ u32 current_limit_map_size;
+ u32 charge_current_limit;
};
static irqreturn_t gpio_charger_irq(int irq, void *devid)
@@ -43,6 +54,35 @@ static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
return power_supply_get_drvdata(psy);
}
+static int set_charge_current_limit(struct gpio_charger *gpio_charger, int val)
+{
+ struct gpio_mapping mapping;
+ int ndescs = gpio_charger->current_limit_gpios->ndescs;
+ struct gpio_desc **gpios = gpio_charger->current_limit_gpios->desc;
+ int i;
+
+ if (!gpio_charger->current_limit_map_size)
+ return -EINVAL;
+
+ for (i = 0; i < gpio_charger->current_limit_map_size; i++) {
+ if (gpio_charger->current_limit_map[i].limit_ua <= val)
+ break;
+ }
+ mapping = gpio_charger->current_limit_map[i];
+
+ for (i = 0; i < ndescs; i++) {
+ bool val = (mapping.gpiodata >> i) & 1;
+ gpiod_set_value_cansleep(gpios[ndescs-i-1], val);
+ }
+
+ gpio_charger->charge_current_limit = mapping.limit_ua;
+
+ dev_dbg(gpio_charger->dev, "set charge current limit to %d (requested: %d)\n",
+ gpio_charger->charge_current_limit, val);
+
+ return 0;
+}
+
static int gpio_charger_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
@@ -58,6 +98,9 @@ static int gpio_charger_get_property(struct power_supply *psy,
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = gpio_charger->charge_current_limit;
+ break;
default:
return -EINVAL;
}
@@ -65,6 +108,34 @@ static int gpio_charger_get_property(struct power_supply *psy,
return 0;
}
+static int gpio_charger_set_property(struct power_supply *psy,
+ enum power_supply_property psp, const union power_supply_propval *val)
+{
+ struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ return set_charge_current_limit(gpio_charger, val->intval);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gpio_charger_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static enum power_supply_type gpio_charger_get_type(struct device *dev)
{
const char *chargetype;
@@ -112,9 +183,70 @@ static int gpio_charger_get_irq(struct device *dev, void *dev_id,
return irq;
}
+static int init_charge_current_limit(struct device *dev,
+ struct gpio_charger *gpio_charger)
+{
+ int i, len;
+ u32 cur_limit = U32_MAX;
+
+ gpio_charger->current_limit_gpios = devm_gpiod_get_array_optional(dev,
+ "charge-current-limit", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_charger->current_limit_gpios)) {
+ dev_err(dev, "error getting current-limit GPIOs\n");
+ return PTR_ERR(gpio_charger->current_limit_gpios);
+ }
+
+ if (!gpio_charger->current_limit_gpios)
+ return 0;
+
+ len = device_property_read_u32_array(dev, "charge-current-limit-mapping",
+ NULL, 0);
+ if (len < 0)
+ return len;
+
+ if (len % 2) {
+ dev_err(dev, "invalid charge-current-limit-mapping length\n");
+ return -EINVAL;
+ }
+
+ gpio_charger->current_limit_map = devm_kmalloc_array(dev,
+ len / 2, sizeof(*gpio_charger->current_limit_map), GFP_KERNEL);
+ if (!gpio_charger->current_limit_map)
+ return -ENOMEM;
+
+ gpio_charger->current_limit_map_size = len / 2;
+
+ len = device_property_read_u32_array(dev, "charge-current-limit-mapping",
+ (u32*) gpio_charger->current_limit_map, len);
+ if (len < 0)
+ return len;
+
+ for (i=0; i < gpio_charger->current_limit_map_size; i++) {
+ if (gpio_charger->current_limit_map[i].limit_ua > cur_limit) {
+ dev_err(dev, "invalid charge-current-limit-mapping\n");
+ return -EINVAL;
+ }
+
+ cur_limit = gpio_charger->current_limit_map[i].limit_ua;
+ }
+
+ /* default to smallest current limitation for safety reasons */
+ len = gpio_charger->current_limit_map_size - 1;
+ set_charge_current_limit(gpio_charger,
+ gpio_charger->current_limit_map[len].limit_ua);
+
+ return 0;
+}
+
+/*
+ * The entries will be overwritten by driver's probe routine depending
+ * on the available features. This list ensures, that the array is big
+ * enough for all optional features.
+ */
static enum power_supply_property gpio_charger_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_STATUS /* Must always be last in the array. */
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
};
static int gpio_charger_probe(struct platform_device *pdev)
@@ -128,6 +260,7 @@ static int gpio_charger_probe(struct platform_device *pdev)
int charge_status_irq;
unsigned long flags;
int ret;
+ int num_props = 0;
if (!pdata && !dev->of_node) {
dev_err(dev, "No platform data\n");
@@ -137,18 +270,19 @@ static int gpio_charger_probe(struct platform_device *pdev)
gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
if (!gpio_charger)
return -ENOMEM;
+ gpio_charger->dev = dev;
/*
* This will fetch a GPIO descriptor from device tree, ACPI or
* boardfile descriptor tables. It's good to try this first.
*/
- gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
+ gpio_charger->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
/*
* If this fails and we're not using device tree, try the
* legacy platform data method.
*/
- if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
+ if (!gpio_charger->gpiod && !dev->of_node) {
/* Non-DT: use legacy GPIO numbers */
if (!gpio_is_valid(pdata->gpio)) {
dev_err(dev, "Invalid gpio pin in pdata\n");
@@ -173,18 +307,38 @@ static int gpio_charger_probe(struct platform_device *pdev)
return PTR_ERR(gpio_charger->gpiod);
}
+ if (gpio_charger->gpiod &&
+ num_props < ARRAY_SIZE(gpio_charger_properties)) {
+ gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_ONLINE;
+ num_props++;
+ }
+
charge_status = devm_gpiod_get_optional(dev, "charge-status", GPIOD_IN);
- gpio_charger->charge_status = charge_status;
- if (IS_ERR(gpio_charger->charge_status))
- return PTR_ERR(gpio_charger->charge_status);
+ if (IS_ERR(charge_status))
+ return PTR_ERR(charge_status);
+ if (charge_status && num_props < ARRAY_SIZE(gpio_charger_properties)) {
+ gpio_charger->charge_status = charge_status;
+ gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_STATUS;
+ num_props++;
+ }
+
+ ret = init_charge_current_limit(dev, gpio_charger);
+ if (ret < 0)
+ return ret;
+ if (gpio_charger->current_limit_map &&
+ num_props < ARRAY_SIZE(gpio_charger_properties)) {
+ gpio_charger_properties[num_props] =
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+ num_props++;
+ }
charger_desc = &gpio_charger->charger_desc;
charger_desc->properties = gpio_charger_properties;
- charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
- /* Remove POWER_SUPPLY_PROP_STATUS from the supported properties. */
- if (!gpio_charger->charge_status)
- charger_desc->num_properties -= 1;
+ charger_desc->num_properties = num_props;
charger_desc->get_property = gpio_charger_get_property;
+ charger_desc->set_property = gpio_charger_set_property;
+ charger_desc->property_is_writeable =
+ gpio_charger_property_is_writeable;
psy_cfg.of_node = dev->of_node;
psy_cfg.drv_data = gpio_charger;
@@ -269,6 +423,6 @@ static struct platform_driver gpio_charger_driver = {
module_platform_driver(gpio_charger_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
+MODULE_DESCRIPTION("Driver for chargers only communicating via GPIO(s)");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-charger");
--
2.26.2
^ permalink raw reply related
* Re: [PATCH v4 00/12] PCI: aardvark: Fix support for Turris MOX and Compex wifi cards
From: Thomas Petazzoni @ 2020-05-13 11:56 UTC (permalink / raw)
To: Pali Rohár
Cc: Jason Cooper, Andrew Lunn, Gregory Clement, Sebastian Hesselbarth,
Rob Herring, Lorenzo Pieralisi, Andrew Murray, Bjorn Helgaas,
Remi Pommarel, Marek Behún, Tomasz Maciej Nowak, Xogium,
devicetree, linux-kernel, linux-pci
In-Reply-To: <20200430080625.26070-1-pali@kernel.org>
Hello,
On Thu, 30 Apr 2020 10:06:13 +0200
Pali Rohár <pali@kernel.org> wrote:
> Marek Behún (5):
> PCI: aardvark: Improve link training
> PCI: aardvark: Add PHY support
> dt-bindings: PCI: aardvark: Describe new properties
> arm64: dts: marvell: armada-37xx: Set pcie_reset_pin to gpio function
> arm64: dts: marvell: armada-37xx: Move PCIe comphy handle property
>
> Pali Rohár (7):
> PCI: aardvark: Train link immediately after enabling training
> PCI: aardvark: Don't blindly enable ASPM L0s and don't write to
> read-only register
> PCI: of: Zero max-link-speed value is invalid
> PCI: aardvark: Issue PERST via GPIO
> PCI: aardvark: Add FIXME comment for PCIE_CORE_CMD_STATUS_REG access
> PCI: aardvark: Replace custom macros by standard linux/pci_regs.h
> macros
> arm64: dts: marvell: armada-37xx: Move PCIe max-link-speed property
Thanks a lot for this work. For a number of reasons, I'm less involved
in Marvell platform support in Linux, but I reviewed your series and
followed the discussions around it, and I'm happy to give my:
Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
for the whole series. The changes all seem sensible, and have been
tested by several folks.
Thanks!
Thomas
--
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH 08/17] spi: dw: Clear DMAC register when done or stopped
From: Serge Semin @ 2020-05-13 11:56 UTC (permalink / raw)
To: Mark Brown
Cc: Serge Semin, Georgy Vlasov, Ramil Zaripov, Alexey Malahov,
Thomas Bogendoerfer, Paul Burton, Ralf Baechle, Arnd Bergmann,
Allison Randal, Andy Shevchenko, Gareth Williams, Rob Herring,
linux-mips, devicetree, Greg Kroah-Hartman, Thomas Gleixner,
linux-spi, linux-kernel
In-Reply-To: <20200508173134.GP4820@sirena.org.uk>
On Fri, May 08, 2020 at 06:31:34PM +0100, Mark Brown wrote:
> On Fri, May 08, 2020 at 04:29:33PM +0300, Serge Semin wrote:
> > If DMAC register is left uncleared any further DMAless transfers
> > may cause the DMAC hardware handshaking interface getting activated.
> > So the next DMA-based Rx/Tx transaction will be started right
> > after the dma_async_issue_pending() method is invoked even if no
> > DMATDLR/DMARDLR conditions are met. This at the same time may cause
> > the Tx/Rx FIFO buffers underrun/overrun. In order to fix this we
> > must clear DMAC register after a current DMA-based transaction is
> > finished.
>
> This also looks like a bugfix so should be pulled forwards to the start
> of the series if possible.
Ok.
-Sergey
^ permalink raw reply
* Re: [PATCH v4 00/12] PCI: aardvark: Fix support for Turris MOX and Compex wifi cards
From: Pali Rohár @ 2020-05-13 11:59 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: thomas.petazzoni, Bjorn Helgaas, Jason Cooper, Andrew Lunn,
Gregory Clement, Sebastian Hesselbarth, Rob Herring,
Andrew Murray, Remi Pommarel, Marek Behún,
Tomasz Maciej Nowak, Xogium, devicetree, linux-kernel, linux-pci
In-Reply-To: <20200513113314.GB32365@e121166-lin.cambridge.arm.com>
On Wednesday 13 May 2020 12:33:14 Lorenzo Pieralisi wrote:
> On Wed, May 13, 2020 at 01:16:51PM +0200, Pali Rohár wrote:
> > On Thursday 30 April 2020 10:06:13 Pali Rohár wrote:
> > > Hello,
> > >
> > > this is the fourth version of the patch series for Armada 3720 PCIe
> > > controller (aardvark). It's main purpose is to fix some bugs regarding
> > > buggy ath10k cards, but we also found out some suspicious stuff about
> > > the driver and the SOC itself, which we try to address.
> > >
> > > Patches are available also in my git branch pci-aardvark:
> > > https://git.kernel.org/pub/scm/linux/kernel/git/pali/linux.git/log/?h=pci-aardvark
> >
> > Hello! Thanks everybody for review and testing of this patch series.
> >
> > I would like to ask, is there something needed to fix / modify in this
> > patch series? If everything is OK, would you Bjorn or Lorenzo take this
> > patch series into your tree?
>
> We need Thomas' ACK on the series. We don't have this HW and
> we comment on the generic code, Thomas owns it and must check that
> what you are changing is sound.
Ok, we will wait for Thomas ACK/review.
> On patch 5 I share Rob's concerns - it does not make much sense
> to have something driver specific there, need to look further.
I fully understand yours concerns. I wanted to solve it. Problem is that
I really do not know which timeout is there applicable. I read
information about PERST# more times but I was not able to clearly deduce
that minimal timeout/delay needed for this reset scenario.
So what I was able to do are just experiments. I found out what is the
minimal needed time to correctly initialize wifi cars which I used for
testing.
You can look into my previous email [1] where I wrote which timeouts are
used by which drivers. Basically every driver is using its own custom
timeout and this is something which should be fixed / improved. In my
opinion authors tested their own (wifi) cards and measured minimal
timeout needed for initializing them.
So somebody with deeper PCI knowledge should look at this PERST# problem
and try to address it.
After it happens I see there two scenarios:
1) Timeout according to specification/authority is lower than what we
currently use. In this case it would mean that we have buggy wifi cards
(and we already know that people reported issues with Compex cards) and
we would have to stay with higher timeout. Probably we can define common
macro with timeout value and use it.
2) Timeout according to specification/authority is bigger then what we
currently use. In this case there is no problem to increase it, card
would be just longer in reset state. What could be problematic for
somebody is that this increase boot / initialization time.
[1] - https://lore.kernel.org/linux-pci/20200424092546.25p3hdtkehohe3xw@pali/
^ permalink raw reply
* RE: [PATCHv2 10/10] net: eth: altera: update devicetree bindings documentation
From: Ooi, Joyce @ 2020-05-13 12:00 UTC (permalink / raw)
To: Rob Herring
Cc: Thor Thayer, David S . Miller, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Westergreen, Dalon, Tan, Ley Foon,
See, Chin Liang, Nguyen, Dinh, devicetree@vger.kernel.org,
Ooi, Joyce
In-Reply-To: <20200512225240.GA18344@bogus>
> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Wednesday, May 13, 2020 6:53 AM
> To: Ooi, Joyce <joyce.ooi@intel.com>
> Cc: Thor Thayer <thor.thayer@linux.intel.com>; David S . Miller
> <davem@davemloft.net>; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; Westergreen, Dalon
> <dalon.westergreen@intel.com>; Tan, Ley Foon <ley.foon.tan@intel.com>;
> See, Chin Liang <chin.liang.see@intel.com>; Nguyen, Dinh
> <dinh.nguyen@intel.com>; devicetree@vger.kernel.org
> Subject: Re: [PATCHv2 10/10] net: eth: altera: update devicetree bindings
> documentation
>
> On Mon, May 04, 2020 at 04:25:58PM +0800, Joyce Ooi wrote:
> > From: Dalon Westergreen <dalon.westergreen@intel.com>
> >
> > Update devicetree bindings documentation to include msgdma prefetcher
> > and ptp bindings.
> >
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
> > Signed-off-by: Joyce Ooi <joyce.ooi@intel.com>
> > ---
> > v2: no change
> > ---
> > .../devicetree/bindings/net/altera_tse.txt | 103
> +++++++++++++++++----
> > 1 file changed, 84 insertions(+), 19 deletions(-)
>
> Reviewed-by: Rob Herring <robh@kernel.org>
>
> One nit below.
>
> >
> > diff --git a/Documentation/devicetree/bindings/net/altera_tse.txt
> > b/Documentation/devicetree/bindings/net/altera_tse.txt
> > index 0b7d4d3758ea..2f2d12603907 100644
> > --- a/Documentation/devicetree/bindings/net/altera_tse.txt
> > +++ b/Documentation/devicetree/bindings/net/altera_tse.txt
> > @@ -2,53 +2,86 @@
> >
> > Required properties:
> > - compatible: Should be "altr,tse-1.0" for legacy SGDMA based TSE, and
> should
> > - be "altr,tse-msgdma-1.0" for the preferred MSGDMA based
> TSE.
> > + be "altr,tse-msgdma-1.0" for the preferred MSGDMA based
> TSE,
> > + and "altr,tse-msgdma-2.0" for MSGDMA with prefetcher
> based
> > + implementations.
> > ALTR is supported for legacy device trees, but is deprecated.
> > altr should be used for all new designs.
> > - reg: Address and length of the register set for the device. It contains
> > the information of registers in the same order as described by
> > reg-names
> > - reg-names: Should contain the reg names
> > - "control_port": MAC configuration space region
> > - "tx_csr": xDMA Tx dispatcher control and status space region
> > - "tx_desc": MSGDMA Tx dispatcher descriptor space region
> > - "rx_csr" : xDMA Rx dispatcher control and status space region
> > - "rx_desc": MSGDMA Rx dispatcher descriptor space region
> > - "rx_resp": MSGDMA Rx dispatcher response space region
> > - "s1": SGDMA descriptor memory
> > - interrupts: Should contain the TSE interrupts and it's mode.
> > - interrupt-names: Should contain the interrupt names
> > - "rx_irq": xDMA Rx dispatcher interrupt
> > - "tx_irq": xDMA Tx dispatcher interrupt
> > + "rx_irq": DMA Rx dispatcher interrupt
> > + "tx_irq": DMA Tx dispatcher interrupt
> > - rx-fifo-depth: MAC receive FIFO buffer depth in bytes
> > - tx-fifo-depth: MAC transmit FIFO buffer depth in bytes
> > - phy-mode: See ethernet.txt in the same directory.
> > - phy-handle: See ethernet.txt in the same directory.
> > - phy-addr: See ethernet.txt in the same directory. A configuration should
> > include phy-handle or phy-addr.
> > -- altr,has-supplementary-unicast:
> > - If present, TSE supports additional unicast addresses.
> > - Otherwise additional unicast addresses are not supported.
> > -- altr,has-hash-multicast-filter:
> > - If present, TSE supports a hash based multicast filter.
> > - Otherwise, hash-based multicast filtering is not supported.
> > -
> > - mdio device tree subnode: When the TSE has a phy connected to its local
> > mdio, there must be device tree subnode with the following
> > required properties:
> > -
> > - compatible: Must be "altr,tse-mdio".
> > - #address-cells: Must be <1>.
> > - #size-cells: Must be <0>.
> >
> > For each phy on the mdio bus, there must be a node with the
> following
> > fields:
> > -
> > - reg: phy id used to communicate to phy.
> > - device_type: Must be "ethernet-phy".
> >
> > The MAC address will be determined using the optional properties
> > defined in ethernet.txt.
> >
> > +- altr,has-supplementary-unicast:
> > + If present, TSE supports additional unicast addresses.
> > + Otherwise additional unicast addresses are not supported.
> > +- altr,has-hash-multicast-filter:
> > + If present, TSE supports a hash based multicast filter.
> > + Otherwise, hash-based multicast filtering is not supported.
> > +- altr,has-ptp:
> > + If present, TSE supports 1588 timestamping. Currently only
> > + supported with the msgdma prefetcher.
> > +- altr,tx-poll-cnt:
> > + Optional cycle count for Tx prefetcher to poll descriptor
> > + list. If not present, defaults to 128, which at 125MHz is
> > + roughly 1usec. Only for "altr,tse-msgdma-2.0".
> > +- altr,rx-poll-cnt:
> > + Optional cycle count for Tx prefetcher to poll descriptor
> > + list. If not present, defaults to 128, which at 125MHz is
> > + roughly 1usec. Only for "altr,tse-msgdma-2.0".
> > +
> > +Required registers by compatibility string:
> > + - "altr,tse-1.0"
> > + "control_port": MAC configuration space region
> > + "tx_csr": DMA Tx dispatcher control and status space region
> > + "rx_csr" : DMA Rx dispatcher control and status space region
> > + "s1": DMA descriptor memory
> > +
> > + - "altr,tse-msgdma-1.0"
> > + "control_port": MAC configuration space region
> > + "tx_csr": DMA Tx dispatcher control and status space region
> > + "tx_desc": DMA Tx dispatcher descriptor space region
> > + "rx_csr" : DMA Rx dispatcher control and status space region
> > + "rx_desc": DMA Rx dispatcher descriptor space region
> > + "rx_resp": DMA Rx dispatcher response space region
> > +
> > + - "altr,tse-msgdma-2.0"
> > + "control_port": MAC configuration space region
> > + "tx_csr": DMA Tx dispatcher control and status space region
> > + "tx_pref": DMA Tx prefetcher configuration space region
> > + "rx_csr" : DMA Rx dispatcher control and status space region
> > + "rx_pref": DMA Rx prefetcher configuration space region
> > + "tod_ctrl": Time of Day Control register only required when
> > + timestamping support is enabled. Timestamping is
> > + only supported with the msgdma-2.0
> implementation.
> > +
> > +Optional properties:
> > +- local-mac-address: See ethernet.txt in the same directory.
> > +- max-frame-size: See ethernet.txt in the same directory.
> > +
> > Example:
> >
> > tse_sub_0_eth_tse_0: ethernet@1,00000000 { @@ -86,6 +119,11
> @@
> > Example:
> > device_type = "ethernet-phy";
> > };
> >
> > + phy2: ethernet-phy@2 {
> > + reg = <0x2>;
> > + device_type = "ethernet-phy";
> > + };
> > +
> > };
> > };
> >
> > @@ -111,3 +149,30 @@ Example:
> > altr,has-hash-multicast-filter;
> > phy-handle = <&phy1>;
> > };
> > +
> > +
> > + tse_sub_2_eth_tse_0: ethernet@1,00002000 {
>
> What bus is this on? Usually a ',' like this is for a chip select number. If just a
> 64-bit address, then no comma.
It's a 64-bit address. I'll remove the comma for this and the one above.
>
> > + compatible = "altr,tse-msgdma-2.0";
> > + reg = <0x00000001 0x00002000 0x00000400>,
> > + <0x00000001 0x00002400 0x00000020>,
> > + <0x00000001 0x00002420 0x00000020>,
> > + <0x00000001 0x00002440 0x00000020>,
> > + <0x00000001 0x00002460 0x00000020>,
> > + <0x00000001 0x00002480 0x00000040>;
> > + reg-names = "control_port", "rx_csr", "rx_pref","tx_csr",
> "tx_pref", "tod_ctrl";
> > + interrupt-parent = <&hps_0_arm_gic_0>;
> > + interrupts = <0 45 4>, <0 44 4>;
> > + interrupt-names = "rx_irq", "tx_irq";
> > + rx-fifo-depth = <2048>;
> > + tx-fifo-depth = <2048>;
> > + address-bits = <48>;
> > + max-frame-size = <1500>;
> > + local-mac-address = [ 00 00 00 00 00 00 ];
> > + phy-mode = "sgmii";
> > + altr,has-supplementary-unicast;
> > + altr,has-hash-multicast-filter;
> > + altr,has-ptp;
> > + altr,tx-poll-cnt = <128>;
> > + altr,rx-poll-cnt = <32>;
> > + phy-handle = <&phy2>;
> > + };
> > --
> > 2.13.0
> >
^ permalink raw reply
* Re: [PATCH 2/5] dt-bindings: usb: ehci: Add missing 2nd register region
From: Greg Kroah-Hartman @ 2020-05-13 12:05 UTC (permalink / raw)
To: Rob Herring
Cc: devicetree, linux-clk, linux-usb, linux-spi, linux-kernel,
Mark Brown
In-Reply-To: <20200512204543.22090-2-robh@kernel.org>
On Tue, May 12, 2020 at 03:45:40PM -0500, Rob Herring wrote:
> The 'ibm,usb-ehci-440epx' compatible has a 2nd 'reg' region, but the
> schema says there is only 1 region. Fix this.
>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: linux-usb@vger.kernel.org
> Signed-off-by: Rob Herring <robh@kernel.org>
> ---
> Please ack, dependency for patch 5.
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
^ permalink raw reply
* Re: [PATCH v7 2/2] mmc: host: meson-mx-sdhc: new driver for the Amlogic Meson SDHC host
From: Ulf Hansson @ 2020-05-13 12:04 UTC (permalink / raw)
To: Martin Blumenstingl
Cc: open list:ARM/Amlogic Meson..., linux-mmc@vger.kernel.org,
Rob Herring, DTML, Jianxin Pan, Linux Kernel Mailing List,
yinxin_1989, Linux ARM, lnykww, Anand Moon, Jerome Brunet
In-Reply-To: <20200512204147.504087-3-martin.blumenstingl@googlemail.com>
On Tue, 12 May 2020 at 22:42, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
>
> The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
> card interface with 1/4/8-bit bus width.
> It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
>
> The public S805 datasheet [0] contains a short documentation about the
> registers. Unfortunately it does not describe how to use the registers
> to make the hardware work. Thus this driver is based on reading (and
> understanding) the Amlogic 3.10 GPL kernel code.
>
> Some hardware details are not easy to see. Jianxin Pan was kind enough
> to answer my questions:
> The hardware has built-in busy timeout support. The maximum timeout is
> 30 seconds. This is only documented in Amlogic's internal
> documentation.
>
> The controller only works with very specific clock configurations. The
> details are not part of the public datasheet. In my own words the
> supported configurations are:
> - 399.812kHz: clkin = 850MHz div = 2126 sd_rx_phase = 63
> - 1MHz: clkin = 850MHz div = 850 sd_rx_phase = 55
> - 5.986MHz: clkin = 850MHz div = 142 sd_rx_phase = 24
> - 25MHz: clkin = 850MHz div = 34 sd_rx_phase = 15
> - 47.222MHz: clkin = 850MHz div = 18 sd_rx_phase = 11/15 (SDR50/HS)
> - 53.125MHz: clkin = 850MHz div = 16 sd_rx_phase = (tuning)
> - 70.833MHz: clkin = 850MHz div = 12 sd_rx_phase = (tuning)
> - 85MHz: clkin = 850MHz div = 10 sd_rx_phase = (tuning)
> - 94.44MHz: clkin = 850MHz div = 9 sd_rx_phase = (tuning)
> - 106.25MHz: clkin = 850MHz div = 8 sd_rx_phase = (tuning)
> - 127.5MHz: clkin = 1275MHz div = 10 sd_rx_phase = (tuning)
> - 141.667MHz: clkin = 850MHz div = 6 sd_rx_phase = (tuning)
> - 159.375MHz: clkin = 1275MHz div = 8 sd_rx_phase = (tuning)
> - 212.5MHz: clkin = 1275MHz div = 6 sd_rx_phase = (tuning)
> - (sd_tx_phase is always 1, 94.44MHz is not listed in the datasheet
> but this is what the 3.10 BSP kernel on Odroid-C1 actually uses)
>
> NOTE: CMD23 support is disabled for now because it results in command
> timeouts and thus decreases read performance.
>
> Tested-by: Wei Wang <lnykww@gmail.com>
> Tested-by: Xin Yin <yinxin_1989@aliyun.com>
> Reviewed-by: Xin Yin <yinxin_1989@aliyun.com>
> Tested-by: Anand Moon <linux.amoon@gmail.com>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
This looks good to me, however I am awaiting a reviewed-by tag from
Jerome, to make sure the clock implementation turn out as agreed upon.
Kind regards
Uffe
> ---
> drivers/mmc/host/Kconfig | 14 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/meson-mx-sdhc-clkc.c | 158 +++++
> drivers/mmc/host/meson-mx-sdhc.c | 907 ++++++++++++++++++++++++++
> drivers/mmc/host/meson-mx-sdhc.h | 141 ++++
> 5 files changed, 1221 insertions(+)
> create mode 100644 drivers/mmc/host/meson-mx-sdhc-clkc.c
> create mode 100644 drivers/mmc/host/meson-mx-sdhc.c
> create mode 100644 drivers/mmc/host/meson-mx-sdhc.h
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 462b5352fea7..df3fc572f842 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -405,6 +405,20 @@ config MMC_MESON_GX
>
> If you have a controller with this interface, say Y here.
>
> +config MMC_MESON_MX_SDHC
> + tristate "Amlogic Meson SDHC Host Controller support"
> + depends on (ARM && ARCH_MESON) || COMPILE_TEST
> + depends on COMMON_CLK
> + depends on OF
> + help
> + This selects support for the SDHC Host Controller on
> + Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
> + The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
> + with 1, 4, and 8 bit bus widths.
> +
> + If you have a controller with this interface, say Y or M here.
> + If unsure, say N.
> +
> config MMC_MESON_MX_SDIO
> tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
> depends on ARCH_MESON || COMPILE_TEST
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index b929ef941208..8bcb420e071c 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_VUB300) += vub300.o
> obj-$(CONFIG_MMC_USHC) += ushc.o
> obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
> obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
> +obj-$(CONFIG_MMC_MESON_MX_SDHC) += meson-mx-sdhc-clkc.o meson-mx-sdhc.o
> obj-$(CONFIG_MMC_MESON_MX_SDIO) += meson-mx-sdio.o
> obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
> obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
> diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c
> new file mode 100644
> index 000000000000..ab0d6c68a078
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc-clkc.c
> @@ -0,0 +1,158 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Amlogic Meson SDHC clock controller
> + *
> + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +
> +#include "meson-mx-sdhc.h"
> +
> +#define MESON_SDHC_NUM_BUILTIN_CLKS 6
> +
> +struct meson_mx_sdhc_clkc {
> + struct clk_mux src_sel;
> + struct clk_divider div;
> + struct clk_gate mod_clk_en;
> + struct clk_gate tx_clk_en;
> + struct clk_gate rx_clk_en;
> + struct clk_gate sd_clk_en;
> +};
> +
> +static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
> + { .fw_name = "clkin0" },
> + { .fw_name = "clkin1" },
> + { .fw_name = "clkin2" },
> + { .fw_name = "clkin3" },
> +};
> +
> +static const struct clk_div_table meson_mx_sdhc_div_table[] = {
> + { .div = 6, .val = 5, },
> + { .div = 8, .val = 7, },
> + { .div = 9, .val = 8, },
> + { .div = 10, .val = 9, },
> + { .div = 12, .val = 11, },
> + { .div = 16, .val = 15, },
> + { .div = 18, .val = 17, },
> + { .div = 34, .val = 33, },
> + { .div = 142, .val = 141, },
> + { .div = 850, .val = 849, },
> + { .div = 2126, .val = 2125, },
> + { .div = 4096, .val = 4095, },
> + { /* sentinel */ }
> +};
> +
> +static int meson_mx_sdhc_clk_hw_register(struct device *dev,
> + const char *name_suffix,
> + const struct clk_parent_data *parents,
> + unsigned int num_parents,
> + const struct clk_ops *ops,
> + struct clk_hw *hw)
> +{
> + struct clk_init_data init = { 0 };
> + char clk_name[32];
> +
> + snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
> + name_suffix);
> +
> + init.name = clk_name;
> + init.ops = ops;
> + init.flags = CLK_SET_RATE_PARENT;
> + init.parent_data = parents;
> + init.num_parents = num_parents;
> +
> + hw->init = &init;
> +
> + return devm_clk_hw_register(dev, hw);
> +}
> +
> +static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
> + const char *name_suffix,
> + struct clk_hw *parent,
> + struct clk_hw *hw)
> +{
> + struct clk_parent_data parent_data = { .hw = parent };
> +
> + return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
> + &clk_gate_ops, hw);
> +}
> +
> +int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
> + struct clk_bulk_data *clk_bulk_data)
> +{
> + struct clk_parent_data div_parent = { 0 };
> + struct meson_mx_sdhc_clkc *clkc_data;
> + int ret;
> +
> + clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
> + if (!clkc_data)
> + return -ENOMEM;
> +
> + clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
> + clkc_data->src_sel.mask = 0x3;
> + clkc_data->src_sel.shift = 16;
> + ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
> + meson_mx_sdhc_src_sel_parents, 4,
> + &clk_mux_ops,
> + &clkc_data->src_sel.hw);
> + if (ret)
> + return ret;
> +
> + clkc_data->div.reg = base + MESON_SDHC_CLKC;
> + clkc_data->div.shift = 0;
> + clkc_data->div.width = 12;
> + clkc_data->div.table = meson_mx_sdhc_div_table;
> + div_parent.hw = &clkc_data->src_sel.hw;
> + ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
> + &clk_divider_ops,
> + &clkc_data->div.hw);
> + if (ret)
> + return ret;
> +
> + clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
> + clkc_data->mod_clk_en.bit_idx = 15;
> + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
> + &clkc_data->div.hw,
> + &clkc_data->mod_clk_en.hw);
> + if (ret)
> + return ret;
> +
> + clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
> + clkc_data->tx_clk_en.bit_idx = 14;
> + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
> + &clkc_data->div.hw,
> + &clkc_data->tx_clk_en.hw);
> + if (ret)
> + return ret;
> +
> + clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
> + clkc_data->rx_clk_en.bit_idx = 13;
> + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
> + &clkc_data->div.hw,
> + &clkc_data->rx_clk_en.hw);
> + if (ret)
> + return ret;
> +
> + clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
> + clkc_data->sd_clk_en.bit_idx = 12;
> + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
> + &clkc_data->div.hw,
> + &clkc_data->sd_clk_en.hw);
> + if (ret)
> + return ret;
> +
> + /*
> + * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
> + * available.
> + */
> + clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
> + clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
> + clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
> + clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
> +
> + return 0;
> +}
> diff --git a/drivers/mmc/host/meson-mx-sdhc.c b/drivers/mmc/host/meson-mx-sdhc.c
> new file mode 100644
> index 000000000000..5c00958d7754
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc.c
> @@ -0,0 +1,907 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Amlogic Meson6/Meson8/Meson8b/Meson8m2 SDHC MMC host controller driver.
> + *
> + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/slot-gpio.h>
> +
> +#include "meson-mx-sdhc.h"
> +
> +#define MESON_SDHC_NUM_BULK_CLKS 4
> +#define MESON_SDHC_MAX_BLK_SIZE 512
> +#define MESON_SDHC_NUM_TUNING_TRIES 10
> +
> +#define MESON_SDHC_WAIT_CMD_READY_SLEEP_US 1
> +#define MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US 100000
> +#define MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US 1
> +#define MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US 200
> +
> +struct meson_mx_sdhc_data {
> + void (*init_hw)(struct mmc_host *mmc);
> + void (*set_pdma)(struct mmc_host *mmc);
> + void (*wait_before_send)(struct mmc_host *mmc);
> + bool hardware_flush_all_cmds;
> +};
> +
> +struct meson_mx_sdhc_host {
> + struct mmc_host *mmc;
> +
> + struct mmc_request *mrq;
> + struct mmc_command *cmd;
> + int error;
> +
> + struct regmap *regmap;
> +
> + struct clk *pclk;
> + struct clk *sd_clk;
> + struct clk_bulk_data bulk_clks[MESON_SDHC_NUM_BULK_CLKS];
> + bool bulk_clks_enabled;
> +
> + const struct meson_mx_sdhc_data *platform;
> +};
> +
> +static const struct regmap_config meson_mx_sdhc_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = MESON_SDHC_CLK2,
> +};
> +
> +static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL |
> + MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
> + MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX |
> + MESON_SDHC_SRST_DMA_IF);
> + usleep_range(10, 100);
> +
> + regmap_write(host->regmap, MESON_SDHC_SRST, 0);
> + usleep_range(10, 100);
> +}
> +
> +static void meson_mx_sdhc_clear_fifo(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 stat;
> +
> + regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
> + if (!FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) &&
> + !FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
> + return;
> +
> + regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_RXFIFO |
> + MESON_SDHC_SRST_TXFIFO | MESON_SDHC_SRST_MAIN_CTRL);
> + udelay(5);
> +
> + regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
> + if (FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) ||
> + FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
> + dev_warn(mmc_dev(host->mmc),
> + "Failed to clear FIFOs, RX: %lu, TX: %lu\n",
> + FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat),
> + FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat));
> +}
> +
> +static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 stat, esta;
> + int ret;
> +
> + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
> + !(stat & MESON_SDHC_STAT_CMD_BUSY),
> + MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
> + MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
> + if (ret) {
> + dev_warn(mmc_dev(mmc),
> + "Failed to poll for CMD_BUSY while processing CMD%d\n",
> + host->cmd->opcode);
> + meson_mx_sdhc_hw_reset(mmc);
> + }
> +
> + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
> + !(esta & MESON_SDHC_ESTA_11_13),
> + MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
> + MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
> + if (ret) {
> + dev_warn(mmc_dev(mmc),
> + "Failed to poll for ESTA[13:11] while processing CMD%d\n",
> + host->cmd->opcode);
> + meson_mx_sdhc_hw_reset(mmc);
> + }
> +}
> +
> +static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
> + struct mmc_command *cmd)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 ictl, send;
> + int pack_len;
> +
> + host->cmd = cmd;
> +
> + ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
> + MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
> + MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
> +
> + send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
> +
> + if (cmd->data) {
> + send |= MESON_SDHC_SEND_CMD_HAS_DATA;
> + send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
> + cmd->data->blocks - 1);
> +
> + if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
> + pack_len = cmd->data->blksz;
> + else
> + pack_len = 0;
> +
> + if (cmd->data->flags & MMC_DATA_WRITE)
> + send |= MESON_SDHC_SEND_DATA_DIR;
> +
> + /*
> + * If command with no data, just wait response done
> + * interrupt(int[0]), and if command with data transfer, just
> + * wait dma done interrupt(int[11]), don't need care about
> + * dat0 busy or not.
> + */
> + if (host->platform->hardware_flush_all_cmds ||
> + cmd->data->flags & MMC_DATA_WRITE)
> + /* hardware flush: */
> + ictl |= MESON_SDHC_ICTL_DMA_DONE;
> + else
> + /* software flush: */
> + ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
> + } else {
> + pack_len = 0;
> +
> + ictl |= MESON_SDHC_ICTL_RESP_OK;
> + }
> +
> + if (cmd->opcode == MMC_STOP_TRANSMISSION)
> + send |= MESON_SDHC_SEND_DATA_STOP;
> +
> + if (cmd->flags & MMC_RSP_PRESENT)
> + send |= MESON_SDHC_SEND_CMD_HAS_RESP;
> +
> + if (cmd->flags & MMC_RSP_136) {
> + send |= MESON_SDHC_SEND_RESP_LEN;
> + send |= MESON_SDHC_SEND_RESP_NO_CRC;
> + }
> +
> + if (!(cmd->flags & MMC_RSP_CRC))
> + send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +
> + if (cmd->flags & MMC_RSP_BUSY)
> + send |= MESON_SDHC_SEND_R1B;
> +
> + /* enable the new IRQs and mask all pending ones */
> + regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
> + regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
> +
> + regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> + MESON_SDHC_CTRL_PACK_LEN,
> + FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
> +
> + if (cmd->data)
> + regmap_write(host->regmap, MESON_SDHC_ADDR,
> + sg_dma_address(cmd->data->sg));
> +
> + meson_mx_sdhc_wait_cmd_ready(mmc);
> +
> + if (cmd->data)
> + host->platform->set_pdma(mmc);
> +
> + if (host->platform->wait_before_send)
> + host->platform->wait_before_send(mmc);
> +
> + regmap_write(host->regmap, MESON_SDHC_SEND, send);
> +}
> +
> +static void meson_mx_sdhc_disable_clks(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + if (!host->bulk_clks_enabled)
> + return;
> +
> + clk_bulk_disable_unprepare(MESON_SDHC_NUM_BULK_CLKS, host->bulk_clks);
> +
> + host->bulk_clks_enabled = false;
> +}
> +
> +static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + int ret;
> +
> + if (host->bulk_clks_enabled)
> + return 0;
> +
> + ret = clk_bulk_prepare_enable(MESON_SDHC_NUM_BULK_CLKS,
> + host->bulk_clks);
> + if (ret)
> + return ret;
> +
> + host->bulk_clks_enabled = true;
> +
> + return 0;
> +}
> +
> +static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 rx_clk_phase;
> + int ret;
> +
> + meson_mx_sdhc_disable_clks(mmc);
> +
> + if (ios->clock) {
> + ret = clk_set_rate(host->sd_clk, ios->clock);
> + if (ret) {
> + dev_warn(mmc_dev(mmc),
> + "Failed to set MMC clock to %uHz: %d\n",
> + ios->clock, host->error);
> + return ret;
> + }
> +
> + ret = meson_mx_sdhc_enable_clks(mmc);
> + if (ret)
> + return ret;
> +
> + mmc->actual_clock = clk_get_rate(host->sd_clk);
> +
> + /*
> + * according to Amlogic the following latching points are
> + * selected with empirical values, there is no (known) formula
> + * to calculate these.
> + */
> + if (mmc->actual_clock > 100000000) {
> + rx_clk_phase = 1;
> + } else if (mmc->actual_clock > 45000000) {
> + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
> + rx_clk_phase = 15;
> + else
> + rx_clk_phase = 11;
> + } else if (mmc->actual_clock >= 25000000) {
> + rx_clk_phase = 15;
> + } else if (mmc->actual_clock > 5000000) {
> + rx_clk_phase = 23;
> + } else if (mmc->actual_clock > 1000000) {
> + rx_clk_phase = 55;
> + } else {
> + rx_clk_phase = 1061;
> + }
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
> + MESON_SDHC_CLK2_RX_CLK_PHASE,
> + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
> + rx_clk_phase));
> + } else {
> + mmc->actual_clock = 0;
> + }
> +
> + return 0;
> +}
> +
> +static void meson_mx_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + unsigned short vdd = ios->vdd;
> +
> + switch (ios->power_mode) {
> + case MMC_POWER_OFF:
> + vdd = 0;
> + fallthrough;
> +
> + case MMC_POWER_UP:
> + if (!IS_ERR(mmc->supply.vmmc)) {
> + host->error = mmc_regulator_set_ocr(mmc,
> + mmc->supply.vmmc,
> + vdd);
> + if (host->error)
> + return;
> + }
> +
> + break;
> +
> + case MMC_POWER_ON:
> + break;
> + }
> +
> + host->error = meson_mx_sdhc_set_clk(mmc, ios);
> + if (host->error)
> + return;
> +
> + switch (ios->bus_width) {
> + case MMC_BUS_WIDTH_1:
> + regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> + MESON_SDHC_CTRL_DAT_TYPE,
> + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 0));
> + break;
> +
> + case MMC_BUS_WIDTH_4:
> + regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> + MESON_SDHC_CTRL_DAT_TYPE,
> + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 1));
> + break;
> +
> + case MMC_BUS_WIDTH_8:
> + regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> + MESON_SDHC_CTRL_DAT_TYPE,
> + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 2));
> + break;
> +
> + default:
> + dev_err(mmc_dev(mmc), "unsupported bus width: %d\n",
> + ios->bus_width);
> + host->error = -EINVAL;
> + return;
> + }
> +}
> +
> +static int meson_mx_sdhc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct mmc_data *data = mrq->data;
> + int dma_len;
> +
> + if (!data)
> + return 0;
> +
> + dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
> + mmc_get_dma_dir(data));
> + if (dma_len <= 0) {
> + dev_err(mmc_dev(mmc), "dma_map_sg failed\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static void meson_mx_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + struct mmc_command *cmd = mrq->cmd;
> +
> + if (!host->error)
> + host->error = meson_mx_sdhc_map_dma(mmc, mrq);
> +
> + if (host->error) {
> + cmd->error = host->error;
> + mmc_request_done(mmc, mrq);
> + return;
> + }
> +
> + host->mrq = mrq;
> +
> + meson_mx_sdhc_start_cmd(mmc, mrq->cmd);
> +}
> +
> +static int meson_mx_sdhc_card_busy(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 stat;
> +
> + regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
> + return FIELD_GET(MESON_SDHC_STAT_DAT3_0, stat) == 0;
> +}
> +
> +static bool meson_mx_sdhc_tuning_point_matches(struct mmc_host *mmc,
> + u32 opcode)
> +{
> + unsigned int i, num_matches = 0;
> + int ret;
> +
> + for (i = 0; i < MESON_SDHC_NUM_TUNING_TRIES; i++) {
> + ret = mmc_send_tuning(mmc, opcode, NULL);
> + if (!ret)
> + num_matches++;
> + }
> +
> + return num_matches == MESON_SDHC_NUM_TUNING_TRIES;
> +}
> +
> +static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + int div, start, len, best_start, best_len;
> + int curr_phase, old_phase, new_phase;
> + u32 val;
> +
> + len = 0;
> + start = 0;
> + best_len = 0;
> +
> + regmap_read(host->regmap, MESON_SDHC_CLK2, &val);
> + old_phase = FIELD_GET(MESON_SDHC_CLK2_RX_CLK_PHASE, val);
> +
> + regmap_read(host->regmap, MESON_SDHC_CLKC, &val);
> + div = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val);
> +
> + for (curr_phase = 0; curr_phase <= div; curr_phase++) {
> + regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
> + MESON_SDHC_CLK2_RX_CLK_PHASE,
> + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
> + curr_phase));
> +
> + if (meson_mx_sdhc_tuning_point_matches(mmc, opcode)) {
> + if (!len) {
> + start = curr_phase;
> +
> + dev_dbg(mmc_dev(mmc),
> + "New RX phase window starts at %u\n",
> + start);
> + }
> +
> + len++;
> + } else {
> + if (len > best_len) {
> + best_start = start;
> + best_len = len;
> +
> + dev_dbg(mmc_dev(mmc),
> + "New best RX phase window: %u - %u\n",
> + best_start, best_start + best_len);
> + }
> +
> + /* reset the current window */
> + len = 0;
> + }
> + }
> +
> + if (len > best_len)
> + /* the last window is the best (or possibly only) window */
> + new_phase = start + (len / 2);
> + else if (best_len)
> + /* there was a better window than the last */
> + new_phase = best_start + (best_len / 2);
> + else
> + /* no window was found at all, reset to the original phase */
> + new_phase = old_phase;
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
> + MESON_SDHC_CLK2_RX_CLK_PHASE,
> + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
> + new_phase));
> +
> + if (!len && !best_len)
> + return -EIO;
> +
> + dev_dbg(mmc_dev(mmc), "Tuned RX clock phase to %u\n", new_phase);
> +
> + return 0;
> +}
> +
> +static const struct mmc_host_ops meson_mx_sdhc_ops = {
> + .hw_reset = meson_mx_sdhc_hw_reset,
> + .request = meson_mx_sdhc_request,
> + .set_ios = meson_mx_sdhc_set_ios,
> + .card_busy = meson_mx_sdhc_card_busy,
> + .execute_tuning = meson_mx_sdhc_execute_tuning,
> + .get_cd = mmc_gpio_get_cd,
> + .get_ro = mmc_gpio_get_ro,
> +};
> +
> +static void meson_mx_sdhc_request_done(struct meson_mx_sdhc_host *host)
> +{
> + struct mmc_request *mrq = host->mrq;
> + struct mmc_host *mmc = host->mmc;
> +
> + /* disable interrupts and mask all pending ones */
> + regmap_update_bits(host->regmap, MESON_SDHC_ICTL,
> + MESON_SDHC_ICTL_ALL_IRQS, 0);
> + regmap_update_bits(host->regmap, MESON_SDHC_ISTA,
> + MESON_SDHC_ISTA_ALL_IRQS, MESON_SDHC_ISTA_ALL_IRQS);
> +
> + host->mrq = NULL;
> + host->cmd = NULL;
> +
> + mmc_request_done(mmc, mrq);
> +}
> +
> +static u32 meson_mx_sdhc_read_response(struct meson_mx_sdhc_host *host, u8 idx)
> +{
> + u32 val;
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_DMA_MODE, 0);
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_PIO_RDRESP,
> + FIELD_PREP(MESON_SDHC_PDMA_PIO_RDRESP, idx));
> +
> + regmap_read(host->regmap, MESON_SDHC_ARGU, &val);
> +
> + return val;
> +}
> +
> +static irqreturn_t meson_mx_sdhc_irq(int irq, void *data)
> +{
> + struct meson_mx_sdhc_host *host = data;
> + struct mmc_command *cmd = host->cmd;
> + u32 ictl, ista;
> +
> + regmap_read(host->regmap, MESON_SDHC_ICTL, &ictl);
> + regmap_read(host->regmap, MESON_SDHC_ISTA, &ista);
> +
> + if (!(ictl & ista))
> + return IRQ_NONE;
> +
> + if (ista & MESON_SDHC_ISTA_RXFIFO_FULL ||
> + ista & MESON_SDHC_ISTA_TXFIFO_EMPTY)
> + cmd->error = -EIO;
> + else if (ista & MESON_SDHC_ISTA_RESP_ERR_CRC)
> + cmd->error = -EILSEQ;
> + else if (ista & MESON_SDHC_ISTA_RESP_TIMEOUT)
> + cmd->error = -ETIMEDOUT;
> +
> + if (cmd->data) {
> + if (ista & MESON_SDHC_ISTA_DATA_ERR_CRC)
> + cmd->data->error = -EILSEQ;
> + else if (ista & MESON_SDHC_ISTA_DATA_TIMEOUT)
> + cmd->data->error = -ETIMEDOUT;
> + }
> +
> + if (cmd->error || (cmd->data && cmd->data->error))
> + dev_dbg(mmc_dev(host->mmc), "CMD%d error, ISTA: 0x%08x\n",
> + cmd->opcode, ista);
> +
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data)
> +{
> + struct meson_mx_sdhc_host *host = irq_data;
> + struct mmc_command *cmd;
> + u32 val;
> +
> + cmd = host->cmd;
> + if (WARN_ON(!cmd))
> + return IRQ_HANDLED;
> +
> + if (cmd->data && !cmd->data->error) {
> + if (!host->platform->hardware_flush_all_cmds &&
> + cmd->data->flags & MMC_DATA_READ) {
> + meson_mx_sdhc_wait_cmd_ready(host->mmc);
> +
> + val = FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
> + 2);
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
> + val);
> + }
> +
> + dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
> + cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
> +
> + cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
> + }
> +
> + meson_mx_sdhc_wait_cmd_ready(host->mmc);
> +
> + if (cmd->flags & MMC_RSP_136) {
> + cmd->resp[0] = meson_mx_sdhc_read_response(host, 4);
> + cmd->resp[1] = meson_mx_sdhc_read_response(host, 3);
> + cmd->resp[2] = meson_mx_sdhc_read_response(host, 2);
> + cmd->resp[3] = meson_mx_sdhc_read_response(host, 1);
> + } else {
> + cmd->resp[0] = meson_mx_sdhc_read_response(host, 0);
> + }
> +
> + if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
> + meson_mx_sdhc_hw_reset(host->mmc);
> + else if (cmd->data)
> + /*
> + * Clear the FIFOs after completing data transfers to prevent
> + * corrupting data on write access. It's not clear why this is
> + * needed (for reads and writes), but it mimics what the BSP
> + * kernel did.
> + */
> + meson_mx_sdhc_clear_fifo(host->mmc);
> +
> + meson_mx_sdhc_request_done(host);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void meson_mx_sdhc_init_hw_meson8(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + regmap_write(host->regmap, MESON_SDHC_MISC,
> + FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 7) |
> + FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
> + FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
> +
> + regmap_write(host->regmap, MESON_SDHC_ENHC,
> + FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 63) |
> + MESON_SDHC_ENHC_MESON6_DMA_WR_RESP |
> + FIELD_PREP(MESON_SDHC_ENHC_MESON6_RX_TIMEOUT, 255) |
> + FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
> +};
> +
> +static void meson_mx_sdhc_set_pdma_meson8(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + if (host->cmd->data->flags & MMC_DATA_WRITE)
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_DMA_MODE |
> + MESON_SDHC_PDMA_RD_BURST |
> + MESON_SDHC_PDMA_TXFIFO_FILL,
> + MESON_SDHC_PDMA_DMA_MODE |
> + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 31) |
> + MESON_SDHC_PDMA_TXFIFO_FILL);
> + else
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_DMA_MODE |
> + MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
> + MESON_SDHC_PDMA_DMA_MODE |
> + FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
> + 1));
> +
> + if (host->cmd->data->flags & MMC_DATA_WRITE)
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_RD_BURST,
> + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15));
> +}
> +
> +static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
> + val == 0,
> + MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
> + MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
> + if (ret)
> + dev_warn(mmc_dev(mmc),
> + "Failed to wait for ESTA to clear: 0x%08x\n", val);
> +
> + if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
> + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
> + val, val & MESON_SDHC_STAT_TXFIFO_CNT,
> + MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
> + MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
> + if (ret)
> + dev_warn(mmc_dev(mmc),
> + "Failed to wait for TX FIFO to fill\n");
> + }
> +}
> +
> +static void meson_mx_sdhc_init_hw_meson8m2(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + regmap_write(host->regmap, MESON_SDHC_MISC,
> + FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 6) |
> + FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
> + FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
> +
> + regmap_write(host->regmap, MESON_SDHC_ENHC,
> + FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 64) |
> + FIELD_PREP(MESON_SDHC_ENHC_MESON8M2_DEBUG, 1) |
> + MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE |
> + FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
> +}
> +
> +static void meson_mx_sdhc_set_pdma_meson8m2(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_DMA_MODE, MESON_SDHC_PDMA_DMA_MODE);
> +}
> +
> +static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
> +{
> + struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +
> + meson_mx_sdhc_hw_reset(mmc);
> +
> + regmap_write(host->regmap, MESON_SDHC_CTRL,
> + FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |
> + FIELD_PREP(MESON_SDHC_CTRL_RX_TIMEOUT, 0x7f) |
> + FIELD_PREP(MESON_SDHC_CTRL_RX_ENDIAN, 0x7) |
> + FIELD_PREP(MESON_SDHC_CTRL_TX_ENDIAN, 0x7));
> +
> + /*
> + * start with a valid divider and enable the memory (un-setting
> + * MESON_SDHC_CLKC_MEM_PWR_OFF).
> + */
> + regmap_write(host->regmap, MESON_SDHC_CLKC, MESON_SDHC_CLKC_CLK_DIV);
> +
> + regmap_write(host->regmap, MESON_SDHC_CLK2,
> + FIELD_PREP(MESON_SDHC_CLK2_SD_CLK_PHASE, 1));
> +
> + regmap_write(host->regmap, MESON_SDHC_PDMA,
> + MESON_SDHC_PDMA_DMA_URGENT |
> + FIELD_PREP(MESON_SDHC_PDMA_WR_BURST, 7) |
> + FIELD_PREP(MESON_SDHC_PDMA_TXFIFO_TH, 49) |
> + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15) |
> + FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_TH, 7));
> +
> + /* some initialization bits depend on the SoC: */
> + host->platform->init_hw(mmc);
> +
> + /* disable and mask all interrupts: */
> + regmap_write(host->regmap, MESON_SDHC_ICTL, 0);
> + regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
> +}
> +
> +static int meson_mx_sdhc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct meson_mx_sdhc_host *host;
> + struct mmc_host *mmc;
> + void __iomem *base;
> + int ret, irq;
> +
> + mmc = mmc_alloc_host(sizeof(*host), dev);
> + if (!mmc)
> + return -ENOMEM;
> +
> + ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
> + mmc);
> + if (ret) {
> + dev_err(dev, "Failed to register mmc_free_host action\n");
> + return ret;
> + }
> +
> + host = mmc_priv(mmc);
> + host->mmc = mmc;
> +
> + platform_set_drvdata(pdev, host);
> +
> + host->platform = device_get_match_data(dev);
> + if (!host->platform)
> + return -EINVAL;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + host->regmap = devm_regmap_init_mmio(dev, base,
> + &meson_mx_sdhc_regmap_config);
> + if (IS_ERR(host->regmap))
> + return PTR_ERR(host->regmap);
> +
> + host->pclk = devm_clk_get(dev, "pclk");
> + if (IS_ERR(host->pclk))
> + return PTR_ERR(host->pclk);
> +
> + /* accessing any register requires the module clock to be enabled: */
> + ret = clk_prepare_enable(host->pclk);
> + if (ret) {
> + dev_err(dev, "Failed to enable 'pclk' clock\n");
> + return ret;
> + }
> +
> + meson_mx_sdhc_init_hw(mmc);
> +
> + ret = meson_mx_sdhc_register_clkc(dev, base, host->bulk_clks);
> + if (ret)
> + goto err_disable_pclk;
> +
> + host->sd_clk = host->bulk_clks[1].clk;
> +
> + /* Get regulators and the supported OCR mask */
> + ret = mmc_regulator_get_supply(mmc);
> + if (ret)
> + goto err_disable_pclk;
> +
> + mmc->max_req_size = SZ_128K;
> + mmc->max_seg_size = mmc->max_req_size;
> + mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
> + mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
> + mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
> + mmc->f_min = clk_round_rate(host->sd_clk, 1);
> + mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
> + mmc->max_current_180 = 300;
> + mmc->max_current_330 = 300;
> + mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_HW_RESET;
> + mmc->ops = &meson_mx_sdhc_ops;
> +
> + ret = mmc_of_parse(mmc);
> + if (ret)
> + goto err_disable_pclk;
> +
> + irq = platform_get_irq(pdev, 0);
> + ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
> + meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
> + NULL, host);
> + if (ret)
> + goto err_disable_pclk;
> +
> + ret = mmc_add_host(mmc);
> + if (ret)
> + goto err_disable_pclk;
> +
> + return 0;
> +
> +err_disable_pclk:
> + clk_disable_unprepare(host->pclk);
> + return ret;
> +}
> +
> +static int meson_mx_sdhc_remove(struct platform_device *pdev)
> +{
> + struct meson_mx_sdhc_host *host = platform_get_drvdata(pdev);
> +
> + mmc_remove_host(host->mmc);
> +
> + meson_mx_sdhc_disable_clks(host->mmc);
> +
> + clk_disable_unprepare(host->pclk);
> +
> + return 0;
> +}
> +
> +static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8 = {
> + .init_hw = meson_mx_sdhc_init_hw_meson8,
> + .set_pdma = meson_mx_sdhc_set_pdma_meson8,
> + .wait_before_send = meson_mx_sdhc_wait_before_send_meson8,
> + .hardware_flush_all_cmds = false,
> +};
> +
> +static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8m2 = {
> + .init_hw = meson_mx_sdhc_init_hw_meson8m2,
> + .set_pdma = meson_mx_sdhc_set_pdma_meson8m2,
> + .hardware_flush_all_cmds = true,
> +};
> +
> +static const struct of_device_id meson_mx_sdhc_of_match[] = {
> + {
> + .compatible = "amlogic,meson8-sdhc",
> + .data = &meson_mx_sdhc_data_meson8
> + },
> + {
> + .compatible = "amlogic,meson8b-sdhc",
> + .data = &meson_mx_sdhc_data_meson8
> + },
> + {
> + .compatible = "amlogic,meson8m2-sdhc",
> + .data = &meson_mx_sdhc_data_meson8m2
> + },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, meson_mx_sdhc_of_match);
> +
> +static struct platform_driver meson_mx_sdhc_driver = {
> + .probe = meson_mx_sdhc_probe,
> + .remove = meson_mx_sdhc_remove,
> + .driver = {
> + .name = "meson-mx-sdhc",
> + .of_match_table = of_match_ptr(meson_mx_sdhc_of_match),
> + },
> +};
> +
> +module_platform_driver(meson_mx_sdhc_driver);
> +
> +MODULE_DESCRIPTION("Meson6, Meson8, Meson8b and Meson8m2 SDHC Host Driver");
> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mmc/host/meson-mx-sdhc.h b/drivers/mmc/host/meson-mx-sdhc.h
> new file mode 100644
> index 000000000000..230e8fbe6b3f
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc.h
> @@ -0,0 +1,141 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + */
> +
> +#ifndef _MESON_MX_SDHC_H_
> +#define _MESON_MX_SDHC_H_
> +
> +#include <linux/bitfield.h>
> +
> +#define MESON_SDHC_ARGU 0x00
> +
> +#define MESON_SDHC_SEND 0x04
> + #define MESON_SDHC_SEND_CMD_INDEX GENMASK(5, 0)
> + #define MESON_SDHC_SEND_CMD_HAS_RESP BIT(6)
> + #define MESON_SDHC_SEND_CMD_HAS_DATA BIT(7)
> + #define MESON_SDHC_SEND_RESP_LEN BIT(8)
> + #define MESON_SDHC_SEND_RESP_NO_CRC BIT(9)
> + #define MESON_SDHC_SEND_DATA_DIR BIT(10)
> + #define MESON_SDHC_SEND_DATA_STOP BIT(11)
> + #define MESON_SDHC_SEND_R1B BIT(12)
> + #define MESON_SDHC_SEND_TOTAL_PACK GENMASK(31, 16)
> +
> +#define MESON_SDHC_CTRL 0x08
> + #define MESON_SDHC_CTRL_DAT_TYPE GENMASK(1, 0)
> + #define MESON_SDHC_CTRL_DDR_MODE BIT(2)
> + #define MESON_SDHC_CTRL_TX_CRC_NOCHECK BIT(3)
> + #define MESON_SDHC_CTRL_PACK_LEN GENMASK(12, 4)
> + #define MESON_SDHC_CTRL_RX_TIMEOUT GENMASK(19, 13)
> + #define MESON_SDHC_CTRL_RX_PERIOD GENMASK(23, 20)
> + #define MESON_SDHC_CTRL_RX_ENDIAN GENMASK(26, 24)
> + #define MESON_SDHC_CTRL_SDIO_IRQ_MODE BIT(27)
> + #define MESON_SDHC_CTRL_DAT0_IRQ_SEL BIT(28)
> + #define MESON_SDHC_CTRL_TX_ENDIAN GENMASK(31, 29)
> +
> +#define MESON_SDHC_STAT 0x0c
> + #define MESON_SDHC_STAT_CMD_BUSY BIT(0)
> + #define MESON_SDHC_STAT_DAT3_0 GENMASK(4, 1)
> + #define MESON_SDHC_STAT_CMD BIT(5)
> + #define MESON_SDHC_STAT_RXFIFO_CNT GENMASK(12, 6)
> + #define MESON_SDHC_STAT_TXFIFO_CNT GENMASK(19, 13)
> + #define MESON_SDHC_STAT_DAT7_4 GENMASK(23, 20)
> +
> +#define MESON_SDHC_CLKC 0x10
> + #define MESON_SDHC_CLKC_CLK_DIV GENMASK(11, 0)
> + #define MESON_SDHC_CLKC_CLK_JIC BIT(24)
> + #define MESON_SDHC_CLKC_MEM_PWR_OFF GENMASK(26, 25)
> +
> +#define MESON_SDHC_ADDR 0x14
> +
> +#define MESON_SDHC_PDMA 0x18
> + #define MESON_SDHC_PDMA_DMA_MODE BIT(0)
> + #define MESON_SDHC_PDMA_PIO_RDRESP GENMASK(3, 1)
> + #define MESON_SDHC_PDMA_DMA_URGENT BIT(4)
> + #define MESON_SDHC_PDMA_WR_BURST GENMASK(9, 5)
> + #define MESON_SDHC_PDMA_RD_BURST GENMASK(14, 10)
> + #define MESON_SDHC_PDMA_RXFIFO_TH GENMASK(21, 15)
> + #define MESON_SDHC_PDMA_TXFIFO_TH GENMASK(28, 22)
> + #define MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH GENMASK(30, 29)
> + #define MESON_SDHC_PDMA_TXFIFO_FILL BIT(31)
> +
> +#define MESON_SDHC_MISC 0x1c
> + #define MESON_SDHC_MISC_WCRC_ERR_PATT GENMASK(6, 4)
> + #define MESON_SDHC_MISC_WCRC_OK_PATT GENMASK(9, 7)
> + #define MESON_SDHC_MISC_BURST_NUM GENMASK(21, 16)
> + #define MESON_SDHC_MISC_THREAD_ID GENMASK(27, 22)
> + #define MESON_SDHC_MISC_MANUAL_STOP BIT(28)
> + #define MESON_SDHC_MISC_TXSTART_THRES GENMASK(31, 29)
> +
> +#define MESON_SDHC_DATA 0x20
> +
> +#define MESON_SDHC_ICTL 0x24
> + #define MESON_SDHC_ICTL_RESP_OK BIT(0)
> + #define MESON_SDHC_ICTL_RESP_TIMEOUT BIT(1)
> + #define MESON_SDHC_ICTL_RESP_ERR_CRC BIT(2)
> + #define MESON_SDHC_ICTL_RESP_OK_NOCLEAR BIT(3)
> + #define MESON_SDHC_ICTL_DATA_1PACK_OK BIT(4)
> + #define MESON_SDHC_ICTL_DATA_TIMEOUT BIT(5)
> + #define MESON_SDHC_ICTL_DATA_ERR_CRC BIT(6)
> + #define MESON_SDHC_ICTL_DATA_XFER_OK BIT(7)
> + #define MESON_SDHC_ICTL_RX_HIGHER BIT(8)
> + #define MESON_SDHC_ICTL_RX_LOWER BIT(9)
> + #define MESON_SDHC_ICTL_DAT1_IRQ BIT(10)
> + #define MESON_SDHC_ICTL_DMA_DONE BIT(11)
> + #define MESON_SDHC_ICTL_RXFIFO_FULL BIT(12)
> + #define MESON_SDHC_ICTL_TXFIFO_EMPTY BIT(13)
> + #define MESON_SDHC_ICTL_ADDI_DAT1_IRQ BIT(14)
> + #define MESON_SDHC_ICTL_ALL_IRQS GENMASK(14, 0)
> + #define MESON_SDHC_ICTL_DAT1_IRQ_DELAY GENMASK(17, 16)
> +
> +#define MESON_SDHC_ISTA 0x28
> + #define MESON_SDHC_ISTA_RESP_OK BIT(0)
> + #define MESON_SDHC_ISTA_RESP_TIMEOUT BIT(1)
> + #define MESON_SDHC_ISTA_RESP_ERR_CRC BIT(2)
> + #define MESON_SDHC_ISTA_RESP_OK_NOCLEAR BIT(3)
> + #define MESON_SDHC_ISTA_DATA_1PACK_OK BIT(4)
> + #define MESON_SDHC_ISTA_DATA_TIMEOUT BIT(5)
> + #define MESON_SDHC_ISTA_DATA_ERR_CRC BIT(6)
> + #define MESON_SDHC_ISTA_DATA_XFER_OK BIT(7)
> + #define MESON_SDHC_ISTA_RX_HIGHER BIT(8)
> + #define MESON_SDHC_ISTA_RX_LOWER BIT(9)
> + #define MESON_SDHC_ISTA_DAT1_IRQ BIT(10)
> + #define MESON_SDHC_ISTA_DMA_DONE BIT(11)
> + #define MESON_SDHC_ISTA_RXFIFO_FULL BIT(12)
> + #define MESON_SDHC_ISTA_TXFIFO_EMPTY BIT(13)
> + #define MESON_SDHC_ISTA_ADDI_DAT1_IRQ BIT(14)
> + #define MESON_SDHC_ISTA_ALL_IRQS GENMASK(14, 0)
> +
> +#define MESON_SDHC_SRST 0x2c
> + #define MESON_SDHC_SRST_MAIN_CTRL BIT(0)
> + #define MESON_SDHC_SRST_RXFIFO BIT(1)
> + #define MESON_SDHC_SRST_TXFIFO BIT(2)
> + #define MESON_SDHC_SRST_DPHY_RX BIT(3)
> + #define MESON_SDHC_SRST_DPHY_TX BIT(4)
> + #define MESON_SDHC_SRST_DMA_IF BIT(5)
> +
> +#define MESON_SDHC_ESTA 0x30
> + #define MESON_SDHC_ESTA_11_13 GENMASK(13, 11)
> +
> +#define MESON_SDHC_ENHC 0x34
> + #define MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE BIT(0)
> + #define MESON_SDHC_ENHC_MESON8M2_CHK_WRRSP BIT(1)
> + #define MESON_SDHC_ENHC_MESON8M2_CHK_DMA BIT(2)
> + #define MESON_SDHC_ENHC_MESON8M2_DEBUG GENMASK(5, 3)
> + #define MESON_SDHC_ENHC_MESON6_RX_TIMEOUT GENMASK(7, 0)
> + #define MESON_SDHC_ENHC_MESON6_DMA_RD_RESP BIT(16)
> + #define MESON_SDHC_ENHC_MESON6_DMA_WR_RESP BIT(17)
> + #define MESON_SDHC_ENHC_SDIO_IRQ_PERIOD GENMASK(15, 8)
> + #define MESON_SDHC_ENHC_RXFIFO_TH GENMASK(24, 18)
> + #define MESON_SDHC_ENHC_TXFIFO_TH GENMASK(31, 25)
> +
> +#define MESON_SDHC_CLK2 0x38
> + #define MESON_SDHC_CLK2_RX_CLK_PHASE GENMASK(11, 0)
> + #define MESON_SDHC_CLK2_SD_CLK_PHASE GENMASK(23, 12)
> +
> +struct clk_bulk_data;
> +
> +int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
> + struct clk_bulk_data *clk_bulk_data);
> +
> +#endif /* _MESON_MX_SDHC_H_ */
> --
> 2.26.2
>
^ permalink raw reply
* Re: [PATCH v3 1/6] irqchip: Add Loongson HyperTransport Vector support
From: Thomas Gleixner @ 2020-05-13 12:06 UTC (permalink / raw)
To: Jiaxun Yang, maz
Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen, linux-kernel,
devicetree, linux-mips
In-Reply-To: <20200501092139.2988670-1-jiaxun.yang@flygoat.com>
Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
> +static void htvec_mask_irq(struct irq_data *d)
> +{
> + struct htvec *priv = irq_data_get_irq_chip_data(d);
> + void __iomem *addr = priv->base + HTVEC_EN_OFF;
> + unsigned long flags;
> + u32 reg;
> +
> + raw_spin_lock_irqsave(&priv->htvec_lock, flags);
No need for irqsave() these functions are called with interrupts disabled.
> + addr += VEC_REG_IDX(d->hwirq) * 4;
> + reg = readl(addr);
> + reg &= ~BIT(VEC_REG_BIT(d->hwirq));
> + writel(reg, addr);
> + raw_spin_unlock_irqrestore(&priv->htvec_lock, flags);
> +}
> +static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
> + unsigned int nr_irqs, void *arg)
> +{
> + struct htvec *priv = domain->host_data;
> + unsigned long hwirq;
> + unsigned int type, i;
> +
> + irq_domain_translate_onecell(domain, arg, &hwirq, &type);
> +
> + for (i = 0; i < nr_irqs; i++)
> + irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
> + priv, handle_edge_irq, NULL, NULL);
This wants curly brackets and the second line of arguments wants to be
aligned with the first argument:
for (i = 0; i < nr_irqs; i++) {
irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
priv, handle_edge_irq, NULL, NULL);
}
See https://lore.kernel.org/lkml/alpine.DEB.2.20.1701171956290.3645@nanos/
The alignment of arguments wants to be fixed all over the place.
> +static int htvec_of_init(struct device_node *node,
> + struct device_node *parent)
> +{
> + struct htvec *priv;
> + int err, parent_irq[4], num_parents = 0, i;
Please order the variable declaration in reverse fir tree length order:
int err, parent_irq[4], num_parents = 0, i;
struct htvec *priv;
That's way better readable than the above. All over the place please.
> + priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
> + VEC_COUNT,
> + &htvec_domain_ops,
> + priv);
> + if (!priv->htvec_domain) {
> + pr_err("Failed to create IRQ domain\n");
> + err = -ENOMEM;
> + goto iounmap_base;
> + }
> +
> + htvec_reset(priv);
> +
> + for (i = 0; i < num_parents; i++)
> + irq_set_chained_handler_and_data(parent_irq[i],
> + htvec_irq_dispatch, priv);
See above.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v2 3/6] irqchip: Add Loongson PCH PIC controller
From: Thomas Gleixner @ 2020-05-13 12:09 UTC (permalink / raw)
To: Jiaxun Yang, maz
Cc: Jiaxun Yang, Jason Cooper, Rob Herring, Huacai Chen, linux-kernel,
devicetree, linux-mips
In-Reply-To: <20200428063247.2223499-3-jiaxun.yang@flygoat.com>
Jiaxun Yang <jiaxun.yang@flygoat.com> writes:
> +static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
> +{
> + void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
> + unsigned long flags;
> + u32 reg;
> +
> + raw_spin_lock_irqsave(&priv->pic_lock, flags);
See other reply.
> + reg = readl(addr);
> + reg |= BIT(PIC_REG_BIT(bit));
> + writel(reg, addr);
> + raw_spin_unlock_irqrestore(&priv->pic_lock, flags);
> +}
> +static int pch_pic_of_init(struct device_node *node,
> + struct device_node *parent)
> +{
> + struct pch_pic *priv;
> + struct irq_domain *parent_domain;
> + int err;
ordering
Thanks,
tglx
^ 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