All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/33] riscv: Add Sipeed Maix support
@ 2020-02-28 21:05 Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 01/33] clk: Always use the supplied struct clk Sean Anderson
                   ` (32 more replies)
  0 siblings, 33 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This patch series adds support for Sipeed Maix boards and the Kendryte
K210 CPU. Currently, only the Maix Bit V2.0 is supported, however other
models are similar. This series depends on
<https://patchwork.ozlabs.org/patch/1215327/>
(clk: Include missing headers for linux/clk-provider.h).

This will (hopefully) be the last version of this series adding any new
functionality, other than that needed to fix the bugs below.

Known Bugs/Limitations:
- Accessing the AI ram hangs, limiting available ram to 6M
- The MMC cannot be accessed with the dw_spi driver
- Trying to boot an image with bootm fails with
  ERROR: Failed to allocate 0x7d60 bytes below 0x80000000.

To flash u-boot to a maix bit, run
kflash -tp /dev/<your tty here> -B bit_mic u-boot-dtb.bin

Boot output should look like the following:

U-Boot 2020.04-rc2-00087-g2221cc09c1-dirty (Feb 28 2020 - 13:53:09 -0500)

DRAM:  8 MiB
WDT:   Started with servicing (60s timeout)
MMC:   spi at 53000000:slot at 0: 0
In:    serial at 38000000
Out:   serial at 38000000
Err:   serial at 38000000
Hit any key to stop autoboot:  0
SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
Reading 5242880 byte(s) at offset 0x00000000
## Starting application at 0x80000000 ...

Changes for v5:
- Rebase onto master
- Add pinconf support
- Add gpio support
- Store environment in spi flash
- Group patches by prefix
- Add additional documentation
- Add SMP support
- Add WDT support

Changes for v4:
- Linted several patches
- Updated the copyright year for several files
- Added tests for syscon-reset, simple-pm-bus, and the pll calc_rate function
- Added/updated documentation
- Fixed SPI for the nor flash
- Fixed PLLs not enabling/setting rate properly
- RISCV_PRIV_1_9_1 now (un)defines all diferring CSRs, and also disables VM
- More devicetree changes

Changes for v3:
- Remove patch to set RV64I as default
- Remove patch for a separate sysctl driver
- Split off cpu frequency patch into its own series
- Reorder support/devicetree patches to come last
- Add patch for reset driver
- Add simple-pm-bus for busses with their own clocks
- Add additional documentation
- Reword mcounteren patch to refer to the RISC-V priv spec 1.9.1
- Many devicetree changes
- Switch to "make savedefconfig" to generate the config

Changes for v2:
- Many bugfixes for the device tree
- Modify the config to build without errors
- Add support for keeping internal PLL frequencies in-range
- Fix several rebase-induced artifacts

Sean Anderson (33):
  clk: Always use the supplied struct clk
  clk: Check that ops of composite clock components exist before calling
  clk: Unconditionally recursively en-/dis-able clocks
  clk: Add functions to register CCF clock structs
  clk: Add K210 pll support
  clk: Add a bypass clock for K210
  clk: Add K210 clock support
  doc: Fix typo in FIT documentation
  dm: Add support for simple-pm-bus
  dm: Fix error handling for dev_read_addr_ptr
  reset: Add generic reset driver
  lib: Always set errno in hcreate_r
  pinctrl: Add support for Kendryte K210 FPIOA
  gpio: sifive: Use generic reg read function
  gpio: dw: Fix warnings about casting int to pointer
  gpio: dw: Add a trailing underscore to generated name
  gpio: dw: Return output value when direction is out
  led: gpio: Default to using node name if label is absent
  spi: dw: Add device tree properties for fields in CTRL1
  spi: dw: Rename "cs-gpio" to "cs-gpios"
  spi: dw: Use generic function to read reg address
  spi: dw: Speed up transfer loops
  spi: dw: Properly set rx_end when not recieving
  spi: dw: Add mem_ops
  wdt: Move asm/utils.h to log2.h
  riscv: Add headers for asm/global_data.h
  riscv: Fix race conditions when initializing IPI
  riscv: Add option to support RISC-V privileged spec 1.9
  riscv: Allow use of reset drivers
  riscv: Try to get cpu frequency from a "clocks" node if it exists
  riscv: Enable cpu clock if it is present
  riscv: Add device tree for K210 and Sipeed Maix BitM
  riscv: Add Sipeed Maix support

 MAINTAINERS                                   |   9 +
 arch/arc/dts/axs10x_mb.dtsi                   |   3 +-
 arch/arc/dts/hsdk.dts                         |   3 +-
 arch/arm/cpu/armv7/cache_v7.c                 |   2 +-
 arch/arm/mach-davinci/spl.c                   |   2 +-
 arch/arm/mach-omap2/clocks-common.c           |   2 +-
 arch/arm/mach-omap2/emif-common.c             |   2 +-
 arch/arm/mach-omap2/omap4/emif.c              |   2 +-
 arch/arm/mach-omap2/omap5/dra7xx_iodelay.c    |   2 +-
 arch/arm/mach-omap2/omap5/emif.c              |   2 +-
 arch/arm/mach-omap2/omap5/hwinit.c            |   2 +-
 arch/arm/mach-socfpga/spl_a10.c               |   2 +-
 arch/arm/mach-socfpga/spl_agilex.c            |   2 +-
 arch/arm/mach-socfpga/spl_gen5.c              |   2 +-
 arch/arm/mach-socfpga/spl_s10.c               |   2 +-
 arch/riscv/Kconfig                            |  14 +
 arch/riscv/cpu/cpu.c                          |  18 +
 arch/riscv/dts/Makefile                       |   1 +
 arch/riscv/dts/k210-maix-bit.dts              | 333 +++++++++
 arch/riscv/dts/k210.dtsi                      | 649 +++++++++++++++++
 arch/riscv/include/asm/csr.h                  |  40 ++
 arch/riscv/include/asm/global_data.h          |   3 +
 arch/riscv/include/asm/smp.h                  |  43 ++
 arch/riscv/lib/andes_plic.c                   |  34 +-
 arch/riscv/lib/reset.c                        |   2 +
 arch/riscv/lib/sbi_ipi.c                      |   5 +
 arch/riscv/lib/sifive_clint.c                 |  33 +-
 arch/riscv/lib/smp.c                          |  68 +-
 arch/sandbox/dts/test.dts                     |  21 +
 arch/sandbox/include/asm/clk.h                |   1 +
 board/sipeed/maix/Kconfig                     |  72 ++
 board/sipeed/maix/MAINTAINERS                 |  11 +
 board/sipeed/maix/Makefile                    |   5 +
 board/sipeed/maix/maix.c                      |  54 ++
 configs/sandbox_defconfig                     |   2 +
 configs/sipeed_maix_bitm_defconfig            |  16 +
 doc/board/index.rst                           |   1 +
 doc/board/sipeed/index.rst                    |   9 +
 doc/board/sipeed/maix.rst                     | 223 ++++++
 .../bus/simple-pm-bus.txt                     |  44 ++
 .../mfd/kendryte,k210-sysctl.txt              |  33 +
 .../pinctrl/kendryte,k210-fpioa.txt           | 116 +++
 .../reset/syscon-reset.txt                    |  36 +
 .../spi/snps,dw-apb-ssi.txt                   |  43 ++
 doc/imx/clk/ccf.txt                           |  63 +-
 doc/uImage.FIT/source_file_format.txt         |   2 +-
 drivers/clk/Kconfig                           |   1 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/clk-composite.c                   | 145 ++--
 drivers/clk/clk-divider.c                     |  62 +-
 drivers/clk/clk-fixed-factor.c                |   3 +-
 drivers/clk/clk-gate.c                        |  44 +-
 drivers/clk/clk-mux.c                         |  12 +-
 drivers/clk/clk-uclass.c                      |  60 +-
 drivers/clk/imx/clk-gate2.c                   |   4 +-
 drivers/clk/imx/clk-imx8mp.c                  |   2 +-
 drivers/clk/kendryte/Kconfig                  |  12 +
 drivers/clk/kendryte/Makefile                 |   1 +
 drivers/clk/kendryte/bypass.c                 | 270 +++++++
 drivers/clk/kendryte/clk.c                    | 478 +++++++++++++
 drivers/clk/kendryte/pll.c                    | 600 ++++++++++++++++
 drivers/core/Kconfig                          |   7 +
 drivers/core/Makefile                         |   1 +
 drivers/core/read.c                           |   2 +-
 drivers/core/simple-pm-bus.c                  |  56 ++
 drivers/cpu/riscv_cpu.c                       |  39 +-
 drivers/gpio/dwapb_gpio.c                     |  33 +-
 drivers/gpio/sifive-gpio.c                    |   2 +-
 drivers/led/led_gpio.c                        |   7 +-
 drivers/pinctrl/Kconfig                       |   1 +
 drivers/pinctrl/Makefile                      |   1 +
 drivers/pinctrl/broadcom/pinctrl-bcm283x.c    |   2 +-
 drivers/pinctrl/kendryte/Kconfig              |   7 +
 drivers/pinctrl/kendryte/Makefile             |   1 +
 drivers/pinctrl/kendryte/pinctrl.c            | 663 ++++++++++++++++++
 drivers/pinctrl/kendryte/pinctrl.h            | 325 +++++++++
 drivers/pinctrl/mediatek/pinctrl-mtk-common.c |   2 +-
 drivers/reset/Kconfig                         |   5 +
 drivers/reset/Makefile                        |   1 +
 drivers/reset/reset-syscon.c                  |  80 +++
 drivers/spi/designware_spi.c                  | 212 ++++--
 drivers/watchdog/designware_wdt.c             |   3 +-
 include/configs/sipeed-maix.h                 |  24 +
 include/dm/read.h                             |   4 +-
 include/dt-bindings/clock/k210-sysctl.h       |  56 ++
 include/dt-bindings/mfd/k210-sysctl.h         |  38 +
 include/dt-bindings/pinctrl/k210-pinctrl.h    |  12 +
 include/dt-bindings/reset/k210-sysctl.h       |  38 +
 include/kendryte/bypass.h                     |  31 +
 include/kendryte/clk.h                        |  35 +
 include/kendryte/pll.h                        |  57 ++
 include/linux/clk-provider.h                  |   9 +
 .../arm/include/asm/utils.h => include/log2.h |   4 +-
 include/test/export.h                         |  16 +
 lib/hashtable.c                               |   8 +-
 test/dm/Makefile                              |   3 +
 test/dm/k210_pll.c                            |  95 +++
 test/dm/simple-pm-bus.c                       |  45 ++
 test/dm/syscon-reset.c                        |  58 ++
 99 files changed, 5344 insertions(+), 368 deletions(-)
 create mode 100644 arch/riscv/dts/k210-maix-bit.dts
 create mode 100644 arch/riscv/dts/k210.dtsi
 create mode 100644 board/sipeed/maix/Kconfig
 create mode 100644 board/sipeed/maix/MAINTAINERS
 create mode 100644 board/sipeed/maix/Makefile
 create mode 100644 board/sipeed/maix/maix.c
 create mode 100644 configs/sipeed_maix_bitm_defconfig
 create mode 100644 doc/board/sipeed/index.rst
 create mode 100644 doc/board/sipeed/maix.rst
 create mode 100644 doc/device-tree-bindings/bus/simple-pm-bus.txt
 create mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
 create mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
 create mode 100644 doc/device-tree-bindings/reset/syscon-reset.txt
 create mode 100644 doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
 create mode 100644 drivers/clk/kendryte/Kconfig
 create mode 100644 drivers/clk/kendryte/Makefile
 create mode 100644 drivers/clk/kendryte/bypass.c
 create mode 100644 drivers/clk/kendryte/clk.c
 create mode 100644 drivers/clk/kendryte/pll.c
 create mode 100644 drivers/core/simple-pm-bus.c
 create mode 100644 drivers/pinctrl/kendryte/Kconfig
 create mode 100644 drivers/pinctrl/kendryte/Makefile
 create mode 100644 drivers/pinctrl/kendryte/pinctrl.c
 create mode 100644 drivers/pinctrl/kendryte/pinctrl.h
 create mode 100644 drivers/reset/reset-syscon.c
 create mode 100644 include/configs/sipeed-maix.h
 create mode 100644 include/dt-bindings/clock/k210-sysctl.h
 create mode 100644 include/dt-bindings/mfd/k210-sysctl.h
 create mode 100644 include/dt-bindings/pinctrl/k210-pinctrl.h
 create mode 100644 include/dt-bindings/reset/k210-sysctl.h
 create mode 100644 include/kendryte/bypass.h
 create mode 100644 include/kendryte/clk.h
 create mode 100644 include/kendryte/pll.h
 rename arch/arm/include/asm/utils.h => include/log2.h (93%)
 create mode 100644 include/test/export.h
 create mode 100644 test/dm/k210_pll.c
 create mode 100644 test/dm/simple-pm-bus.c
 create mode 100644 test/dm/syscon-reset.c

-- 
2.25.0

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 01/33] clk: Always use the supplied struct clk
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 02/33] clk: Check that ops of composite clock components exist before calling Sean Anderson
                   ` (31 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

CCF clocks should always use the struct clock passed to their methods for
extracting the driver-specific clock information struct. Previously, many
functions would use the clk->dev->priv if the device was bound. This could
cause problems with composite clocks. The individual clocks in a composite
clock did not have the ->dev field filled in. This was fine, because the
device-specific clock information would be used. However, since there was
no ->dev, there was no way to get the parent clock. This caused the
recalc_rate method of the CCF divider clock to fail. One option would be to
use the clk->priv field to get the composite clock and from there get the
appropriate parent device. However, this would tie the implementation to
the composite clock. In general, different devices should not rely on the
contents of ->priv from another device.

The simple solution to this problem is to just always use the supplied
struct clock. The composite clock now fills in the ->dev pointer of its
child clocks.  This allows child clocks to make calls like clk_get_parent()
without issue.

imx avoided the above problem by using a custom get_rate function with
composite clocks.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Acked-by: Lukasz Majewski <lukma@denx.de>
---

Changes in v4:
- Lint

Changes in v3:
- Documented new assumptions in the CCF
- Wrapped docs to 80 columns

 doc/imx/clk/ccf.txt            | 63 +++++++++++++++++-----------------
 drivers/clk/clk-composite.c    |  7 ++++
 drivers/clk/clk-divider.c      |  6 ++--
 drivers/clk/clk-fixed-factor.c |  3 +-
 drivers/clk/clk-gate.c         |  6 ++--
 drivers/clk/clk-mux.c          | 12 +++----
 drivers/clk/imx/clk-gate2.c    |  4 +--
 7 files changed, 50 insertions(+), 51 deletions(-)

diff --git a/doc/imx/clk/ccf.txt b/doc/imx/clk/ccf.txt
index 36b60dc438..e40ac360e8 100644
--- a/doc/imx/clk/ccf.txt
+++ b/doc/imx/clk/ccf.txt
@@ -1,42 +1,37 @@
 Introduction:
 =============
 
-This documentation entry describes the Common Clock Framework [CCF]
-port from Linux kernel (v5.1.12) to U-Boot.
+This documentation entry describes the Common Clock Framework [CCF] port from
+Linux kernel (v5.1.12) to U-Boot.
 
-This code is supposed to bring CCF to IMX based devices (imx6q, imx7
-imx8). Moreover, it also provides some common clock code, which would
-allow easy porting of CCF Linux code to other platforms.
+This code is supposed to bring CCF to IMX based devices (imx6q, imx7 imx8).
+Moreover, it also provides some common clock code, which would allow easy
+porting of CCF Linux code to other platforms.
 
 Design decisions:
 =================
 
-* U-Boot's driver model [DM] for clk differs from Linux CCF. The most
-  notably difference is the lack of support for hierarchical clocks and
-  "clock as a manager driver" (single clock DTS node acts as a starting
-  point for all other clocks).
+* U-Boot's driver model [DM] for clk differs from Linux CCF. The most notably
+  difference is the lack of support for hierarchical clocks and "clock as a
+  manager driver" (single clock DTS node acts as a starting point for all other
+  clocks).
 
-* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE
-  is not set (no need for recursive access).
+* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE is
+  not set (no need for recursive access).
 
-* On purpose the "manager" clk driver (clk-imx6q.c) is not using large
-  table to store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = ....
-  Instead we use udevice's linked list for the same class (UCLASS_CLK).
+* On purpose the "manager" clk driver (clk-imx6q.c) is not using large table to
+  store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = .... Instead we
+  use udevice's linked list for the same class (UCLASS_CLK).
 
   Rationale:
   ----------
-    When porting the code as is from Linux, one would need ~1KiB of RAM to
-    store it. This is way too much if we do plan to use this driver in SPL.
+    When porting the code as is from Linux, one would need ~1KiB of RAM to store
+    it. This is way too much if we do plan to use this driver in SPL.
 
 * The "central" structure of this patch series is struct udevice and its
   uclass_priv field contains the struct clk pointer (to the originally created
   one).
 
-* Up till now U-Boot's driver model (DM) CLK operates on udevice (main
-  access to clock is by udevice ops)
-  In the CCF the access to struct clk (embodying pointer to *dev) is
-  possible via dev_get_clk_ptr() (it is a wrapper on dev_get_uclass_priv()).
-
 * To keep things simple the struct udevice's uclass_priv pointer is used to
   store back pointer to corresponding struct clk. However, it is possible to
   modify clk-uclass.c file and add there struct uc_clk_priv, which would have
@@ -45,13 +40,17 @@ Design decisions:
   setting .per_device_auto_alloc_size = sizeof(struct uc_clk_priv)) the
   uclass_priv stores the pointer to struct clk.
 
+* Non-CCF clocks do not have a pointer to a clock in clk->dev->priv. In the case
+  of composite clocks, clk->dev->priv may not match clk. Drivers should always
+  use the struct clk which is passed to them, and not clk->dev->priv.
+
 * It is advised to add common clock code (like already added rate and flags) to
   the struct clk, which is a top level description of the clock.
 
 * U-Boot's driver model already provides the facility to automatically allocate
-  (via private_alloc_size) device private data (accessible via dev->priv).
-  It may look appealing to use this feature to allocate private structures for
-  CCF clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
+  (via private_alloc_size) device private data (accessible via dev->priv). It
+  may look appealing to use this feature to allocate private structures for CCF
+  clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
 
   The above feature had not been used for following reasons:
   - The original CCF Linux kernel driver is the "manager" for clocks - it
@@ -64,21 +63,23 @@ Design decisions:
 
 * I've added the clk_get_parent(), which reads parent's dev->uclass_priv to
   provide parent's struct clk pointer. This seems the easiest way to get
-  child/parent relationship for struct clk in U-Boot's udevice based clocks.
+  child/parent relationship for struct clk in U-Boot's udevice based clocks.  In
+  the future arbitrary parents may be supported by adding a get_parent function
+  to clk_ops.
 
 * Linux's CCF 'struct clk_core' corresponds to U-Boot's udevice in 'struct clk'.
   Clock IP block agnostic flags from 'struct clk_core' (e.g. NOCACHE) have been
-  moved from this struct one level up to 'struct clk'.
+  moved from this struct one level up to 'struct clk'. Many flags are
+  unimplemented at the moment.
 
 * For tests the new ./test/dm/clk_ccf.c and ./drivers/clk/clk_sandbox_ccf.c
   files have been introduced. The latter setups the CCF clock structure for
-  sandbox by reusing, if possible, generic clock primitives - like divier
-  and mux. The former file provides code to tests this setup.
+  sandbox by reusing, if possible, generic clock primitives - like divier and
+  mux. The former file provides code to tests this setup.
 
   For sandbox new CONFIG_SANDBOX_CLK_CCF Kconfig define has been introduced.
-  All new primitives added for new architectures must have corresponding test
-  in the two aforementioned files.
-
+  All new primitives added for new architectures must have corresponding test in
+  the two aforementioned files.
 
 Testing (sandbox):
 ==================
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 414185031e..2ff1d6b47f 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -147,6 +147,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 		goto err;
 	}
 
+	if (composite->mux)
+		composite->mux->dev = clk->dev;
+	if (composite->rate)
+		composite->rate->dev = clk->dev;
+	if (composite->gate)
+		composite->gate->dev = clk->dev;
+
 	return clk;
 
 err:
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index d79ae367b8..5fe1c3941f 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -72,8 +72,7 @@ unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
 
 static ulong clk_divider_recalc_rate(struct clk *clk)
 {
-	struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_divider *divider = to_clk_divider(clk);
 	unsigned long parent_rate = clk_get_parent_rate(clk);
 	unsigned int val;
 
@@ -152,8 +151,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
 
 static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate)
 {
-	struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_divider *divider = to_clk_divider(clk);
 	unsigned long parent_rate = clk_get_parent_rate(clk);
 	int value;
 	u32 val;
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 2ceb6bb171..0eb24b87fc 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -20,8 +20,7 @@
 
 static ulong clk_factor_recalc_rate(struct clk *clk)
 {
-	struct clk_fixed_factor *fix =
-		to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
+	struct clk_fixed_factor *fix = to_clk_fixed_factor(clk);
 	unsigned long parent_rate = clk_get_parent_rate(clk);
 	unsigned long long int rate;
 
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 6415c2f1b9..b5827dced0 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -45,8 +45,7 @@
  */
 static void clk_gate_endisable(struct clk *clk, int enable)
 {
-	struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_gate *gate = to_clk_gate(clk);
 	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
 	u32 reg;
 
@@ -88,8 +87,7 @@ static int clk_gate_disable(struct clk *clk)
 
 int clk_gate_is_enabled(struct clk *clk)
 {
-	struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_gate *gate = to_clk_gate(clk);
 	u32 reg;
 
 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index b9d2ae6778..0502217dc4 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -37,8 +37,7 @@
 int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
 			 unsigned int val)
 {
-	struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_mux *mux = to_clk_mux(clk);
 	int num_parents = mux->num_parents;
 
 	if (table) {
@@ -81,8 +80,7 @@ unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
 
 u8 clk_mux_get_parent(struct clk *clk)
 {
-	struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_mux *mux = to_clk_mux(clk);
 	u32 val;
 
 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
@@ -99,8 +97,7 @@ u8 clk_mux_get_parent(struct clk *clk)
 static int clk_fetch_parent_index(struct clk *clk,
 				  struct clk *parent)
 {
-	struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_mux *mux = to_clk_mux(clk);
 
 	int i;
 
@@ -117,8 +114,7 @@ static int clk_fetch_parent_index(struct clk *clk,
 
 static int clk_mux_set_parent(struct clk *clk, struct clk *parent)
 {
-	struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
-			dev_get_clk_ptr(clk->dev) : clk);
+	struct clk_mux *mux = to_clk_mux(clk);
 	int index;
 	u32 val;
 	u32 reg;
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c
index b38890d5ba..40b2d4caab 100644
--- a/drivers/clk/imx/clk-gate2.c
+++ b/drivers/clk/imx/clk-gate2.c
@@ -39,7 +39,7 @@ struct clk_gate2 {
 
 static int clk_gate2_enable(struct clk *clk)
 {
-	struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+	struct clk_gate2 *gate = to_clk_gate2(clk);
 	u32 reg;
 
 	reg = readl(gate->reg);
@@ -52,7 +52,7 @@ static int clk_gate2_enable(struct clk *clk)
 
 static int clk_gate2_disable(struct clk *clk)
 {
-	struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+	struct clk_gate2 *gate = to_clk_gate2(clk);
 	u32 reg;
 
 	reg = readl(gate->reg);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 02/33] clk: Check that ops of composite clock components exist before calling
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 01/33] clk: Always use the supplied struct clk Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks Sean Anderson
                   ` (30 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

clk_composite_ops was shared between all devices in the composite clock
driver.  If one clock had a feature (such as supporting set_parent) which
another clock did not, it could call a null pointer dereference.

This patch does three things
1. It adds null-pointer checks to all composite clock functions.
2. It makes clk_composite_ops const and sets its functions at compile-time.
3. It adds some basic sanity checks to num_parents.

The combined effect of these changes is that any of mux, rate, or gate can
be NULL, and composite clocks will still function normally. Previously, at
least mux had to exist, since clk_composite_get_parent was used to
determine the parent for clk_register.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Acked-by: Lukasz Majewski <lukma@denx.de>
---

Changes in v4:
- Return ENOTSUPP not ENOSYS with no set_parent

Changes in v3:
- Don't return an error code where a no-op would be fine

 drivers/clk/clk-composite.c | 57 +++++++++++++++++++++++--------------
 1 file changed, 35 insertions(+), 22 deletions(-)

diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 2ff1d6b47f..819bfca2fc 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -24,7 +24,10 @@ static u8 clk_composite_get_parent(struct clk *clk)
 		(struct clk *)dev_get_clk_ptr(clk->dev) : clk);
 	struct clk *mux = composite->mux;
 
-	return clk_mux_get_parent(mux);
+	if (mux)
+		return clk_mux_get_parent(mux);
+	else
+		return 0;
 }
 
 static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
@@ -34,7 +37,10 @@ static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
 	const struct clk_ops *mux_ops = composite->mux_ops;
 	struct clk *mux = composite->mux;
 
-	return mux_ops->set_parent(mux, parent);
+	if (mux && mux_ops)
+		return mux_ops->set_parent(mux, parent);
+	else
+		return -ENOTSUPP;
 }
 
 static unsigned long clk_composite_recalc_rate(struct clk *clk)
@@ -44,7 +50,10 @@ static unsigned long clk_composite_recalc_rate(struct clk *clk)
 	const struct clk_ops *rate_ops = composite->rate_ops;
 	struct clk *rate = composite->rate;
 
-	return rate_ops->get_rate(rate);
+	if (rate && rate_ops)
+		return rate_ops->get_rate(rate);
+	else
+		return clk_get_parent_rate(clk);
 }
 
 static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
@@ -54,7 +63,10 @@ static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
 	const struct clk_ops *rate_ops = composite->rate_ops;
 	struct clk *clk_rate = composite->rate;
 
-	return rate_ops->set_rate(clk_rate, rate);
+	if (rate && rate_ops)
+		return rate_ops->set_rate(clk_rate, rate);
+	else
+		return clk_get_rate(clk);
 }
 
 static int clk_composite_enable(struct clk *clk)
@@ -64,7 +76,10 @@ static int clk_composite_enable(struct clk *clk)
 	const struct clk_ops *gate_ops = composite->gate_ops;
 	struct clk *gate = composite->gate;
 
-	return gate_ops->enable(gate);
+	if (gate && gate_ops)
+		return gate_ops->enable(gate);
+	else
+		return 0;
 }
 
 static int clk_composite_disable(struct clk *clk)
@@ -74,15 +89,12 @@ static int clk_composite_disable(struct clk *clk)
 	const struct clk_ops *gate_ops = composite->gate_ops;
 	struct clk *gate = composite->gate;
 
-	gate_ops->disable(gate);
-
-	return 0;
+	if (gate && gate_ops)
+		return gate_ops->disable(gate);
+	else
+		return 0;
 }
 
-struct clk_ops clk_composite_ops = {
-	/* This will be set according to clk_register_composite */
-};
-
 struct clk *clk_register_composite(struct device *dev, const char *name,
 				   const char * const *parent_names,
 				   int num_parents, struct clk *mux,
@@ -96,7 +108,9 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 	struct clk *clk;
 	struct clk_composite *composite;
 	int ret;
-	struct clk_ops *composite_ops = &clk_composite_ops;
+
+	if (!num_parents || (num_parents != 1 && !mux))
+		return ERR_PTR(-EINVAL);
 
 	composite = kzalloc(sizeof(*composite), GFP_KERNEL);
 	if (!composite)
@@ -105,8 +119,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 	if (mux && mux_ops) {
 		composite->mux = mux;
 		composite->mux_ops = mux_ops;
-		if (mux_ops->set_parent)
-			composite_ops->set_parent = clk_composite_set_parent;
 		mux->data = (ulong)composite;
 	}
 
@@ -115,11 +127,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 			clk = ERR_PTR(-EINVAL);
 			goto err;
 		}
-		composite_ops->get_rate = clk_composite_recalc_rate;
-
-		/* .set_rate requires either .round_rate or .determine_rate */
-		if (rate_ops->set_rate)
-			composite_ops->set_rate = clk_composite_set_rate;
 
 		composite->rate = rate;
 		composite->rate_ops = rate_ops;
@@ -134,8 +141,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 
 		composite->gate = gate;
 		composite->gate_ops = gate_ops;
-		composite_ops->enable = clk_composite_enable;
-		composite_ops->disable = clk_composite_disable;
 		gate->data = (ulong)composite;
 	}
 
@@ -161,6 +166,14 @@ err:
 	return clk;
 }
 
+static const struct clk_ops clk_composite_ops = {
+	.set_parent = clk_composite_set_parent,
+	.get_rate = clk_composite_recalc_rate,
+	.set_rate = clk_composite_set_rate,
+	.enable = clk_composite_enable,
+	.disable = clk_composite_disable,
+};
+
 U_BOOT_DRIVER(clk_composite) = {
 	.name	= UBOOT_DM_CLK_COMPOSITE,
 	.id	= UCLASS_CLK,
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 01/33] clk: Always use the supplied struct clk Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 02/33] clk: Check that ops of composite clock components exist before calling Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:50   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 04/33] clk: Add functions to register CCF clock structs Sean Anderson
                   ` (29 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

For clocks not in the CCF, their parents will not have UCLASS_CLK, so we
just enable them as normal. The enable count is local to the struct clk,
but this will never result in the actual en-/dis-able op being called
(unless the same struct clk is enabled twice).

For clocks in the CCF, we always traverse up the tree when enabling.
Previously, CCF clocks without id set would be skipped, stopping the
traversal too early.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Acked-by: Lukasz Majewski <lukma@denx.de>
---

Changes in v5:
- Clear enable_count on request

Changes in v4:
- Lint

Changes in v3:
- New

 drivers/clk/clk-uclass.c | 60 ++++++++++++++++++----------------------
 1 file changed, 27 insertions(+), 33 deletions(-)

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index b42e76ebfa..ffd8f874dc 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -410,6 +410,7 @@ int clk_request(struct udevice *dev, struct clk *clk)
 	ops = clk_dev_ops(dev);
 
 	clk->dev = dev;
+	clk->enable_count = 0;
 
 	if (!ops->request)
 		return 0;
@@ -522,7 +523,6 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 int clk_enable(struct clk *clk)
 {
 	const struct clk_ops *ops;
-	struct clk *clkp = NULL;
 	int ret;
 
 	debug("%s(clk=%p \"%s\")\n", __func__, clk, clk->dev->name);
@@ -531,32 +531,29 @@ int clk_enable(struct clk *clk)
 	ops = clk_dev_ops(clk->dev);
 
 	if (CONFIG_IS_ENABLED(CLK_CCF)) {
-		/* Take id 0 as a non-valid clk, such as dummy */
-		if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
-			if (clkp->enable_count) {
-				clkp->enable_count++;
-				return 0;
-			}
-			if (clkp->dev->parent &&
-			    device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
-				ret = clk_enable(dev_get_clk_ptr(clkp->dev->parent));
-				if (ret) {
-					printf("Enable %s failed\n",
-					       clkp->dev->parent->name);
-					return ret;
-				}
+		if (clk->enable_count) {
+			clk->enable_count++;
+			return 0;
+		}
+		if (clk->dev->parent &&
+		    device_get_uclass_id(clk->dev->parent) == UCLASS_CLK) {
+			ret = clk_enable(dev_get_clk_ptr(clk->dev->parent));
+			if (ret) {
+				printf("Enable %s failed\n",
+				       clk->dev->parent->name);
+				return ret;
 			}
 		}
 
 		if (ops->enable) {
 			ret = ops->enable(clk);
 			if (ret) {
-				printf("Enable %s failed\n", clk->dev->name);
+				printf("Enable %s failed (error %d)\n",
+				       clk->dev->name, ret);
 				return ret;
 			}
 		}
-		if (clkp)
-			clkp->enable_count++;
+		clk->enable_count++;
 	} else {
 		if (!ops->enable)
 			return -ENOSYS;
@@ -582,7 +579,6 @@ int clk_enable_bulk(struct clk_bulk *bulk)
 int clk_disable(struct clk *clk)
 {
 	const struct clk_ops *ops;
-	struct clk *clkp = NULL;
 	int ret;
 
 	debug("%s(clk=%p)\n", __func__, clk);
@@ -591,29 +587,27 @@ int clk_disable(struct clk *clk)
 	ops = clk_dev_ops(clk->dev);
 
 	if (CONFIG_IS_ENABLED(CLK_CCF)) {
-		if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
-			if (clkp->enable_count == 0) {
-				printf("clk %s already disabled\n",
-				       clkp->dev->name);
-				return 0;
-			}
-
-			if (--clkp->enable_count > 0)
-				return 0;
+		if (clk->enable_count == 0) {
+			printf("clk %s already disabled\n",
+			       clk->dev->name);
+			return 0;
 		}
 
+		if (--clk->enable_count > 0)
+			return 0;
+
 		if (ops->disable) {
 			ret = ops->disable(clk);
 			if (ret)
 				return ret;
 		}
 
-		if (clkp && clkp->dev->parent &&
-		    device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
-			ret = clk_disable(dev_get_clk_ptr(clkp->dev->parent));
+		if (clk->dev->parent &&
+		    device_get_uclass_id(clk->dev) == UCLASS_CLK) {
+			ret = clk_disable(dev_get_clk_ptr(clk->dev->parent));
 			if (ret) {
-				printf("Disable %s failed\n",
-				       clkp->dev->parent->name);
+				printf("Disable %s failed (error %d)\n",
+				       clk->dev->parent->name, ret);
 				return ret;
 			}
 		}
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 04/33] clk: Add functions to register CCF clock structs
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (2 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 05/33] clk: Add K210 pll support Sean Anderson
                   ` (28 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This patch adds alternate versions of the clk_*_register functions for use
with statically-allocated struct clks. This allows drivers to define clocks
at compile-time and register them at run-time without malloc-ing. This
increases the size of the binary, but should not affect ram usage (since
the clocks now no longer live on the heap).

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/clk/clk-composite.c  | 103 +++++++++++++++++++----------------
 drivers/clk/clk-divider.c    |  56 +++++++++----------
 drivers/clk/clk-gate.c       |  38 ++++++++-----
 include/linux/clk-provider.h |   9 +++
 4 files changed, 112 insertions(+), 94 deletions(-)

diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 819bfca2fc..b328c4e5a5 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -95,6 +95,51 @@ static int clk_composite_disable(struct clk *clk)
 		return 0;
 }
 
+struct clk *clk_register_composite_struct(const char *name,
+					  const char * const *parent_names,
+					  int num_parents,
+					  struct clk_composite *composite)
+{
+	int ret;
+	struct clk *clk;
+
+	if (!num_parents || (num_parents != 1 && !composite->mux))
+		return ERR_PTR(-EINVAL);
+
+	if (composite->mux && composite->mux_ops)
+		composite->mux->data = (ulong)composite;
+
+	if (composite->rate && composite->rate_ops) {
+		if (!composite->rate_ops->get_rate)
+			return ERR_PTR(-EINVAL);
+
+		composite->rate->data = (ulong)composite;
+	}
+
+	if (composite->gate && composite->gate_ops) {
+		if (!composite->gate_ops->enable ||
+		    !composite->gate_ops->disable)
+			return ERR_PTR(-EINVAL);
+
+		composite->gate->data = (ulong)composite;
+	}
+
+	clk = &composite->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
+			   parent_names[clk_composite_get_parent(clk)]);
+	if (ret)
+		clk = ERR_PTR(ret);
+
+	if (composite->mux)
+		composite->mux->dev = clk->dev;
+	if (composite->rate)
+		composite->rate->dev = clk->dev;
+	if (composite->gate)
+		composite->gate->dev = clk->dev;
+
+	return clk;
+}
+
 struct clk *clk_register_composite(struct device *dev, const char *name,
 				   const char * const *parent_names,
 				   int num_parents, struct clk *mux,
@@ -107,62 +152,24 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 {
 	struct clk *clk;
 	struct clk_composite *composite;
-	int ret;
-
-	if (!num_parents || (num_parents != 1 && !mux))
-		return ERR_PTR(-EINVAL);
 
 	composite = kzalloc(sizeof(*composite), GFP_KERNEL);
 	if (!composite)
 		return ERR_PTR(-ENOMEM);
 
-	if (mux && mux_ops) {
-		composite->mux = mux;
-		composite->mux_ops = mux_ops;
-		mux->data = (ulong)composite;
-	}
+	composite->mux = mux;
+	composite->mux_ops = mux_ops;
 
-	if (rate && rate_ops) {
-		if (!rate_ops->get_rate) {
-			clk = ERR_PTR(-EINVAL);
-			goto err;
-		}
+	composite->rate = rate;
+	composite->rate_ops = rate_ops;
 
-		composite->rate = rate;
-		composite->rate_ops = rate_ops;
-		rate->data = (ulong)composite;
-	}
+	composite->gate = gate;
+	composite->gate_ops = gate_ops;
 
-	if (gate && gate_ops) {
-		if (!gate_ops->enable || !gate_ops->disable) {
-			clk = ERR_PTR(-EINVAL);
-			goto err;
-		}
-
-		composite->gate = gate;
-		composite->gate_ops = gate_ops;
-		gate->data = (ulong)composite;
-	}
-
-	clk = &composite->clk;
-	ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
-			   parent_names[clk_composite_get_parent(clk)]);
-	if (ret) {
-		clk = ERR_PTR(ret);
-		goto err;
-	}
-
-	if (composite->mux)
-		composite->mux->dev = clk->dev;
-	if (composite->rate)
-		composite->rate->dev = clk->dev;
-	if (composite->gate)
-		composite->gate->dev = clk->dev;
-
-	return clk;
-
-err:
-	kfree(composite);
+	clk = clk_register_composite_struct(name, parent_names, num_parents,
+					    composite);
+	if (IS_ERR(clk))
+		kfree(composite);
 	return clk;
 }
 
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 5fe1c3941f..747504d0a0 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -178,22 +178,37 @@ const struct clk_ops clk_divider_ops = {
 	.set_rate = clk_divider_set_rate,
 };
 
-static struct clk *_register_divider(struct device *dev, const char *name,
-		const char *parent_name, unsigned long flags,
-		void __iomem *reg, u8 shift, u8 width,
-		u8 clk_divider_flags, const struct clk_div_table *table)
+struct clk *clk_register_divider_struct(const char *name,
+					const char *parent_name,
+					struct clk_divider *div)
 {
-	struct clk_divider *div;
-	struct clk *clk;
 	int ret;
+	struct clk *clk;
 
-	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
-		if (width + shift > 16) {
+	if (div->flags & CLK_DIVIDER_HIWORD_MASK) {
+		if (div->width + div->shift > 16) {
 			pr_warn("divider value exceeds LOWORD field\n");
 			return ERR_PTR(-EINVAL);
 		}
 	}
 
+	/* register the clock */
+	clk = &div->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
+	if (ret)
+		return ERR_PTR(ret);
+	return clk;
+}
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+				 const char *parent_name, unsigned long flags,
+				 void __iomem *reg, u8 shift, u8 width,
+				 u8 clk_divider_flags)
+{
+	struct clk_divider *div;
+	struct clk *clk;
+
 	/* allocate the divider */
 	div = kzalloc(sizeof(*div), GFP_KERNEL);
 	if (!div)
@@ -204,34 +219,13 @@ static struct clk *_register_divider(struct device *dev, const char *name,
 	div->shift = shift;
 	div->width = width;
 	div->flags = clk_divider_flags;
-	div->table = table;
 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
 	div->io_divider_val = *(u32 *)reg;
 #endif
 
-	/* register the clock */
-	clk = &div->clk;
-
-	ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
-	if (ret) {
-		kfree(div);
-		return ERR_PTR(ret);
-	}
-
-	return clk;
-}
-
-struct clk *clk_register_divider(struct device *dev, const char *name,
-		const char *parent_name, unsigned long flags,
-		void __iomem *reg, u8 shift, u8 width,
-		u8 clk_divider_flags)
-{
-	struct clk *clk;
-
-	clk =  _register_divider(dev, name, parent_name, flags, reg, shift,
-				 width, clk_divider_flags, NULL);
+	clk = clk_register_divider_struct(name, parent_name, div);
 	if (IS_ERR(clk))
-		return ERR_CAST(clk);
+		kfree(div);
 	return clk;
 }
 
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index b5827dced0..82445d2ccb 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -111,6 +111,27 @@ const struct clk_ops clk_gate_ops = {
 	.get_rate = clk_generic_get_rate,
 };
 
+struct clk *clk_register_gate_struct(const char *name, const char *parent_name,
+				     struct clk_gate *gate)
+{
+	int ret;
+	struct clk *clk;
+
+	if (gate->flags & CLK_GATE_HIWORD_MASK) {
+		if (gate->bit_idx > 15) {
+			pr_err("gate bit exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	clk = &gate->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
+	if (ret)
+		return ERR_PTR(ret);
+	return clk;
+}
+
 struct clk *clk_register_gate(struct device *dev, const char *name,
 			      const char *parent_name, unsigned long flags,
 			      void __iomem *reg, u8 bit_idx,
@@ -118,14 +139,6 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 {
 	struct clk_gate *gate;
 	struct clk *clk;
-	int ret;
-
-	if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
-		if (bit_idx > 15) {
-			pr_err("gate bit exceeds LOWORD field\n");
-			return ERR_PTR(-EINVAL);
-		}
-	}
 
 	/* allocate the gate */
 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
@@ -140,14 +153,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 	gate->io_gate_val = *(u32 *)reg;
 #endif
 
-	clk = &gate->clk;
-
-	ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
-	if (ret) {
+	clk = clk_register_gate_struct(name, parent_name, gate);
+	if (IS_ERR(clk))
 		kfree(gate);
-		return ERR_PTR(ret);
-	}
-
 	return clk;
 }
 
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 8a20743ad8..8bf8dca0a3 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -95,6 +95,8 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 			      const char *parent_name, unsigned long flags,
 			      void __iomem *reg, u8 bit_idx,
 			      u8 clk_gate_flags, spinlock_t *lock);
+struct clk *clk_register_gate_struct(const char *name, const char *parent_name,
+				     struct clk_gate *gate);
 
 struct clk_div_table {
 	unsigned int	val;
@@ -166,6 +168,10 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
 		struct clk *rate_clk, const struct clk_ops *rate_ops,
 		struct clk *gate_clk, const struct clk_ops *gate_ops,
 		unsigned long flags);
+struct clk *clk_register_composite_struct(const char *name,
+					  const char * const *parent_names,
+					  int num_parents,
+					  struct clk_composite *composite);
 
 int clk_register(struct clk *clk, const char *drv_name, const char *name,
 		 const char *parent_name);
@@ -178,6 +184,9 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 shift, u8 width,
 		u8 clk_divider_flags);
+struct clk *clk_register_divider_struct(const char *name,
+					const char *parent_name,
+					struct clk_divider *div);
 
 struct clk *clk_register_mux(struct device *dev, const char *name,
 		const char * const *parent_names, u8 num_parents,
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 05/33] clk: Add K210 pll support
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (3 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 04/33] clk: Add functions to register CCF clock structs Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 06/33] clk: Add a bypass clock for K210 Sean Anderson
                   ` (27 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This pll code is primarily based on the code from the kendryte standalone
sdk in lib/drivers/sysctl.c. k210_pll_calc_params is roughly analogous to
the algorithm used to set the pll frequency, but it has been completely
rewritten to be fixed-point based.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Add function to register from a struct

Changes in v4:
- Rename the reference clock to "divider clock", and input clock to "reference
  clock" to match the upstream documentation.
- Add a test for calc_params. This currently resides in test/dm, but perhaps it
  should be moved to its own directory.
- Update MAINTAINERS
- Update copyright
- Lint

Changes in v3:
- Add an option to not include support for setting the pll rate. This saves
  around 1K in the final executable.
- Remove udelays to suppress warnings
- Bypass PLL after enabling, instead of before
- Check if the PLL is enabled already before doing a reset
- Fix bug with locked mask

Changes in v2:
- Rename driver to "k210_clk_pll"
- Add additional in-line documentation on algorithm and PLLs
- Restrict the range of internal VCO and reference frequencies
- Don't load driver before relocation
- Remove spurious references to mach-k210

 drivers/clk/Kconfig           |   1 +
 drivers/clk/Makefile          |   1 +
 drivers/clk/kendryte/Kconfig  |  12 +
 drivers/clk/kendryte/Makefile |   1 +
 drivers/clk/kendryte/pll.c    | 600 ++++++++++++++++++++++++++++++++++
 include/kendryte/pll.h        |  57 ++++
 include/test/export.h         |  16 +
 test/dm/Makefile              |   1 +
 test/dm/k210_pll.c            |  95 ++++++
 9 files changed, 784 insertions(+)
 create mode 100644 drivers/clk/kendryte/Kconfig
 create mode 100644 drivers/clk/kendryte/Makefile
 create mode 100644 drivers/clk/kendryte/pll.c
 create mode 100644 include/kendryte/pll.h
 create mode 100644 include/test/export.h
 create mode 100644 test/dm/k210_pll.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1992d4a4b4..fb8335267b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -155,6 +155,7 @@ source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/at91/Kconfig"
 source "drivers/clk/exynos/Kconfig"
 source "drivers/clk/imx/Kconfig"
+source "drivers/clk/kendryte/Kconfig"
 source "drivers/clk/meson/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/owl/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e01783391d..d911954581 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
 obj-$(CONFIG_CLK_EXYNOS) += exynos/
 obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
 obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
+obj-$(CONFIG_CLK_K210) += kendryte/
 obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
 obj-$(CONFIG_CLK_OWL) += owl/
 obj-$(CONFIG_CLK_RENESAS) += renesas/
diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig
new file mode 100644
index 0000000000..7b69c8afaf
--- /dev/null
+++ b/drivers/clk/kendryte/Kconfig
@@ -0,0 +1,12 @@
+config CLK_K210
+	bool "Clock support for Kendryte K210"
+	depends on CLK && CLK_CCF
+	help
+	  This enables support clock driver for Kendryte K210 platforms.
+
+config CLK_K210_SET_RATE
+	bool "Enable setting the Kendryte K210 PLL rate"
+	depends on CLK_K210
+	help
+	  Add functionality to calculate new rates for K210 PLLs. Enabling this
+	  feature adds around 1K to U-Boot's final size.
diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
new file mode 100644
index 0000000000..c56d93ea1c
--- /dev/null
+++ b/drivers/clk/kendryte/Makefile
@@ -0,0 +1 @@
+obj-y += pll.o
diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c
new file mode 100644
index 0000000000..8333e32e61
--- /dev/null
+++ b/drivers/clk/kendryte/pll.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#include <kendryte/pll.h>
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <asm/io.h>
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <log.h>
+#include <serial.h>
+
+#define CLK_K210_PLL "k210_clk_pll"
+
+#ifdef CONFIG_CLK_K210_SET_RATE
+static int k210_pll_enable(struct clk *clk);
+static int k210_pll_disable(struct clk *clk);
+
+/*
+ * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc.
+ * General-Purpose PLL. The logical layout of the PLL with internal feedback is
+ * approximately the following:
+ *
+ *  +---------------+
+ *  |reference clock|
+ *  +---------------+
+ *          |
+ *          v
+ *        +--+
+ *        |/r|
+ *        +--+
+ *          |
+ *          v
+ *   +-------------+
+ *   |divided clock|
+ *   +-------------+
+ *          |
+ *          v
+ *  +--------------+
+ *  |phase detector|<---+
+ *  +--------------+    |
+ *          |           |
+ *          v   +--------------+
+ *        +---+ |feedback clock|
+ *        |VCO| +--------------+
+ *        +---+         ^
+ *          |    +--+   |
+ *          +--->|/f|---+
+ *          |    +--+
+ *          v
+ *        +---+
+ *        |/od|
+ *        +---+
+ *          |
+ *          v
+ *       +------+
+ *       |output|
+ *       +------+
+ *
+ * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode,
+ * the effect of the division by f is to multiply the input frequency. The
+ * equation for the output rate is
+ *   rate = (rate_in * f) / (r * od).
+ * Moving knowns to one side of the equation, we get
+ *   rate / rate_in = f / (r * od)
+ * Rearranging slightly,
+ *   abs_error = abs((rate / rate_in) - (f / (r * od))).
+ * To get relative, error, we divide by the expected ratio
+ *   error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in).
+ * Simplifying,
+ *   error = abs(1 - f / (r * od)) / (rate / rate_in)
+ *   error = abs(1 - (f * rate_in) / (r * od * rate))
+ * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate,
+ *   error = abs((f * inv_ratio) / (r * od) - 1)
+ * This is the error used in evaluating parameters.
+ *
+ * r and od are four bits each, while f is six bits. Because r and od are
+ * multiplied together, instead of the full 256 values possible if both bits
+ * were used fully, there are only 97 distinct products. Combined with f, there
+ * are 6208 theoretical settings for the PLL. However, most of these settings
+ * can be ruled out immediately because they do not have the correct ratio.
+ *
+ * In addition to the constraint of approximating the desired ratio, parameters
+ * must also keep internal pll frequencies within acceptable ranges. The divided
+ * clock's minimum and maximum frequencies have a ratio of around 128.  This
+ * leaves fairly substantial room to work with, especially since the only
+ * affected parameter is r. The VCO's minimum and maximum frequency have a ratio
+ * of 5, which is considerably more restrictive.
+ *
+ * The r and od factors are stored in a table. This is to make it easy to find
+ * the next-largest product. Some products have multiple factorizations, but
+ * only when one factor has at least a 2.5x ratio to the factors of the other
+ * factorization. This is because any smaller ratio would not make a difference
+ * when ensuring the VCO's frequency is within spec.
+ *
+ * Throughout the calculation function, fixed point arithmetic is used. Because
+ * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit
+ * 32.32 fixed-point numbers are used to represent ratios. In general, to
+ * implement division, the numerator is first multiplied by 2^32. This gives a
+ * result where the whole number part is in the upper 32 bits, and the fraction
+ * is in the lower 32 bits.
+ *
+ * In general, rounding is done to the closest integer. This helps find the best
+ * approximation for the ratio. Rounding in one direction (e.g down) could cause
+ * the function to miss a better ratio with one of the parameters increased by
+ * one.
+ */
+
+/*
+ * The factors table was generated with the following python code:
+ *
+ * def p(x, y):
+ *    return (1.0*x/y > 2.5) or (1.0*y/x > 2.5)
+ *
+ * factors = {}
+ * for i in range(1, 17):
+ *    for j in range(1, 17):
+ *       fs = factors.get(i*j) or []
+ *       if fs == [] or all([
+ *             (p(i, x) and p(i, y)) or (p(j, x) and p(j, y))
+ *             for (x, y) in fs]):
+ *          fs.append((i, j))
+ *          factors[i*j] = fs
+ *
+ * for k, l in sorted(factors.items()):
+ *    for v in l:
+ *       print("PACK(%s, %s)," % v)
+ */
+#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF))
+#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1)
+#define UNPACK_OD(val) (((val) & 0xF) + 1)
+static const u8 factors[] = {
+	PACK(1, 1),
+	PACK(1, 2),
+	PACK(1, 3),
+	PACK(1, 4),
+	PACK(1, 5),
+	PACK(1, 6),
+	PACK(1, 7),
+	PACK(1, 8),
+	PACK(1, 9),
+	PACK(3, 3),
+	PACK(1, 10),
+	PACK(1, 11),
+	PACK(1, 12),
+	PACK(3, 4),
+	PACK(1, 13),
+	PACK(1, 14),
+	PACK(1, 15),
+	PACK(3, 5),
+	PACK(1, 16),
+	PACK(4, 4),
+	PACK(2, 9),
+	PACK(2, 10),
+	PACK(3, 7),
+	PACK(2, 11),
+	PACK(2, 12),
+	PACK(5, 5),
+	PACK(2, 13),
+	PACK(3, 9),
+	PACK(2, 14),
+	PACK(2, 15),
+	PACK(2, 16),
+	PACK(3, 11),
+	PACK(5, 7),
+	PACK(3, 12),
+	PACK(3, 13),
+	PACK(4, 10),
+	PACK(3, 14),
+	PACK(4, 11),
+	PACK(3, 15),
+	PACK(3, 16),
+	PACK(7, 7),
+	PACK(5, 10),
+	PACK(4, 13),
+	PACK(6, 9),
+	PACK(5, 11),
+	PACK(4, 14),
+	PACK(4, 15),
+	PACK(7, 9),
+	PACK(4, 16),
+	PACK(5, 13),
+	PACK(6, 11),
+	PACK(5, 14),
+	PACK(6, 12),
+	PACK(5, 15),
+	PACK(7, 11),
+	PACK(6, 13),
+	PACK(5, 16),
+	PACK(9, 9),
+	PACK(6, 14),
+	PACK(8, 11),
+	PACK(6, 15),
+	PACK(7, 13),
+	PACK(6, 16),
+	PACK(7, 14),
+	PACK(9, 11),
+	PACK(10, 10),
+	PACK(8, 13),
+	PACK(7, 15),
+	PACK(9, 12),
+	PACK(10, 11),
+	PACK(7, 16),
+	PACK(9, 13),
+	PACK(8, 15),
+	PACK(11, 11),
+	PACK(9, 14),
+	PACK(8, 16),
+	PACK(10, 13),
+	PACK(11, 12),
+	PACK(9, 15),
+	PACK(10, 14),
+	PACK(11, 13),
+	PACK(9, 16),
+	PACK(10, 15),
+	PACK(11, 14),
+	PACK(12, 13),
+	PACK(10, 16),
+	PACK(11, 15),
+	PACK(12, 14),
+	PACK(13, 13),
+	PACK(11, 16),
+	PACK(12, 15),
+	PACK(13, 14),
+	PACK(12, 16),
+	PACK(13, 15),
+	PACK(14, 14),
+	PACK(13, 16),
+	PACK(14, 15),
+	PACK(14, 16),
+	PACK(15, 15),
+	PACK(15, 16),
+	PACK(16, 16),
+};
+
+TEST_STATIC int k210_pll_calc_params(u32 rate, u32 rate_in,
+				     struct k210_pll_params *best)
+{
+	int i;
+	s64 error, best_error;
+	u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */
+	u64 max_r;
+	u64 r, f, od;
+
+	/*
+	 * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the
+	 * VCO frequency. These are not the same limits as below because od can
+	 * reduce the output frequency by 16.
+	 */
+	if (rate > 1750000000 || rate < 21250000)
+		return -EINVAL;
+
+	/* Similar restrictions on the input rate */
+	if (rate_in > 1750000000 || rate_in < 13300000)
+		return -EINVAL;
+
+	ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in);
+	inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate);
+	/* Can't increase by more than 64 or reduce by more than 256 */
+	if (rate > rate_in && ratio > (64ULL << 32))
+		return -EINVAL;
+	else if (rate <= rate_in && inv_ratio > (256ULL << 32))
+		return -EINVAL;
+
+	/*
+	 * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3
+	 * MHz. There is no minimum, since the only way to get a higher input
+	 * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs
+	 * cannot output frequencies greater than 1.75 GHz, the minimum would
+	 * never be greater than one.
+	 */
+	max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000);
+
+	/* Variables get immediately incremented, so start at -1th iteration */
+	i = -1;
+	f = 0;
+	r = 0;
+	od = 0;
+	error = best_error = S64_MAX;
+	/* do-while here so we always try@least one ratio */
+	do {
+		/*
+		 * Whether we swapped r and od while enforcing frequency limits
+		 */
+		bool swapped = false;
+		u64 last_od = od;
+		u64 last_r = r;
+
+		/*
+		 * Try the next largest value for f (or r and od) and
+		 * recalculate the other parameters based on that
+		 */
+		if (rate > rate_in) {
+			/*
+			 * Skip factors of the same product if we already tried
+			 * out that product
+			 */
+			do {
+				i++;
+				r = UNPACK_R(factors[i]);
+				od = UNPACK_OD(factors[i]);
+			} while (i + 1 < ARRAY_SIZE(factors) &&
+				 r * od == last_r * last_od);
+
+			/* Round close */
+			f = (r * od * ratio + BIT(31)) >> 32;
+			if (f > 64)
+				f = 64;
+		} else {
+			u64 tmp = ++f * inv_ratio;
+			bool round_up = !!(tmp & BIT(31));
+			u32 goal = (tmp >> 32) + round_up;
+			u32 err, last_err;
+
+			/* Get the next r/od pair in factors */
+			while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) {
+				i++;
+				r = UNPACK_R(factors[i]);
+				od = UNPACK_OD(factors[i]);
+			}
+
+			/*
+			 * This is a case of double rounding. If we rounded up
+			 * above, we need to round down (in cases of ties) here.
+			 * This prevents off-by-one errors resulting from
+			 * choosing X+2 over X when X.Y rounds up to X+1 and
+			 * there is no r * od = X+1. For the converse, when X.Y
+			 * is rounded down to X, we should choose X+1 over X-1.
+			 */
+			err = abs(r * od - goal);
+			last_err = abs(last_r * last_od - goal);
+			if (last_err < err || (round_up && last_err == err)) {
+				i--;
+				r = last_r;
+				od = last_od;
+			}
+		}
+
+		/*
+		 * Enforce limits on internal clock frequencies. If we
+		 * aren't in spec, try swapping r and od. If everything is
+		 * in-spec, calculate the relative error.
+		 */
+		while (true) {
+			/*
+			 * Whether the intermediate frequencies are out-of-spec
+			 */
+			bool out_of_spec = false;
+
+			if (r > max_r) {
+				out_of_spec = true;
+			} else {
+				/*
+				 * There is no way to only divide once; we need
+				 * to examine the frequency with and without the
+				 * effect of od.
+				 */
+				u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r);
+
+				if (vco > 1750000000 || vco < 340000000)
+					out_of_spec = true;
+			}
+
+			if (out_of_spec) {
+				if (!swapped) {
+					u64 tmp = r;
+
+					r = od;
+					od = tmp;
+					swapped = true;
+					continue;
+				} else {
+					/*
+					 * Try looking ahead to see if there are
+					 * additional factors for the same
+					 * product.
+					 */
+					if (i + 1 < ARRAY_SIZE(factors)) {
+						u64 new_r, new_od;
+
+						i++;
+						new_r = UNPACK_R(factors[i]);
+						new_od = UNPACK_OD(factors[i]);
+						if (r * od == new_r * new_od) {
+							r = new_r;
+							od = new_od;
+							swapped = false;
+							continue;
+						}
+						i--;
+					}
+					break;
+				}
+			}
+
+			error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od);
+			/* The lower 16 bits are spurious */
+			error = abs((error - BIT(32))) >> 16;
+
+			if (error < best_error) {
+				best->r = r;
+				best->f = f;
+				best->od = od;
+				best_error = error;
+			}
+			break;
+		}
+	} while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0);
+
+	if (best_error == S64_MAX)
+		return -EINVAL;
+
+	log_debug("best error %lld\n", best_error);
+	return 0;
+}
+
+static ulong k210_pll_set_rate(struct clk *clk, ulong rate)
+{
+	int err;
+	long long rate_in = clk_get_parent_rate(clk);
+	struct k210_pll_params params = {};
+	struct k210_pll *pll = to_k210_pll(clk);
+	u32 reg;
+
+	if (rate_in < 0)
+		return rate_in;
+
+	log_debug("Calculating parameters with rate=%lu and rate_in=%lld\n",
+		  rate, rate_in);
+	err = k210_pll_calc_params(rate, rate_in, &params);
+	if (err)
+		return err;
+	log_debug("Got r=%u f=%u od=%u\n", params.r, params.f, params.od);
+
+	/*
+	 * Don't use clk_disable as it might not actually disable the pll due to
+	 * refcounting
+	 */
+	k210_pll_disable(clk);
+
+	reg = readl(pll->reg);
+	reg &= ~K210_PLL_CLKR
+	    &  ~K210_PLL_CLKF
+	    &  ~K210_PLL_CLKOD
+	    &  ~K210_PLL_BWADJ;
+	reg |= FIELD_PREP(K210_PLL_CLKR, params.r - 1)
+	    |  FIELD_PREP(K210_PLL_CLKF, params.f - 1)
+	    |  FIELD_PREP(K210_PLL_CLKOD, params.od - 1)
+	    |  FIELD_PREP(K210_PLL_BWADJ, params.f - 1);
+	writel(reg, pll->reg);
+
+	err = k210_pll_enable(clk);
+	if (err)
+		return err;
+
+	serial_setbrg();
+	return clk_get_rate(clk);
+}
+#endif /* CONFIG_CLK_K210_SET_RATE */
+
+static ulong k210_pll_get_rate(struct clk *clk)
+{
+	long long rate_in = clk_get_parent_rate(clk);
+	struct k210_pll *pll = to_k210_pll(clk);
+	u64 r, f, od;
+	u32 reg = readl(pll->reg);
+
+	if (rate_in < 0 || (reg & K210_PLL_BYPASS))
+		return rate_in;
+
+	if (!(reg & K210_PLL_PWRD))
+		return 0;
+
+	r = FIELD_GET(K210_PLL_CLKR, reg) + 1;
+	f = FIELD_GET(K210_PLL_CLKF, reg) + 1;
+	od = FIELD_GET(K210_PLL_CLKOD, reg) + 1;
+
+	return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od);
+}
+
+/*
+ * Wait for the PLL to be locked. If the PLL is not locked, try clearing the
+ * slip before retrying
+ */
+static void k210_pll_waitfor_lock(struct k210_pll *pll)
+{
+	u32 mask = GENMASK(pll->width - 1, 0) << pll->shift;
+
+	while (true) {
+		u32 reg = readl(pll->lock);
+
+		if ((reg & mask) == mask)
+			break;
+
+		reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP);
+		writel(reg, pll->lock);
+	}
+}
+
+/* Adapted from sysctl_pll_enable */
+static int k210_pll_enable(struct clk *clk)
+{
+	struct k210_pll *pll = to_k210_pll(clk);
+	u32 reg = readl(pll->reg);
+
+	if ((reg | K210_PLL_PWRD) && !(reg | K210_PLL_RESET))
+		return 0;
+
+	reg |= K210_PLL_PWRD;
+	writel(reg, pll->reg);
+
+	/* Ensure reset is low before asserting it */
+	reg &= ~K210_PLL_RESET;
+	writel(reg, pll->reg);
+	reg |= K210_PLL_RESET;
+	writel(reg, pll->reg);
+	nop();
+	nop();
+	reg &= ~K210_PLL_RESET;
+	writel(reg, pll->reg);
+
+	k210_pll_waitfor_lock(pll);
+
+	reg &= ~K210_PLL_BYPASS;
+	writel(reg, pll->reg);
+
+	return 0;
+}
+
+static int k210_pll_disable(struct clk *clk)
+{
+	struct k210_pll *pll = to_k210_pll(clk);
+	u32 reg = readl(pll->reg);
+
+	/*
+	 * Bypassing before powering off is important so child clocks don't stop
+	 * working. This is especially important for pll0, the indirect parent
+	 * of the cpu clock.
+	 */
+	reg |= K210_PLL_BYPASS;
+	writel(reg, pll->reg);
+
+	reg &= ~K210_PLL_PWRD;
+	writel(reg, pll->reg);
+	return 0;
+}
+
+const struct clk_ops k210_pll_ops = {
+	.get_rate = k210_pll_get_rate,
+#ifdef CONFIG_CLK_K210_SET_RATE
+	.set_rate = k210_pll_set_rate,
+#endif
+	.enable = k210_pll_enable,
+	.disable = k210_pll_disable,
+};
+
+struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
+				     struct k210_pll *pll)
+{
+	int ret;
+	struct clk *clk = &pll->clk;
+
+	ret = clk_register(clk, CLK_K210_PLL, name, parent_name);
+	if (ret)
+		return ERR_PTR(ret);
+	return clk;
+}
+
+struct clk *k210_register_pll(const char *name, const char *parent_name,
+			      void __iomem *reg, void __iomem *lock, u8 shift,
+			      u8 width)
+{
+	struct clk *clk;
+	struct k210_pll *pll;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+	pll->reg = reg;
+	pll->lock = lock;
+	pll->shift = shift;
+	pll->width = width;
+
+	clk = k210_register_pll_struct(name, parent_name, pll);
+	if (IS_ERR(clk))
+		kfree(pll);
+	return clk;
+}
+
+U_BOOT_DRIVER(k210_pll) = {
+	.name	= CLK_K210_PLL,
+	.id	= UCLASS_CLK,
+	.ops	= &k210_pll_ops,
+};
diff --git a/include/kendryte/pll.h b/include/kendryte/pll.h
new file mode 100644
index 0000000000..e181b0a9c3
--- /dev/null
+++ b/include/kendryte/pll.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#ifndef K210_PLL_H
+#define K210_PLL_H
+
+#include <clk.h>
+#include <test/export.h>
+
+#define K210_PLL_CLKR GENMASK(3, 0)
+#define K210_PLL_CLKF GENMASK(9, 4)
+#define K210_PLL_CLKOD GENMASK(13, 10) /* Output Divider */
+#define K210_PLL_BWADJ GENMASK(19, 14) /* BandWidth Adjust */
+#define K210_PLL_RESET BIT(20)
+#define K210_PLL_PWRD BIT(21) /* PoWeReD */
+#define K210_PLL_INTFB BIT(22) /* Internal FeedBack */
+#define K210_PLL_BYPASS BIT(23)
+#define K210_PLL_TEST BIT(24)
+#define K210_PLL_EN BIT(25)
+#define K210_PLL_TEST_EN BIT(26)
+
+#define K210_PLL_LOCK 0
+#define K210_PLL_CLEAR_SLIP 2
+#define K210_PLL_TEST_OUT 3
+
+struct k210_pll {
+	struct clk clk;
+	void __iomem *reg; /* Base PLL register */
+	void __iomem *lock; /* Common PLL lock register */
+	u8 shift; /* Offset of bits in lock register */
+	u8 width; /* Width of lock bits to test against */
+};
+
+#define to_k210_pll(_clk) container_of(_clk, struct k210_pll, clk)
+
+struct k210_pll_params {
+	u8 r;
+	u8 f;
+	u8 od;
+};
+
+#ifdef CONFIG_UNIT_TEST
+TEST_STATIC int k210_pll_calc_params(u32 rate, u32 rate_in,
+				     struct k210_pll_params *best);
+#define nop()
+#endif
+
+extern const struct clk_ops k210_pll_ops;
+
+struct clk *k210_register_pll_struct(const char *name, const char *parent_name,
+				     struct k210_pll *pll);
+struct clk *k210_register_pll(const char *name, const char *parent_name,
+			      void __iomem *reg, void __iomem *lock, u8 shift,
+			      u8 width);
+
+#endif /* K210_PLL_H */
diff --git a/include/test/export.h b/include/test/export.h
new file mode 100644
index 0000000000..afc755a8ff
--- /dev/null
+++ b/include/test/export.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef TEST_EXPORT_H
+#define TEST_EXPORT_H
+
+/* Declare something static, unless we are doing unit tests */
+#ifdef CONFIG_UNIT_TEST
+#define TEST_STATIC
+#else
+#define TEST_STATIC static
+#endif
+
+#endif /* TEST_EXPORT_H */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index dd1ceff86c..ef6065b60f 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -70,4 +70,5 @@ obj-$(CONFIG_DMA) += dma.o
 obj-$(CONFIG_DM_MDIO) += mdio.o
 obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
 obj-$(CONFIG_DM_RNG) += rng.o
+obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
 endif
diff --git a/test/dm/k210_pll.c b/test/dm/k210_pll.c
new file mode 100644
index 0000000000..3ddd12d991
--- /dev/null
+++ b/test/dm/k210_pll.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dm/test.h>
+#include <kendryte/pll.h>
+#include <test/ut.h>
+
+static int dm_test_k210_pll_calc_params(u32 rate, u32 rate_in,
+					struct k210_pll_params *best)
+{
+	u64 f, r, od, max_r, inv_ratio;
+	s64 error, best_error;
+
+	error = best_error = S64_MAX;
+	max_r = min(16ULL, DIV_ROUND_DOWN_ULL(rate_in, 13300000));
+	inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate);
+
+	/* Brute force it */
+	for (r = 1; r <= max_r; r++) {
+		for (f = 1; f <= 64; f++) {
+			for (od = 1; od <= 16; od++) {
+				u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r);
+
+				if (vco > 1750000000 || vco < 340000000)
+					continue;
+
+				error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio,
+							      r * od);
+				/* The lower 16 bits are spurious */
+				error = abs((error - BIT(32))) >> 16;
+				if (error < best_error) {
+					best->r = r;
+					best->f = f;
+					best->od = od;
+					best_error = error;
+				}
+			}
+		}
+	}
+
+	if (best_error == S64_MAX)
+		return -EINVAL;
+	return 0;
+}
+
+static int dm_test_k210_pll_compare(struct k210_pll_params *ours,
+				    struct k210_pll_params *theirs)
+{
+	return (u32)ours->f * theirs->r * theirs->od !=
+	       (u32)theirs->f * ours->r * ours->od;
+}
+
+static int dm_test_k210_pll(struct unit_test_state *uts)
+{
+	struct k210_pll_params ours, theirs;
+
+	/* General range checks */
+	ut_asserteq(-EINVAL, k210_pll_calc_params(0, 26000000, &theirs));
+	ut_asserteq(-EINVAL, k210_pll_calc_params(390000000, 0, &theirs));
+	ut_asserteq(-EINVAL, k210_pll_calc_params(2000000000, 26000000,
+						  &theirs));
+	ut_asserteq(-EINVAL, k210_pll_calc_params(390000000, 2000000000,
+						  &theirs));
+	ut_asserteq(-EINVAL, k210_pll_calc_params(1500000000, 20000000,
+						  &theirs));
+
+	/* Verify we get the same output with brute-force */
+	ut_assertok(dm_test_k210_pll_calc_params(390000000, 26000000, &ours));
+	ut_assertok(k210_pll_calc_params(390000000, 26000000, &theirs));
+	ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+	ut_assertok(dm_test_k210_pll_calc_params(26000000, 390000000, &ours));
+	ut_assertok(k210_pll_calc_params(26000000, 390000000, &theirs));
+	ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+	ut_assertok(dm_test_k210_pll_calc_params(400000000, 26000000, &ours));
+	ut_assertok(k210_pll_calc_params(400000000, 26000000, &theirs));
+	ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+	ut_assertok(dm_test_k210_pll_calc_params(27000000, 26000000, &ours));
+	ut_assertok(k210_pll_calc_params(27000000, 26000000, &theirs));
+	ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+	ut_assertok(dm_test_k210_pll_calc_params(26000000, 27000000, &ours));
+	ut_assertok(k210_pll_calc_params(26000000, 27000000, &theirs));
+	ut_assertok(dm_test_k210_pll_compare(&ours, &theirs));
+
+	return 0;
+}
+DM_TEST(dm_test_k210_pll, 0);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 06/33] clk: Add a bypass clock for K210
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (4 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 05/33] clk: Add K210 pll support Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 07/33] clk: Add K210 clock support Sean Anderson
                   ` (26 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This is a small driver to do a software bypass of a clock if hardware
bypass is not working. I have tried to write this in a generic fashion, so
that it could be potentially broken out of the kendryte code at some future
date. For the K210, it is used to have aclk bypass pll0 and use in0 instead
so that the CPU keeps on working.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Add function to register from a struct bypass

Changes in v4:
- New

 drivers/clk/kendryte/Makefile |   2 +-
 drivers/clk/kendryte/bypass.c | 270 ++++++++++++++++++++++++++++++++++
 include/kendryte/bypass.h     |  31 ++++
 3 files changed, 302 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/kendryte/bypass.c
 create mode 100644 include/kendryte/bypass.h

diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
index c56d93ea1c..47f682fce3 100644
--- a/drivers/clk/kendryte/Makefile
+++ b/drivers/clk/kendryte/Makefile
@@ -1 +1 @@
-obj-y += pll.o
+obj-y += bypass.o pll.o
diff --git a/drivers/clk/kendryte/bypass.c b/drivers/clk/kendryte/bypass.c
new file mode 100644
index 0000000000..eb3e27d055
--- /dev/null
+++ b/drivers/clk/kendryte/bypass.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <kendryte/bypass.h>
+
+#include <clk-uclass.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#define LOG_CATEGORY UCLASS_CLK
+#include <log.h>
+
+#define CLK_K210_BYPASS "k210_clk_bypass"
+
+/*
+ * This is a small driver to do a software bypass of a clock if hardware bypass
+ * is not working. I have tried to write this in a generic fashion, so that it
+ * could be potentially broken out of the kendryte code at some future date.
+ *
+ * Say you have the following clock configuration
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ *         ^
+ *        /|
+ *       / |
+ *      /  |
+ *     /   |
+ *    /    |
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * But the pll does not have a bypass, so when you configure the pll, the
+ * configuration needs to change to look like
+ *
+ * +---+ +---+
+ * |osc| |pll|
+ * +---+ +---+
+ *   ^
+ *   |\
+ *   | \
+ *   |  \
+ *   |   \
+ *   |    \
+ * +---+ +---+
+ * |clk| |clk|
+ * +---+ +---+
+ *
+ * To set this up, create a bypass clock with bypassee=pll and alt=osc. When
+ * creating the child clocks, set their parent to the bypass clock. After
+ * creating all the children, call k210_bypass_setchildren().
+ */
+
+static int k210_bypass_dobypass(struct k210_bypass *bypass)
+{
+	int ret, i;
+
+	/*
+	 * If we already have saved parents, then the children are already
+	 * bypassed
+	 */
+	if (bypass->child_count && bypass->saved_parents[0])
+		return 0;
+
+	for (i = 0; i < bypass->child_count; i++) {
+		struct clk *child = bypass->children[i];
+		struct clk *parent = clk_get_parent(child);
+
+		if (IS_ERR(parent)) {
+			for (; i; i--)
+				bypass->saved_parents[i] = NULL;
+			return PTR_ERR(parent);
+		}
+		bypass->saved_parents[i] = parent;
+	}
+
+	for (i = 0; i < bypass->child_count; i++) {
+		struct clk *child = bypass->children[i];
+
+		ret = clk_set_parent(child, bypass->alt);
+		if (ret) {
+			for (; i; i--)
+				clk_set_parent(bypass->children[i],
+					       bypass->saved_parents[i]);
+			for (i = 0; i < bypass->child_count; i++)
+				bypass->saved_parents[i] = NULL;
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int k210_bypass_unbypass(struct k210_bypass *bypass)
+{
+	int err, ret, i;
+
+	if (!bypass->child_count && !bypass->saved_parents[0]) {
+		log_warning("Cannot unbypass children; dobypass not called first\n");
+		return 0;
+	}
+
+	ret = 0;
+	for (i = 0; i < bypass->child_count; i++) {
+		err = clk_set_parent(bypass->children[i],
+				     bypass->saved_parents[i]);
+		if (err)
+			ret = err;
+		bypass->saved_parents[i] = NULL;
+	}
+	return ret;
+}
+
+static ulong k210_bypass_get_rate(struct clk *clk)
+{
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+	const struct clk_ops *ops = bypass->bypassee_ops;
+
+	if (ops->get_rate)
+		return ops->get_rate(bypass->bypassee);
+	else
+		return clk_get_parent_rate(bypass->bypassee);
+}
+
+static ulong k210_bypass_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret;
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+	const struct clk_ops *ops = bypass->bypassee_ops;
+
+	/* Don't bother bypassing if we aren't going to set the rate */
+	if (!ops->set_rate)
+		return k210_bypass_get_rate(clk);
+
+	ret = k210_bypass_dobypass(bypass);
+	if (ret)
+		return ret;
+
+	ret = ops->set_rate(bypass->bypassee, rate);
+	if (ret < 0)
+		return ret;
+
+	return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+	const struct clk_ops *ops = bypass->bypassee_ops;
+
+	if (ops->set_parent)
+		return ops->set_parent(bypass->bypassee, parent);
+	else
+		return -ENOTSUPP;
+}
+
+/*
+ * For these next two functions, do the bypassing even if there is no
+ * en-/-disable function, since the bypassing itself can be observed in between
+ * calls.
+ */
+static int k210_bypass_enable(struct clk *clk)
+{
+	int ret;
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+	const struct clk_ops *ops = bypass->bypassee_ops;
+
+	ret = k210_bypass_dobypass(bypass);
+	if (ret)
+		return ret;
+
+	if (ops->enable)
+		ret = ops->enable(bypass->bypassee);
+	else
+		ret = 0;
+	if (ret)
+		return ret;
+
+	return k210_bypass_unbypass(bypass);
+}
+
+static int k210_bypass_disable(struct clk *clk)
+{
+	int ret;
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+	const struct clk_ops *ops = bypass->bypassee_ops;
+
+	ret = k210_bypass_dobypass(bypass);
+	if (ret)
+		return ret;
+
+	if (ops->disable)
+		return ops->disable(bypass->bypassee);
+	else
+		return 0;
+}
+
+static const struct clk_ops k210_bypass_ops = {
+	.get_rate = k210_bypass_get_rate,
+	.set_rate = k210_bypass_set_rate,
+	.set_parent = k210_bypass_set_parent,
+	.enable = k210_bypass_enable,
+	.disable = k210_bypass_disable,
+};
+
+int k210_bypass_set_children(struct clk *clk, struct clk **children,
+			     size_t child_count)
+{
+	struct k210_bypass *bypass = to_k210_bypass(clk);
+
+	kfree(bypass->saved_parents);
+	if (child_count) {
+		bypass->saved_parents =
+			kcalloc(child_count, sizeof(struct clk *), GFP_KERNEL);
+		if (!bypass->saved_parents)
+			return -ENOMEM;
+	}
+	bypass->child_count = child_count;
+	bypass->children = children;
+
+	return 0;
+}
+
+struct clk *k210_register_bypass_struct(const char *name,
+					const char *parent_name,
+					struct k210_bypass *bypass)
+{
+	int ret;
+	struct clk *clk;
+
+	clk = &bypass->clk;
+
+	ret = clk_register(clk, CLK_K210_BYPASS, name, parent_name);
+	if (ret)
+		return ERR_PTR(ret);
+
+	bypass->bypassee->dev = clk->dev;
+	return clk;
+}
+
+struct clk *k210_register_bypass(const char *name, const char *parent_name,
+				 struct clk *bypassee,
+				 const struct clk_ops *bypassee_ops,
+				 struct clk *alt)
+{
+	struct clk *clk;
+	struct k210_bypass *bypass;
+
+	bypass = kzalloc(sizeof(*bypass), GFP_KERNEL);
+	if (!bypass)
+		return ERR_PTR(-ENOMEM);
+
+	bypass->bypassee = bypassee;
+	bypass->bypassee_ops = bypassee_ops;
+	bypass->alt = alt;
+
+	clk = k210_register_bypass_struct(name, parent_name, bypass);
+	if (IS_ERR(clk))
+		kfree(bypass);
+	return clk;
+}
+
+U_BOOT_DRIVER(k210_bypass) = {
+	.name	= CLK_K210_BYPASS,
+	.id	= UCLASS_CLK,
+	.ops	= &k210_bypass_ops,
+};
diff --git a/include/kendryte/bypass.h b/include/kendryte/bypass.h
new file mode 100644
index 0000000000..a081cbd12f
--- /dev/null
+++ b/include/kendryte/bypass.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+#ifndef K210_BYPASS_H
+#define K210_BYPASS_H
+
+#include <clk.h>
+
+struct k210_bypass {
+	struct clk clk;
+	struct clk **children; /* Clocks to reparent */
+	struct clk **saved_parents; /* Parents saved over en-/dis-able */
+	struct clk *bypassee; /* Clock to bypass */
+	const struct clk_ops *bypassee_ops; /* Ops of the bypass clock */
+	struct clk *alt; /* Clock to set children to when bypassing */
+	size_t child_count;
+};
+
+#define to_k210_bypass(_clk) container_of(_clk, struct k210_bypass, clk)
+
+int k210_bypass_set_children(struct clk *clk, struct clk **children,
+			     size_t child_count);
+struct clk *k210_register_bypass_struct(const char *name,
+					const char *parent_name,
+					struct k210_bypass *bypass);
+struct clk *k210_register_bypass(const char *name, const char *parent_name,
+				 struct clk *bypassee,
+				 const struct clk_ops *bypassee_ops,
+				 struct clk *alt);
+#endif /* K210_BYPASS_H */
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 07/33] clk: Add K210 clock support
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (5 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 06/33] clk: Add a bypass clock for K210 Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:58   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 08/33] doc: Fix typo in FIT documentation Sean Anderson
                   ` (25 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Due to the large number of clocks, I decided to use the CCF. The overall
structure is modeled after the imx code. Clocks are stored in several
arrays.  There are some translation macros (FOOIFY()) which allow for more
dense packing.  A possible improvement could be to only store the
parameters we need, instead of the whole CCF struct.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Don't unmap priv->reg
- Remove comment on APB clocks since it has been clarified by Kendryte
- Add i2s mclks
- Reorder clock ids to be continuous
- Rewrite to statically allocate all clocks. This has helped find several bugs
  (since it is easy to see when a clock has the wrong register).
- Fix ACLK sometimes having the wrong parent
- Fix SPI3 having the wrong divider
- Prevent being probed multiple times on failure

Changes in v4:
- Reparent aclk before configuring pll0
- Update copyright
- Lint

Changes in v3:
- Removed sysctl struct, replacing it with defines. This is to have the same
  interface to sysctl from C as from the device tree.
- Fixed clocks having the same id
- Fixed clocks not using the correct register/bits
- Aligned the defines in headers

Changes in v2:
- Add clk.o to obj-y
- Don't probe before relocation

 MAINTAINERS                                   |   7 +
 .../mfd/kendryte,k210-sysctl.txt              |  33 ++
 drivers/clk/kendryte/Kconfig                  |   2 +-
 drivers/clk/kendryte/Makefile                 |   2 +-
 drivers/clk/kendryte/clk.c                    | 478 ++++++++++++++++++
 include/dt-bindings/clock/k210-sysctl.h       |  56 ++
 include/dt-bindings/mfd/k210-sysctl.h         |  38 ++
 include/kendryte/clk.h                        |  35 ++
 8 files changed, 649 insertions(+), 2 deletions(-)
 create mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
 create mode 100644 drivers/clk/kendryte/clk.c
 create mode 100644 include/dt-bindings/clock/k210-sysctl.h
 create mode 100644 include/dt-bindings/mfd/k210-sysctl.h
 create mode 100644 include/kendryte/clk.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 82e4159bec..8e9e0569ba 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -805,6 +805,13 @@ F:	arch/riscv/
 F:	cmd/riscv/
 F:	tools/prelink-riscv.c
 
+RISC-V KENDRYTE
+M:	Sean Anderson <seanga2@gmail.com>
+S:	Maintained
+F:	doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
+F:	drivers/clk/kendryte/
+F:	include/kendryte/
+
 RNG
 M:	Sughosh Ganu <sughosh.ganu@linaro.org>
 R:	Heinrich Schuchardt <xypron.glpk@gmx.de>
diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
new file mode 100644
index 0000000000..5b24abcb62
--- /dev/null
+++ b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
@@ -0,0 +1,33 @@
+Kendryte K210 Sysctl
+
+This binding describes the K210 sysctl device, which contains many miscellaneous
+registers controlling system functionality. This node is a register map and can
+be reference by other bindings which need a phandle to the K210 sysctl regmap.
+
+Required properties:
+- compatible: should be
+	"kendryte,k210-sysctl", "syscon", "simple-mfd"
+- reg: address and length of the sysctl registers
+- reg-io-width: must be <4>
+
+Clock sub-node
+
+This node is a binding for the clock tree driver
+
+Required properties:
+- compatible: should be "kendryte,k210-clk"
+- clocks: phandle to the "in0" external oscillator
+- #clock-cells: must be <1>
+
+Example:
+sysctl: syscon at 50440000 {
+	compatible = "kendryte,k210-sysctl", "syscon", "simple-mfd";
+	reg = <0x50440000 0x100>;
+	reg-io-width = <4>;
+
+	sysclk: clock-controller {
+		compatible = "kendryte,k210-clk";
+		clocks = <&in0>;
+		#clock-cells = <1>;
+	};
+};
diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig
index 7b69c8afaf..073fca0781 100644
--- a/drivers/clk/kendryte/Kconfig
+++ b/drivers/clk/kendryte/Kconfig
@@ -1,6 +1,6 @@
 config CLK_K210
 	bool "Clock support for Kendryte K210"
-	depends on CLK && CLK_CCF
+	depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
 	help
 	  This enables support clock driver for Kendryte K210 platforms.
 
diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
index 47f682fce3..6fb68253ae 100644
--- a/drivers/clk/kendryte/Makefile
+++ b/drivers/clk/kendryte/Makefile
@@ -1 +1 @@
-obj-y += bypass.o pll.o
+obj-y += bypass.o clk.o pll.o
diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c
new file mode 100644
index 0000000000..b01d246682
--- /dev/null
+++ b/drivers/clk/kendryte/clk.c
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+#include <kendryte/clk.h>
+
+#include <asm/io.h>
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <dt-bindings/mfd/k210-sysctl.h>
+#include <dm.h>
+#include <log.h>
+#include <mapmem.h>
+
+#include <kendryte/bypass.h>
+#include <kendryte/pll.h>
+
+static ulong k210_clk_get_rate(struct clk *clk)
+{
+	struct clk *c;
+	int err = clk_get_by_id(clk->id, &c);
+
+	if (err)
+		return err;
+	return clk_get_rate(c);
+}
+
+static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	struct clk *c;
+	int err = clk_get_by_id(clk->id, &c);
+
+	if (err)
+		return err;
+	return clk_set_rate(c, rate);
+}
+
+static int k210_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk *c, *p;
+	int err = clk_get_by_id(clk->id, &c);
+
+	if (err)
+		return err;
+
+	err = clk_get_by_id(parent->id, &p);
+	if (err)
+		return err;
+
+	return clk_set_parent(c, p);
+}
+
+static int k210_clk_endisable(struct clk *clk, bool enable)
+{
+	struct clk *c;
+	int err = clk_get_by_id(clk->id, &c);
+
+	if (err)
+		return err;
+	return enable ? clk_enable(c) : clk_disable(c);
+}
+
+static int k210_clk_enable(struct clk *clk)
+{
+	return k210_clk_endisable(clk, true);
+}
+
+static int k210_clk_disable(struct clk *clk)
+{
+	return k210_clk_endisable(clk, false);
+}
+
+static const struct clk_ops k210_clk_ops = {
+	.set_rate = k210_clk_set_rate,
+	.get_rate = k210_clk_get_rate,
+	.set_parent = k210_clk_set_parent,
+	.enable = k210_clk_enable,
+	.disable = k210_clk_disable,
+};
+
+static const char * const generic_sels[] = { "in0_half", "pll0_half" };
+/* The first clock is in0, which is filled in by k210_clk_probe */
+static const char *aclk_sels[] = { NULL, "pll0_half" };
+static const char *pll2_sels[] = { NULL, "pll0", "pll1" };
+
+#define DIV(id, reg, shift, width) DIV_FLAGS(id, reg, shift, width, 0)
+#define DIV_LIST \
+	DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \
+		  CLK_DIVIDER_POWER_OF_TWO) \
+	DIV(K210_CLK_APB0,   K210_SYSCTL_SEL0,  3,  3) \
+	DIV(K210_CLK_APB1,   K210_SYSCTL_SEL0,  6,  3) \
+	DIV(K210_CLK_APB2,   K210_SYSCTL_SEL0,  9,  3) \
+	DIV(K210_CLK_SRAM0,  K210_SYSCTL_THR0,  0,  4) \
+	DIV(K210_CLK_SRAM1,  K210_SYSCTL_THR0,  4,  4) \
+	DIV(K210_CLK_AI,     K210_SYSCTL_THR0,  8,  4) \
+	DIV(K210_CLK_DVP,    K210_SYSCTL_THR0, 12,  4) \
+	DIV(K210_CLK_ROM,    K210_SYSCTL_THR0, 16,  4) \
+	DIV(K210_CLK_SPI0,   K210_SYSCTL_THR1,  0,  8) \
+	DIV(K210_CLK_SPI1,   K210_SYSCTL_THR1,  8,  8) \
+	DIV(K210_CLK_SPI2,   K210_SYSCTL_THR1, 16,  8) \
+	DIV(K210_CLK_SPI3,   K210_SYSCTL_THR1, 24,  8) \
+	DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2,  0,  8) \
+	DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2,  8,  8) \
+	DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16,  8) \
+	DIV(K210_CLK_I2S0,   K210_SYSCTL_THR3,  0, 16) \
+	DIV(K210_CLK_I2S1,   K210_SYSCTL_THR3, 16, 16) \
+	DIV(K210_CLK_I2S2,   K210_SYSCTL_THR4,  0, 16) \
+	DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16,  8) \
+	DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24,  8) \
+	DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4,  0,  8) \
+	DIV(K210_CLK_I2C0,   K210_SYSCTL_THR5,  8,  8) \
+	DIV(K210_CLK_I2C1,   K210_SYSCTL_THR5, 16,  8) \
+	DIV(K210_CLK_I2C2,   K210_SYSCTL_THR5, 24,  8) \
+	DIV(K210_CLK_WDT0,   K210_SYSCTL_THR6,  0,  8) \
+	DIV(K210_CLK_WDT1,   K210_SYSCTL_THR6,  8,  8)
+
+#define _DIVIFY(id) K210_CLK_DIV_##id
+#define DIVIFY(id) _DIVIFY(id)
+#define DIV_FLAGS(id, ...) DIVIFY(id),
+enum k210_clk_div_ids {
+	DIV_LIST
+};
+#undef DIV_FLAGS
+
+#define DIV_FLAGS(id, _reg, _shift, _width, _flags) [DIVIFY(id)] = { \
+	.reg = (void *)(_reg), \
+	.shift = (_shift), \
+	.width = (_width), \
+	.flags = (_flags), \
+},
+static struct clk_divider k210_clk_dividers[]  = {
+	DIV_LIST
+};
+#undef DIV_FLAGS
+#undef DIV
+#undef DIV_LIST
+
+#define GATE_LIST \
+	GATE(K210_CLK_CPU,    K210_SYSCTL_EN_CENT,  0) \
+	GATE(K210_CLK_SRAM0,  K210_SYSCTL_EN_CENT,  1) \
+	GATE(K210_CLK_SRAM1,  K210_SYSCTL_EN_CENT,  2) \
+	GATE(K210_CLK_APB0,   K210_SYSCTL_EN_CENT,  3) \
+	GATE(K210_CLK_APB1,   K210_SYSCTL_EN_CENT,  4) \
+	GATE(K210_CLK_APB2,   K210_SYSCTL_EN_CENT,  5) \
+	GATE(K210_CLK_ROM,    K210_SYSCTL_EN_PERI,  0) \
+	GATE(K210_CLK_DMA,    K210_SYSCTL_EN_PERI,  1) \
+	GATE(K210_CLK_AI,     K210_SYSCTL_EN_PERI,  2) \
+	GATE(K210_CLK_DVP,    K210_SYSCTL_EN_PERI,  3) \
+	GATE(K210_CLK_FFT,    K210_SYSCTL_EN_PERI,  4) \
+	GATE(K210_CLK_GPIO,   K210_SYSCTL_EN_PERI,  5) \
+	GATE(K210_CLK_SPI0,   K210_SYSCTL_EN_PERI,  6) \
+	GATE(K210_CLK_SPI1,   K210_SYSCTL_EN_PERI,  7) \
+	GATE(K210_CLK_SPI2,   K210_SYSCTL_EN_PERI,  8) \
+	GATE(K210_CLK_SPI3,   K210_SYSCTL_EN_PERI,  9) \
+	GATE(K210_CLK_I2S0,   K210_SYSCTL_EN_PERI, 10) \
+	GATE(K210_CLK_I2S1,   K210_SYSCTL_EN_PERI, 11) \
+	GATE(K210_CLK_I2S2,   K210_SYSCTL_EN_PERI, 12) \
+	GATE(K210_CLK_I2C0,   K210_SYSCTL_EN_PERI, 13) \
+	GATE(K210_CLK_I2C1,   K210_SYSCTL_EN_PERI, 14) \
+	GATE(K210_CLK_I2C2,   K210_SYSCTL_EN_PERI, 15) \
+	GATE(K210_CLK_UART1,  K210_SYSCTL_EN_PERI, 16) \
+	GATE(K210_CLK_UART2,  K210_SYSCTL_EN_PERI, 17) \
+	GATE(K210_CLK_UART3,  K210_SYSCTL_EN_PERI, 18) \
+	GATE(K210_CLK_AES,    K210_SYSCTL_EN_PERI, 19) \
+	GATE(K210_CLK_FPIOA,  K210_SYSCTL_EN_PERI, 20) \
+	GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \
+	GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \
+	GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \
+	GATE(K210_CLK_WDT0,   K210_SYSCTL_EN_PERI, 24) \
+	GATE(K210_CLK_WDT1,   K210_SYSCTL_EN_PERI, 25) \
+	GATE(K210_CLK_SHA,    K210_SYSCTL_EN_PERI, 26) \
+	GATE(K210_CLK_OTP,    K210_SYSCTL_EN_PERI, 27) \
+	GATE(K210_CLK_RTC,    K210_SYSCTL_EN_PERI, 29)
+
+#define _GATEIFY(id) K210_CLK_GATE_##id
+#define GATEIFY(id) _GATEIFY(id)
+#define GATE(id, ...) GATEIFY(id),
+enum k210_clk_gate_ids {
+	GATE_LIST
+};
+#undef GATE
+
+#define GATE(id, _reg, _idx) [GATEIFY(id)] = { \
+	.reg = (void *)(_reg), \
+	.bit_idx = (_idx), \
+},
+static struct clk_gate k210_clk_gates[] = {
+	GATE_LIST
+};
+#undef GATE
+#undef GATE_LIST
+
+#define MUX(id, reg, shift, width) \
+	MUX_PARENTS(id, generic_sels, reg, shift, width)
+#define MUX_LIST \
+	MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \
+	MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0,  0, 1) \
+	MUX(K210_CLK_SPI3,   K210_SYSCTL_SEL0, 12, 1) \
+	MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \
+	MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \
+	MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1)
+
+#define _MUXIFY(id) K210_CLK_MUX_##id
+#define MUXIFY(id) _MUXIFY(id)
+#define MUX_PARENTS(id, ...) MUXIFY(id),
+enum k210_clk_mux_ids {
+	MUX_LIST
+};
+#undef MUX_PARENTS
+
+#define MUX_PARENTS(id, parents, _reg, _shift, _width) [MUXIFY(id)] = { \
+	.parent_names = (const char * const *)(parents), \
+	.num_parents = ARRAY_SIZE(parents), \
+	.reg = (void *)(_reg), \
+	.shift = (_shift), \
+	.mask = BIT(_width) - 1, \
+},
+static struct clk_mux k210_clk_muxes[] = {
+	MUX_LIST
+};
+#undef MUX_PARENTS
+#undef MUX
+#undef MUX_LIST
+
+#define PLL(_reg, _shift, _width) { \
+	.reg = (void *)(_reg), \
+	.lock = (void *)K210_SYSCTL_PLL_LOCK, \
+	.shift = (_shift), \
+	.width = (_width), \
+}
+static struct k210_pll k210_clk_plls[] = {
+	[0] = PLL(K210_SYSCTL_PLL0,  0, 2),
+	[1] = PLL(K210_SYSCTL_PLL1,  8, 1),
+	[2] = PLL(K210_SYSCTL_PLL2, 16, 1),
+};
+#undef PLL
+
+#define COMP(id, mux, div, gate) \
+	COMP_FULL(id, &(mux)->clk, &clk_mux_ops, \
+		  &(div)->clk, &clk_divider_ops, \
+		  &(gate)->clk, &clk_gate_ops)
+#define COMP_ID(id) \
+	COMP(id, &k210_clk_muxes[MUXIFY(id)], \
+	     &k210_clk_dividers[DIVIFY(id)], \
+	     &k210_clk_gates[GATEIFY(id)])
+#define COMP_NOMUX(id, div, gate) \
+	COMP_FULL(id, NULL, NULL, \
+		  &(div)->clk, &clk_divider_ops, \
+		  &(gate)->clk, &clk_gate_ops)
+#define COMP_NOMUX_ID(id) \
+	COMP_NOMUX(id, &k210_clk_dividers[DIVIFY(id)], \
+		   &k210_clk_gates[GATEIFY(id)])
+#define COMP_LIST \
+	COMP_FULL(K210_CLK_PLL2, \
+		  &k210_clk_muxes[MUXIFY(K210_CLK_PLL2)].clk, &clk_mux_ops, \
+		  &k210_clk_plls[2].clk, &k210_pll_ops, \
+		  &k210_clk_plls[2].clk, &k210_pll_ops) \
+	COMP_FULL(K210_CLK_ACLK, \
+		  &k210_clk_muxes[MUXIFY(K210_CLK_ACLK)].clk, &clk_mux_ops, \
+		  &k210_clk_dividers[DIVIFY(K210_CLK_ACLK)].clk, \
+		  &clk_divider_ops, \
+		  NULL, NULL) \
+	COMP_ID(K210_CLK_SPI3) \
+	COMP_ID(K210_CLK_TIMER0) \
+	COMP_ID(K210_CLK_TIMER1) \
+	COMP_ID(K210_CLK_TIMER2) \
+	COMP_NOMUX_ID(K210_CLK_SRAM0) \
+	COMP_NOMUX_ID(K210_CLK_SRAM1) \
+	COMP_NOMUX_ID(K210_CLK_ROM) \
+	COMP_NOMUX_ID(K210_CLK_DVP) \
+	COMP_NOMUX_ID(K210_CLK_APB0) \
+	COMP_NOMUX_ID(K210_CLK_APB1) \
+	COMP_NOMUX_ID(K210_CLK_APB2) \
+	COMP_NOMUX_ID(K210_CLK_AI) \
+	COMP_NOMUX_ID(K210_CLK_I2S0) \
+	COMP_NOMUX_ID(K210_CLK_I2S1) \
+	COMP_NOMUX_ID(K210_CLK_I2S2) \
+	COMP_NOMUX_ID(K210_CLK_WDT0) \
+	COMP_NOMUX_ID(K210_CLK_WDT1) \
+	COMP_NOMUX_ID(K210_CLK_SPI0) \
+	COMP_NOMUX_ID(K210_CLK_SPI1) \
+	COMP_NOMUX_ID(K210_CLK_SPI2) \
+	COMP_NOMUX_ID(K210_CLK_I2C0) \
+	COMP_NOMUX_ID(K210_CLK_I2C1) \
+	COMP_NOMUX_ID(K210_CLK_I2C2)
+
+#define _COMPIFY(id) K210_CLK_COMP_##id
+#define COMPIFY(id) _COMPIFY(id)
+#define COMP_FULL(id, ...) COMPIFY(id),
+enum k210_clk_comp_ids {
+	COMP_LIST
+};
+#undef COMP_FULL
+
+#define COMP_FULL(id, _mux, _mux_ops, _div, _div_ops, _gate, _gate_ops) \
+[COMPIFY(id)] = { \
+	.mux = (_mux), \
+	.mux_ops = (_mux_ops), \
+	.rate = (_div), \
+	.rate_ops = (_div_ops), \
+	.gate = (_gate), \
+	.gate_ops = (_gate_ops), \
+},
+static struct clk_composite k210_clk_comps[] = {
+	COMP_LIST
+};
+#undef COMP_FULL
+#undef COMP
+#undef COMP_ID
+#undef COMP_NOMUX
+#undef COMP_NOMUX_ID
+#undef COMP_LIST
+
+static struct clk *k210_clk_bypass_children = {
+	&k210_clk_comps[COMPIFY(K210_CLK_ACLK)].clk,
+};
+
+static struct clk *k210_clk_bypass_saved_parents = {
+	NULL,
+};
+
+static struct k210_bypass k210_clk_bypass = {
+	.bypassee = &k210_clk_plls[0].clk,
+	.bypassee_ops = &k210_pll_ops,
+	.children = &k210_clk_bypass_children,
+	.child_count = 1,
+	.saved_parents = &k210_clk_bypass_saved_parents,
+};
+
+static bool probed = false;
+
+static int k210_clk_probe(struct udevice *dev)
+{
+	int ret, i;
+	const char *in0;
+	struct clk *in0_clk;
+	void *base;
+
+	/* Only one instance of this driver allowed */
+	if (READ_ONCE(probed))
+		return -ENOTSUPP;
+
+	base = dev_read_addr_ptr(dev_get_parent(dev));
+	if (!base)
+		return -EINVAL;
+
+	in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL);
+	if (!in0_clk)
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 0, in0_clk);
+	if (ret)
+		return ret;
+	in0 = in0_clk->dev->name;
+
+	WRITE_ONCE(probed, true);
+
+	aclk_sels[0] = in0;
+	pll2_sels[0] = in0;
+
+	/* Fixup registers to be absolute, rather than relative */
+#define FIXUP_REGS(clocks) \
+	for (i = 0; i < ARRAY_SIZE(clocks); i++) \
+		clocks[i].reg += (ulong)base
+	FIXUP_REGS(k210_clk_dividers);
+	FIXUP_REGS(k210_clk_gates);
+	FIXUP_REGS(k210_clk_muxes);
+#undef FIXUP_REGS
+	for (i = 0; i < ARRAY_SIZE(k210_clk_plls); i++) {
+		k210_clk_plls[i].reg += (ulong)base;
+		k210_clk_plls[i].lock += (ulong)base;
+	}
+
+	/*
+	 * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we
+	 * need to manually reparent it whenever we configure pll0
+	 */
+	k210_clk_bypass.alt = in0_clk;
+	clk_dm(K210_CLK_PLL0,
+	       k210_register_bypass_struct("pll0", in0, &k210_clk_bypass));
+	clk_dm(K210_CLK_PLL1,
+	       k210_register_pll_struct("pll1", in0, &k210_clk_plls[1]));
+	/* PLL2 is muxed, so set up a composite clock */
+	clk_dm(K210_CLK_PLL2,
+	       clk_register_composite_struct("pll2", pll2_sels,
+					     ARRAY_SIZE(pll2_sels),
+					     &k210_clk_comps[COMPIFY(K210_CLK_PLL2)]));
+
+	/* Half-frequency clocks for "even" dividers */
+	k210_clk_half("in0_half", in0);
+	k210_clk_half("pll0_half", "pll0");
+	k210_clk_half("pll2_half", "pll2");
+
+	/* ACLK has no gate */
+	clk_dm(K210_CLK_ACLK,
+	       clk_register_composite_struct("aclk", aclk_sels,
+					     ARRAY_SIZE(aclk_sels),
+					     &k210_clk_comps[COMPIFY(K210_CLK_ACLK)]));
+
+#define REGISTER_COMP(id, name) \
+	clk_dm(id, clk_register_composite_struct(name, generic_sels, \
+						 ARRAY_SIZE(generic_sels), \
+						 &k210_clk_comps[COMPIFY(id)]))
+	REGISTER_COMP(K210_CLK_SPI3,   "spi3");
+	REGISTER_COMP(K210_CLK_TIMER0, "timer0");
+	REGISTER_COMP(K210_CLK_TIMER1, "timer1");
+	REGISTER_COMP(K210_CLK_TIMER2, "timer2");
+#undef COMP
+
+	/* Dividing clocks, no mux */
+#define REGISTER_COMP_NOMUX(id, name, _parent) do { \
+	const char *parent = _parent; \
+	clk_dm(id, \
+	       clk_register_composite_struct(name, &parent, 1, \
+					     &k210_clk_comps[COMPIFY(id)])); \
+} while (false)
+	REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0",  "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1",  "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_ROM,   "rom",    "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_DVP,   "dvp",    "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_APB0,  "apb0",   "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_APB1,  "apb1",   "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_APB2,  "apb2",   "aclk");
+	REGISTER_COMP_NOMUX(K210_CLK_AI,    "ai",     "pll1");
+	REGISTER_COMP_NOMUX(K210_CLK_I2S0,  "i2s0",   "pll2_half");
+	REGISTER_COMP_NOMUX(K210_CLK_I2S1,  "i2s1",   "pll2_half");
+	REGISTER_COMP_NOMUX(K210_CLK_I2S2,  "i2s2",   "pll2_half");
+	REGISTER_COMP_NOMUX(K210_CLK_WDT0,  "wdt0",   "in0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_WDT1,  "wdt1",   "in0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_SPI0,  "spi0",   "pll0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_SPI1,  "spi1",   "pll0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_SPI2,  "spi2",   "pll0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_I2C0,  "i2c0",   "pll0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_I2C1,  "i2c1",   "pll0_half");
+	REGISTER_COMP_NOMUX(K210_CLK_I2C2,  "i2c2",   "pll0_half");
+#undef REGISTER_COMP_NOMUX
+
+	/* Dividing clocks */
+#define REGISTER_DIV(id, name, parent) clk_dm(id, \
+	clk_register_divider_struct(name, parent, \
+				    &k210_clk_dividers[DIVIFY(id)]))
+	REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half");
+	REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half");
+	REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half");
+#undef REGISTER_DIV
+
+	/* Gated clocks */
+#define REGISTER_GATE(id, name, parent) \
+	clk_dm(id, clk_register_gate_struct(name, parent, \
+					    &k210_clk_gates[GATEIFY(id)]))
+	REGISTER_GATE(K210_CLK_CPU,   "cpu",    "aclk");
+	REGISTER_GATE(K210_CLK_DMA,   "dma",    "aclk");
+	REGISTER_GATE(K210_CLK_FFT,   "fft",    "aclk");
+	REGISTER_GATE(K210_CLK_GPIO,  "gpio",   "apb0");
+	REGISTER_GATE(K210_CLK_UART1, "uart1",  "apb0");
+	REGISTER_GATE(K210_CLK_UART2, "uart2",  "apb0");
+	REGISTER_GATE(K210_CLK_UART3, "uart3",  "apb0");
+	REGISTER_GATE(K210_CLK_FPIOA, "fpioa",  "apb0");
+	REGISTER_GATE(K210_CLK_SHA,   "sha",    "apb0");
+	REGISTER_GATE(K210_CLK_AES,   "aes",    "apb1");
+	REGISTER_GATE(K210_CLK_OTP,   "otp",    "apb1");
+	REGISTER_GATE(K210_CLK_RTC,   "rtc",    in0);
+#undef REGISTER_GATE
+
+	return 0;
+}
+
+static const struct udevice_id k210_clk_ids[] = {
+	{ .compatible = "kendryte,k210-clk" },
+	{ },
+};
+
+U_BOOT_DRIVER(k210_clk) = {
+	.name = "k210_clk",
+	.id = UCLASS_CLK,
+	.of_match = k210_clk_ids,
+	.ops = &k210_clk_ops,
+	.probe = k210_clk_probe,
+};
diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h
new file mode 100644
index 0000000000..16d67b282f
--- /dev/null
+++ b/include/dt-bindings/clock/k210-sysctl.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef CLOCK_K210_SYSCTL_H
+#define CLOCK_K210_SYSCTL_H
+
+/*
+ * Arbitrary identifiers for clocks.
+ */
+#define K210_CLK_NONE   0
+#define K210_CLK_PLL0   1
+#define K210_CLK_PLL1   2
+#define K210_CLK_PLL2   3
+#define K210_CLK_CPU    4
+#define K210_CLK_SRAM0  5
+#define K210_CLK_SRAM1  6
+#define K210_CLK_APB0   7
+#define K210_CLK_APB1   8
+#define K210_CLK_APB2   9
+#define K210_CLK_ROM    10
+#define K210_CLK_DMA    11
+#define K210_CLK_AI     12
+#define K210_CLK_DVP    13
+#define K210_CLK_FFT    14
+#define K210_CLK_GPIO   15
+#define K210_CLK_SPI0   16
+#define K210_CLK_SPI1   17
+#define K210_CLK_SPI2   18
+#define K210_CLK_SPI3   19
+#define K210_CLK_I2S0   20
+#define K210_CLK_I2S1   21
+#define K210_CLK_I2S2   22
+#define K210_CLK_I2S0_M 23
+#define K210_CLK_I2S1_M 24
+#define K210_CLK_I2S2_M 25
+#define K210_CLK_I2C0   26
+#define K210_CLK_I2C1   27
+#define K210_CLK_I2C2   28
+#define K210_CLK_UART1  29
+#define K210_CLK_UART2  30
+#define K210_CLK_UART3  31
+#define K210_CLK_AES    32
+#define K210_CLK_FPIOA  33
+#define K210_CLK_TIMER0 34
+#define K210_CLK_TIMER1 35
+#define K210_CLK_TIMER2 36
+#define K210_CLK_WDT0   37
+#define K210_CLK_WDT1   38
+#define K210_CLK_SHA    39
+#define K210_CLK_OTP    40
+#define K210_CLK_RTC    41
+#define K210_CLK_ACLK   42
+
+#endif /* CLOCK_K210_SYSCTL_H */
diff --git a/include/dt-bindings/mfd/k210-sysctl.h b/include/dt-bindings/mfd/k210-sysctl.h
new file mode 100644
index 0000000000..e16d7302cd
--- /dev/null
+++ b/include/dt-bindings/mfd/k210-sysctl.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef K210_SYSCTL_H
+#define K210_SYSCTL_H
+
+/* Taken from kendryte-standalone-sdk/lib/drivers/include/sysctl.h */
+#define K210_SYSCTL_GIT_ID     0x00 /* Git short commit id */
+#define K210_SYSCTL_CLK_FREQ   0x04 /* System clock base frequency */
+#define K210_SYSCTL_PLL0       0x08 /* PLL0 controller */
+#define K210_SYSCTL_PLL1       0x0C /* PLL1 controller */
+#define K210_SYSCTL_PLL2       0x10 /* PLL2 controller */
+#define K210_SYSCTL_PLL_LOCK   0x18 /* PLL lock tester */
+#define K210_SYSCTL_ROM_ERROR  0x1C /* AXI ROM detector */
+#define K210_SYSCTL_SEL0       0x20 /* Clock select controller0 */
+#define K210_SYSCTL_SEL1       0x24 /* Clock select controller1 */
+#define K210_SYSCTL_EN_CENT    0x28 /* Central clock enable */
+#define K210_SYSCTL_EN_PERI    0x2C /* Peripheral clock enable */
+#define K210_SYSCTL_SOFT_RESET 0x30 /* Soft reset ctrl */
+#define K210_SYSCTL_PERI_RESET 0x34 /* Peripheral reset controller */
+#define K210_SYSCTL_THR0       0x38 /* Clock threshold controller 0 */
+#define K210_SYSCTL_THR1       0x3C /* Clock threshold controller 1 */
+#define K210_SYSCTL_THR2       0x40 /* Clock threshold controller 2 */
+#define K210_SYSCTL_THR3       0x44 /* Clock threshold controller 3 */
+#define K210_SYSCTL_THR4       0x48 /* Clock threshold controller 4 */
+#define K210_SYSCTL_THR5       0x4C /* Clock threshold controller 5 */
+#define K210_SYSCTL_THR6       0x50 /* Clock threshold controller 6 */
+#define K210_SYSCTL_MISC       0x54 /* Miscellaneous controller */
+#define K210_SYSCTL_PERI       0x58 /* Peripheral controller */
+#define K210_SYSCTL_SPI_SLEEP  0x5C /* SPI sleep controller */
+#define K210_SYSCTL_RESET_STAT 0x60 /* Reset source status */
+#define K210_SYSCTL_DMA_SEL0   0x64 /* DMA handshake selector */
+#define K210_SYSCTL_DMA_SEL1   0x68 /* DMA handshake selector */
+#define K210_SYSCTL_POWER_SEL  0x6C /* IO Power Mode Select controller */
+
+#endif /* K210_SYSCTL_H */
diff --git a/include/kendryte/clk.h b/include/kendryte/clk.h
new file mode 100644
index 0000000000..9c6245d468
--- /dev/null
+++ b/include/kendryte/clk.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef K210_CLK_H
+#define K210_CLK_H
+
+#define LOG_CATEGORY UCLASS_CLK
+#include <linux/types.h>
+#include <linux/clk-provider.h>
+
+static inline struct clk *k210_clk_gate(const char *name,
+					const char *parent_name,
+					void __iomem *reg, u8 bit_idx)
+{
+	return clk_register_gate(NULL, name, parent_name, 0, reg, bit_idx, 0,
+				 NULL);
+}
+
+static inline struct clk *k210_clk_half(const char *name,
+					const char *parent_name)
+{
+	return clk_register_fixed_factor(NULL, name, parent_name, 0, 1, 2);
+}
+
+static inline struct clk *k210_clk_div(const char *name,
+				       const char *parent_name,
+				       void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_register_divider(NULL, name, parent_name, 0, reg, shift,
+				    width, 0);
+}
+
+#endif /* K210_CLK_H */
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 08/33] doc: Fix typo in FIT documentation
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (6 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 07/33] clk: Add K210 clock support Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  2:21   ` Bin Meng
  2020-02-28 21:05 ` [PATCH v5 09/33] dm: Add support for simple-pm-bus Sean Anderson
                   ` (24 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

u_boot should be u-boot

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 doc/uImage.FIT/source_file_format.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/uImage.FIT/source_file_format.txt b/doc/uImage.FIT/source_file_format.txt
index 18d2aedcb7..00c4dc337c 100644
--- a/doc/uImage.FIT/source_file_format.txt
+++ b/doc/uImage.FIT/source_file_format.txt
@@ -172,7 +172,7 @@ the '/images' node should have the following layout:
   - os : OS name, mandatory for types "kernel" and "ramdisk". Valid OS names
     are: "openbsd", "netbsd", "freebsd", "4_4bsd", "linux", "svr4", "esix",
     "solaris", "irix", "sco", "dell", "ncr", "lynxos", "vxworks", "psos", "qnx",
-    "u_boot", "rtems", "unity", "integrity".
+    "u-boot", "rtems", "unity", "integrity".
   - arch : Architecture name, mandatory for types: "standalone", "kernel",
     "firmware", "ramdisk" and "fdt". Valid architecture names are: "alpha",
     "arm", "i386", "ia64", "mips", "mips64", "ppc", "s390", "sh", "sparc",
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 09/33] dm: Add support for simple-pm-bus
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (7 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 08/33] doc: Fix typo in FIT documentation Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr Sean Anderson
                   ` (23 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This type of bus is used in Linux to designate buses which have power
domains and/or clocks which need to be enabled before their child devices
can be used. Because power domains are automatically enabled before probing
in U-Boot, we just need to enable any clocks present.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---

Changes in v5:
- Reorder includes (simple pm)

Changes in v4:
- Split the bus off into its own driver
- Add test
- Fix line spacing in Kconfig
- Lint

Changes in v3:
- New

 arch/sandbox/dts/test.dts                     |  6 ++
 arch/sandbox/include/asm/clk.h                |  1 +
 configs/sandbox_defconfig                     |  1 +
 .../bus/simple-pm-bus.txt                     | 44 +++++++++++++++
 drivers/core/Kconfig                          |  7 +++
 drivers/core/Makefile                         |  1 +
 drivers/core/simple-pm-bus.c                  | 56 +++++++++++++++++++
 test/dm/Makefile                              |  1 +
 test/dm/simple-pm-bus.c                       | 45 +++++++++++++++
 9 files changed, 162 insertions(+)
 create mode 100644 doc/device-tree-bindings/bus/simple-pm-bus.txt
 create mode 100644 drivers/core/simple-pm-bus.c
 create mode 100644 test/dm/simple-pm-bus.c

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 4a277934a7..96e0b55eed 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -934,6 +934,12 @@
 	mdio: mdio-test {
 		compatible = "sandbox,mdio";
 	};
+
+	pm-bus-test {
+		compatible = "simple-pm-bus";
+		clocks = <&clk_sandbox 4>;
+		power-domains = <&pwrdom 1>;
+	};
 };
 
 #include "sandbox_pmic.dtsi"
diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h
index 1573e4a134..c184c4bffc 100644
--- a/arch/sandbox/include/asm/clk.h
+++ b/arch/sandbox/include/asm/clk.h
@@ -21,6 +21,7 @@ enum sandbox_clk_id {
 	SANDBOX_CLK_ID_I2C,
 	SANDBOX_CLK_ID_UART1,
 	SANDBOX_CLK_ID_UART2,
+	SANDBOX_CLK_ID_BUS,
 
 	SANDBOX_CLK_ID_COUNT,
 };
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 7b02b8de7c..22c1a073c4 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -90,6 +90,7 @@ CONFIG_REGMAP=y
 CONFIG_SYSCON=y
 CONFIG_DEVRES=y
 CONFIG_DEBUG_DEVRES=y
+CONFIG_SIMPLE_PM_BUS=y
 CONFIG_ADC=y
 CONFIG_ADC_SANDBOX=y
 CONFIG_AXI=y
diff --git a/doc/device-tree-bindings/bus/simple-pm-bus.txt b/doc/device-tree-bindings/bus/simple-pm-bus.txt
new file mode 100644
index 0000000000..6f15037131
--- /dev/null
+++ b/doc/device-tree-bindings/bus/simple-pm-bus.txt
@@ -0,0 +1,44 @@
+Simple Power-Managed Bus
+========================
+
+A Simple Power-Managed Bus is a transparent bus that doesn't need a real
+driver, as it's typically initialized by the boot loader.
+
+However, its bus controller is part of a PM domain, or under the control of a
+functional clock.  Hence, the bus controller's PM domain and/or clock must be
+enabled for child devices connected to the bus (either on-SoC or externally)
+to function.
+
+While "simple-pm-bus" follows the "simple-bus" set of properties, as specified
+in the Devicetree Specification, it is not an extension of "simple-bus".
+
+
+Required properties:
+  - compatible: Must contain at least "simple-pm-bus".
+		Must not contain "simple-bus".
+		It's recommended to let this be preceded by one or more
+		vendor-specific compatible values.
+  - #address-cells, #size-cells, ranges: Must describe the mapping between
+		parent address and child address spaces.
+
+Optional platform-specific properties for clock or PM domain control (at least
+one of them is required):
+  - clocks: Must contain a reference to the functional clock(s),
+  - power-domains: Must contain a reference to the PM domain.
+Please refer to the binding documentation for the clock and/or PM domain
+providers for more details.
+
+
+Example:
+
+	bsc: bus at fec10000 {
+		compatible = "renesas,bsc-sh73a0", "renesas,bsc",
+			     "simple-pm-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0 0 0x20000000>;
+		reg = <0xfec10000 0x400>;
+		interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&zb_clk>;
+		power-domains = <&pd_a4s>;
+	};
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index 3b95b5387b..0cd687526e 100644
--- a/drivers/core/Kconfig
+++ b/drivers/core/Kconfig
@@ -195,6 +195,13 @@ config SPL_SIMPLE_BUS
 	  Supports the 'simple-bus' driver, which is used on some systems
 	  in SPL.
 
+config SIMPLE_PM_BUS
+	bool "Support simple-pm-bus driver"
+	depends on DM && OF_CONTROL && CLK && POWER_DOMAIN
+	help
+	  Supports the 'simple-pm-bus' driver, which is used for busses that
+	  have power domains and/or clocks which need to be enabled before use.
+
 config OF_TRANSLATE
 	bool "Translate addresses using fdt_translate_address"
 	depends on DM && OF_CONTROL
diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index bce7467da1..284af7caaa 100644
--- a/drivers/core/Makefile
+++ b/drivers/core/Makefile
@@ -6,6 +6,7 @@ obj-y	+= device.o fdtaddr.o lists.o root.o uclass.o util.o
 obj-$(CONFIG_DEVRES) += devres.o
 obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE)	+= device-remove.o
 obj-$(CONFIG_$(SPL_)SIMPLE_BUS)	+= simple-bus.o
+obj-$(CONFIG_SIMPLE_PM_BUS)	+= simple-pm-bus.o
 obj-$(CONFIG_DM)	+= dump.o
 obj-$(CONFIG_$(SPL_TPL_)REGMAP)	+= regmap.o
 obj-$(CONFIG_$(SPL_TPL_)SYSCON)	+= syscon-uclass.o
diff --git a/drivers/core/simple-pm-bus.c b/drivers/core/simple-pm-bus.c
new file mode 100644
index 0000000000..2313bd9a27
--- /dev/null
+++ b/drivers/core/simple-pm-bus.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+
+/*
+ * Power domains are taken care of by driver_probe, so we just have to enable
+ * clocks
+ */
+static const int simple_pm_bus_probe(struct udevice *dev)
+{
+	int ret;
+	struct clk_bulk *bulk = dev_get_priv(dev);
+
+	ret = clk_get_bulk(dev, bulk);
+	if (ret)
+		return ret;
+
+	ret = clk_enable_bulk(bulk);
+	if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
+		clk_release_bulk(bulk);
+		return ret;
+	}
+	return 0;
+}
+
+static const int simple_pm_bus_remove(struct udevice *dev)
+{
+	int ret;
+	struct clk_bulk *bulk = dev_get_priv(dev);
+
+	ret = clk_release_bulk(bulk);
+	if (ret && ret != -ENOSYS && ret != -ENOTSUPP)
+		return ret;
+	else
+		return 0;
+}
+
+static const struct udevice_id simple_pm_bus_ids[] = {
+	{ .compatible = "simple-pm-bus" },
+	{ }
+};
+
+U_BOOT_DRIVER(simple_pm_bus_drv) = {
+	.name	= "simple_pm_bus",
+	.id	= UCLASS_SIMPLE_BUS,
+	.of_match = simple_pm_bus_ids,
+	.probe = simple_pm_bus_probe,
+	.remove = simple_pm_bus_remove,
+	.priv_auto_alloc_size = sizeof(struct clk_bulk),
+	.flags	= DM_FLAG_PRE_RELOC,
+};
diff --git a/test/dm/Makefile b/test/dm/Makefile
index ef6065b60f..e0be0d684f 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -71,4 +71,5 @@ obj-$(CONFIG_DM_MDIO) += mdio.o
 obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
 obj-$(CONFIG_DM_RNG) += rng.o
 obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
+obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
 endif
diff --git a/test/dm/simple-pm-bus.c b/test/dm/simple-pm-bus.c
new file mode 100644
index 0000000000..978c7f191e
--- /dev/null
+++ b/test/dm/simple-pm-bus.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <test/ut.h>
+#include <asm/clk.h>
+#include <asm/power-domain.h>
+
+/* These must match the ids in the device tree */
+#define TEST_CLOCK_ID 4
+#define TEST_POWER_ID 1
+
+static int dm_test_simple_pm_bus(struct unit_test_state *uts)
+{
+	struct udevice *power;
+	struct udevice *clock;
+	struct udevice *bus;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+					      "power-domain", &power));
+	ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+					      &clock));
+	ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+	ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, "pm-bus-test",
+					      &bus));
+	ut_asserteq(1, sandbox_power_domain_query(power, TEST_POWER_ID));
+	ut_asserteq(1, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+	ut_assertok(device_remove(bus, DM_REMOVE_NORMAL));
+	/* must re-probe since device_remove also removes the power domain */
+	ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+					      "power-domain", &power));
+	ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+	ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+	return 0;
+}
+DM_TEST(dm_test_simple_pm_bus, DM_TESTF_SCAN_FDT);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (8 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 09/33] dm: Add support for simple-pm-bus Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  2:22   ` Bin Meng
  2020-03-02 19:46   ` Simon Glass
  2020-02-28 21:05 ` [PATCH v5 11/33] reset: Add generic reset driver Sean Anderson
                   ` (22 subsequent siblings)
  32 siblings, 2 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

dev_read_addr_ptr had different semantics depending on whether OF_LIVE was
enabled. This patch converts both implementations to return NULL on error,
and converts all call sites which check for FDT_ADDR_T_NONE to check for
NULL instead. This patch also removes the call to map_physmem, since we
have dev_remap_addr* for those semantics.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/clk/imx/clk-imx8mp.c                  | 2 +-
 drivers/core/read.c                           | 2 +-
 drivers/pinctrl/broadcom/pinctrl-bcm283x.c    | 2 +-
 drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 2 +-
 include/dm/read.h                             | 4 +++-
 5 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index a2693d2f7a..df30f4a087 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -281,7 +281,7 @@ static int imx8mp_clk_probe(struct udevice *dev)
 	clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
 
 	base = dev_read_addr_ptr(dev);
-	if (base == (void *)FDT_ADDR_T_NONE)
+	if (!base)
 		return -EINVAL;
 
 	clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels)));
diff --git a/drivers/core/read.c b/drivers/core/read.c
index 1f999b1b31..2a8813fff1 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -154,7 +154,7 @@ void *dev_read_addr_ptr(const struct udevice *dev)
 {
 	fdt_addr_t addr = dev_read_addr(dev);
 
-	return (addr == FDT_ADDR_T_NONE) ? NULL : map_sysmem(addr, 0);
+	return (addr == FDT_ADDR_T_NONE) ? NULL : addr;
 }
 
 void *dev_remap_addr(const struct udevice *dev)
diff --git a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
index eb720f09f8..6961536a4d 100644
--- a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
+++ b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
@@ -116,7 +116,7 @@ int bcm283x_pinctl_probe(struct udevice *dev)
 	}
 
 	priv->base_reg = dev_read_addr_ptr(dev);
-	if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
+	if (!priv->base_reg) {
 		debug("%s: Failed to get base address\n", __func__);
 		return -EINVAL;
 	}
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index c7351f32bb..bd95662ed5 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -630,7 +630,7 @@ int mtk_pinctrl_common_probe(struct udevice *dev,
 	int ret;
 
 	priv->base = dev_read_addr_ptr(dev);
-	if (priv->base == (void *)FDT_ADDR_T_NONE)
+	if (!priv->base)
 		return -EINVAL;
 
 	priv->soc = soc;
diff --git a/include/dm/read.h b/include/dm/read.h
index da8c7f25e7..d7183713dd 100644
--- a/include/dm/read.h
+++ b/include/dm/read.h
@@ -715,7 +715,9 @@ static inline fdt_addr_t dev_read_addr(const struct udevice *dev)
 
 static inline void *dev_read_addr_ptr(const struct udevice *dev)
 {
-	return devfdt_get_addr_ptr(dev);
+	void *addr = devfdt_get_addr_ptr(dev);
+
+	return ((fdt_addr_t)addr == FDT_ADDR_T_NONE) ? NULL : addr;
 }
 
 static inline fdt_addr_t dev_read_addr_pci(const struct udevice *dev)
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 11/33] reset: Add generic reset driver
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (9 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 12/33] lib: Always set errno in hcreate_r Sean Anderson
                   ` (21 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This patch adds a generic reset driver. It is designed to be useful when
one has a register in a regmap which contains bits that reset other
devices. I thought this seemed like a very generic use, so here is a
generic driver. The overall structure has been modeled on the syscon-reboot
driver.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---

Changes in v5:
- Reorder includes
- Include linux/err.h explicitly

Changes in v4:
- Added basic test
- Fix incorrect usage of regmap_update_bits

Changes in v3:
- New

 arch/sandbox/dts/test.dts                     | 15 ++++
 configs/sandbox_defconfig                     |  1 +
 .../reset/syscon-reset.txt                    | 36 +++++++++
 drivers/reset/Kconfig                         |  5 ++
 drivers/reset/Makefile                        |  1 +
 drivers/reset/reset-syscon.c                  | 80 +++++++++++++++++++
 test/dm/Makefile                              |  1 +
 test/dm/syscon-reset.c                        | 58 ++++++++++++++
 8 files changed, 197 insertions(+)
 create mode 100644 doc/device-tree-bindings/reset/syscon-reset.txt
 create mode 100644 drivers/reset/reset-syscon.c
 create mode 100644 test/dm/syscon-reset.c

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 96e0b55eed..6f2fcc64a4 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -940,6 +940,21 @@
 		clocks = <&clk_sandbox 4>;
 		power-domains = <&pwrdom 1>;
 	};
+
+	resetc2: syscon-reset {
+		compatible = "syscon-reset";
+		#reset-cells = <1>;
+		regmap = <&syscon0>;
+		offset = <1>;
+		mask = <0x27FFFFFF>;
+		assert-high = <0>;
+	};
+
+	syscon-reset-test {
+		compatible = "sandbox,misc_sandbox";
+		resets = <&resetc2 15>, <&resetc2 30>, <&resetc2 60>;
+		reset-names = "valid", "no_mask", "out_of_range";
+	};
 };
 
 #include "sandbox_pmic.dtsi"
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 22c1a073c4..ff0eaf3c4d 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -191,6 +191,7 @@ CONFIG_DM_RESET=y
 CONFIG_SANDBOX_RESET=y
 CONFIG_DM_RNG=y
 CONFIG_RNG_SANDBOX=y
+CONFIG_RESET_SYSCON=y
 CONFIG_DM_RTC=y
 CONFIG_RTC_RV8803=y
 CONFIG_DEBUG_UART_SANDBOX=y
diff --git a/doc/device-tree-bindings/reset/syscon-reset.txt b/doc/device-tree-bindings/reset/syscon-reset.txt
new file mode 100644
index 0000000000..f136b3d225
--- /dev/null
+++ b/doc/device-tree-bindings/reset/syscon-reset.txt
@@ -0,0 +1,36 @@
+Generic SYSCON mapped register reset driver
+
+This is a generic reset driver using syscon to map the reset register.
+The reset is generally performed with a write to the reset register
+defined by the register map pointed by syscon reference plus the offset and
+shifted by the reset specifier/
+
+To assert a reset on some device, the equivalent of the following operation is
+performed, where reset_id is the reset specifier from the device's resets
+property.
+
+	if (BIT(reset_id) & mask)
+		regmap[offset][reset_id] = assert-high;
+
+Required properties:
+- compatible: should contain "syscon-reset"
+- #reset-cells: must be 1
+- regmap: this is phandle to the register map node
+- offset: offset in the register map for the reboot register (in bytes)
+
+Optional properties:
+- mask: accept only the reset specifiers defined by the mask (32 bit)
+- assert-high: Bit to write when asserting a reset. Defaults to 1.
+
+Default will be little endian mode, 32 bit access only.
+
+Example:
+
+	reset-controller {
+		compatible = "syscon-reset";
+		#reset-cells = <1>;
+		regmap = <&sysctl>;
+		offset = <0x20>;
+		mask = <0x27FFFFFF>;
+		assert-high = <0>;
+	};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 75ccd65799..097bf32b21 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -148,4 +148,9 @@ config RESET_IMX7
 	help
 	  Support for reset controller on i.MX7/8 SoCs.
 
+config RESET_SYSCON
+	bool "Enable generic syscon reset driver support"
+	depends on DM_RESET
+	help
+	  Support generic syscon mapped register reset devices.
 endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 0a044d5d8c..433f1eca54 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_HISILICON) += reset-hisilicon.o
 obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o
diff --git a/drivers/reset/reset-syscon.c b/drivers/reset/reset-syscon.c
new file mode 100644
index 0000000000..34dfe0bab6
--- /dev/null
+++ b/drivers/reset/reset-syscon.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <reset.h>
+#include <reset-uclass.h>
+#include <syscon.h>
+#include <linux/err.h>
+
+struct syscon_reset_priv {
+	struct regmap *regmap;
+	uint offset;
+	uint mask;
+	bool assert_high;
+};
+
+static int syscon_reset_request(struct reset_ctl *rst)
+{
+	struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+	if (BIT(rst->id) & priv->mask)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static int syscon_reset_assert(struct reset_ctl *rst)
+{
+	struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+	return regmap_update_bits(priv->regmap, priv->offset, BIT(rst->id),
+				  priv->assert_high ? BIT(rst->id) : 0);
+}
+
+static int syscon_reset_deassert(struct reset_ctl *rst)
+{
+	struct syscon_reset_priv *priv = dev_get_priv(rst->dev);
+
+	return regmap_update_bits(priv->regmap, priv->offset, BIT(rst->id),
+				  priv->assert_high ? 0 : BIT(rst->id));
+}
+
+static const struct reset_ops syscon_reset_ops = {
+	.request = syscon_reset_request,
+	.rst_assert = syscon_reset_assert,
+	.rst_deassert = syscon_reset_deassert,
+};
+
+int syscon_reset_probe(struct udevice *dev)
+{
+	struct syscon_reset_priv *priv = dev_get_priv(dev);
+
+	priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap");
+	if (IS_ERR(priv->regmap))
+		return -ENODEV;
+
+	priv->offset = dev_read_u32_default(dev, "offset", 0);
+	priv->mask = dev_read_u32_default(dev, "mask", 0);
+	priv->assert_high = dev_read_u32_default(dev, "assert-high", true);
+
+	return 0;
+}
+
+static const struct udevice_id syscon_reset_ids[] = {
+	{ .compatible = "syscon-reset" },
+	{ },
+};
+
+U_BOOT_DRIVER(syscon_reset) = {
+	.name = "syscon_reset",
+	.id = UCLASS_RESET,
+	.of_match = syscon_reset_ids,
+	.probe = syscon_reset_probe,
+	.priv_auto_alloc_size = sizeof(struct syscon_reset_priv),
+	.ops = &syscon_reset_ops,
+};
diff --git a/test/dm/Makefile b/test/dm/Makefile
index e0be0d684f..187f288362 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -72,4 +72,5 @@ obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
 obj-$(CONFIG_DM_RNG) += rng.o
 obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
 obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
+obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
 endif
diff --git a/test/dm/syscon-reset.c b/test/dm/syscon-reset.c
new file mode 100644
index 0000000000..1db0206e00
--- /dev/null
+++ b/test/dm/syscon-reset.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <test/ut.h>
+#include <asm/test.h>
+
+/* The following values must match the device tree */
+#define TEST_RESET_REG 1
+#define TEST_RESET_ASSERT_HIGH 0
+#define TEST_RESET_ASSERT (TEST_RESET_ASSERT_HIGH ? (u32)-1 : (u32)0)
+#define TEST_RESET_DEASSERT (~TEST_RESET_ASSERT)
+
+#define TEST_RESET_VALID 15
+#define TEST_RESET_NOMASK 30
+#define TEST_RESET_OUTOFRANGE 60
+
+static int dm_test_syscon_reset(struct unit_test_state *uts)
+{
+	struct regmap *map;
+	struct reset_ctl rst;
+	struct udevice *reset;
+	struct udevice *syscon;
+	struct udevice *syscon_reset;
+	uint reg;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "syscon-reset-test",
+					      &reset));
+	ut_assertok(uclass_get_device_by_name(UCLASS_SYSCON, "syscon at 0",
+					      &syscon));
+	ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "syscon-reset",
+					      &syscon_reset));
+	ut_assertok_ptr((map = syscon_get_regmap(syscon)));
+
+	ut_asserteq(-EINVAL, reset_get_by_name(reset, "no_mask", &rst));
+	ut_asserteq(-EINVAL, reset_get_by_name(reset, "out_of_range", &rst));
+	ut_assertok(reset_get_by_name(reset, "valid", &rst));
+
+	sandbox_set_enable_memio(true);
+	ut_assertok(regmap_write(map, TEST_RESET_REG, TEST_RESET_DEASSERT));
+	ut_assertok(reset_assert(&rst));
+	ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+	ut_asserteq(TEST_RESET_DEASSERT ^ BIT(TEST_RESET_VALID), reg);
+
+	ut_assertok(reset_deassert(&rst));
+	ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+	ut_asserteq(TEST_RESET_DEASSERT, reg);
+
+	return 0;
+}
+DM_TEST(dm_test_syscon_reset, DM_TESTF_SCAN_FDT);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 12/33] lib: Always set errno in hcreate_r
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (10 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 11/33] reset: Add generic reset driver Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  2:24   ` Bin Meng
  2020-02-28 21:05 ` [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA Sean Anderson
                   ` (20 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This could give a confusing error message if it failed and didn't set
errno.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 lib/hashtable.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/lib/hashtable.c b/lib/hashtable.c
index 907e8a642f..e9ac7e252e 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -109,8 +109,10 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)
 	}
 
 	/* There is still another table active. Return with error. */
-	if (htab->table != NULL)
+	if (htab->table != NULL) {
+		__set_errno(EINVAL);
 		return 0;
+	}
 
 	/* Change nel to the first prime number not smaller as nel. */
 	nel |= 1;		/* make odd */
@@ -123,8 +125,10 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)
 	/* allocate memory and zero out */
 	htab->table = (struct env_entry_node *)calloc(htab->size + 1,
 						sizeof(struct env_entry_node));
-	if (htab->table == NULL)
+	if (htab->table == NULL) {
+		__set_errno(ENOMEM);
 		return 0;
+	}
 
 	/* everything went alright */
 	return 1;
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (11 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 12/33] lib: Always set errno in hcreate_r Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:47   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 14/33] gpio: sifive: Use generic reg read function Sean Anderson
                   ` (19 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The Fully-Programmable Input/Output Array (FPIOA) device controls pin
multiplexing on the K210. The FPIOA can remap any supported function to any
multifunctional IO pin. It can also perform basic GPIO functions, such as
reading the current value of a pin.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 MAINTAINERS                                   |   2 +
 .../pinctrl/kendryte,k210-fpioa.txt           | 116 +++
 drivers/pinctrl/Kconfig                       |   1 +
 drivers/pinctrl/Makefile                      |   1 +
 drivers/pinctrl/kendryte/Kconfig              |   7 +
 drivers/pinctrl/kendryte/Makefile             |   1 +
 drivers/pinctrl/kendryte/pinctrl.c            | 663 ++++++++++++++++++
 drivers/pinctrl/kendryte/pinctrl.h            | 325 +++++++++
 include/dt-bindings/pinctrl/k210-pinctrl.h    |  12 +
 9 files changed, 1128 insertions(+)
 create mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
 create mode 100644 drivers/pinctrl/kendryte/Kconfig
 create mode 100644 drivers/pinctrl/kendryte/Makefile
 create mode 100644 drivers/pinctrl/kendryte/pinctrl.c
 create mode 100644 drivers/pinctrl/kendryte/pinctrl.h
 create mode 100644 include/dt-bindings/pinctrl/k210-pinctrl.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 8e9e0569ba..8725bcf9af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -809,7 +809,9 @@ RISC-V KENDRYTE
 M:	Sean Anderson <seanga2@gmail.com>
 S:	Maintained
 F:	doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
+F:	doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
 F:	drivers/clk/kendryte/
+F:	drivers/pinctrl/kendryte/
 F:	include/kendryte/
 
 RNG
diff --git a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
new file mode 100644
index 0000000000..299ce65304
--- /dev/null
+++ b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
@@ -0,0 +1,116 @@
+Kendryte K210 FPIOA
+
+This binding describes the Fully-Programmable Input/Output Array found on
+Kendryte K210 SoCs. Any of the 256 functions can be mapped to any of the 48
+pins.
+
+Required properties:
+- compatible: should be "kendryte,k210-fpioa"
+- reg: address and length of the FPIOA registers
+- kendryte,sysctl: phandle to the "sysctl" register map node
+- kendryte,power-offset: offset in the register map of the power bank control
+  register (in bytes)
+
+Configuration nodes
+
+Pin configuration nodes are documented in pinctrl-bindings.txt
+
+Valid values for pins, groups, and function names:
+
+pins:
+	"IO_X", where X is a number from 0 to 47.
+
+groups:
+	A0, A1, A2, B0, B1, B2, C0, C1
+
+functions:
+	JTAG_TCLK, JTAG_TDI, JTAG_TMS, JTAG_TDO, SPI0_D0, SPI0_D1, SPI0_D2,
+	SPI0_D3, SPI0_D4, SPI0_D5, SPI0_D6, SPI0_D7, SPI0_SS0, SPI0_SS1,
+	SPI0_SS2, SPI0_SS3, SPI0_ARB, SPI0_SCLK, UARTHS_RX, UARTHS_TX, RESV6,
+	RESV7, CLK_SPI1, CLK_I2C1, GPIOHS0, GPIOHS1, GPIOHS2, GPIOHS3, GPIOHS4,
+	GPIOHS5, GPIOHS6, GPIOHS7, GPIOHS8, GPIOHS9, GPIOHS10, GPIOHS11,
+	GPIOHS12, GPIOHS13, GPIOHS14, GPIOHS15, GPIOHS16, GPIOHS17, GPIOHS18,
+	GPIOHS19, GPIOHS20, GPIOHS21, GPIOHS22, GPIOHS23, GPIOHS24, GPIOHS25,
+	GPIOHS26, GPIOHS27, GPIOHS28, GPIOHS29, GPIOHS30, GPIOHS31, GPIO0,
+	GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, UART1_RX, UART1_TX,
+	UART2_RX, UART2_TX, UART3_RX, UART3_TX, SPI1_D0, SPI1_D1, SPI1_D2,
+	SPI1_D3, SPI1_D4, SPI1_D5, SPI1_D6, SPI1_D7, SPI1_SS0, SPI1_SS1,
+	SPI1_SS2, SPI1_SS3, SPI1_ARB, SPI1_SCLK, SPI2_D0, SPI2_SS, SPI2_SCLK,
+	I2S0_MCLK, I2S0_SCLK, I2S0_WS, I2S0_IN_D0, I2S0_IN_D1, I2S0_IN_D2,
+	I2S0_IN_D3, I2S0_OUT_D0, I2S0_OUT_D1, I2S0_OUT_D2, I2S0_OUT_D3,
+	I2S1_MCLK, I2S1_SCLK, I2S1_WS, I2S1_IN_D0, I2S1_IN_D1, I2S1_IN_D2,
+	I2S1_IN_D3, I2S1_OUT_D0, I2S1_OUT_D1, I2S1_OUT_D2, I2S1_OUT_D3,
+	I2S2_MCLK, I2S2_SCLK, I2S2_WS, I2S2_IN_D0, I2S2_IN_D1, I2S2_IN_D2,
+	I2S2_IN_D3, I2S2_OUT_D0, I2S2_OUT_D1, I2S2_OUT_D2, I2S2_OUT_D3, RESV0,
+	RESV1, RESV2, RESV3, RESV4, RESV5, I2C0_SCLK, I2C0_SDA, I2C1_SCLK,
+	I2C1_SDA, I2C2_SCLK, I2C2_SDA, DVP_XCLK, DVP_RST, DVP_PWDN, DVP_VSYNC,
+	DVP_HREF, DVP_PCLK, DVP_D0, DVP_D1, DVP_D2, DVP_D3, DVP_D4, DVP_D5,
+	DVP_D6, DVP_D7, SCCB_SCLK, SCCB_SDA, UART1_CTS, UART1_DSR, UART1_DCD,
+	UART1_RI, UART1_SIR_IN, UART1_DTR, UART1_RTS, UART1_OUT2, UART1_OUT1,
+	UART1_SIR_OUT, UART1_BAUD, UART1_RE, UART1_DE, UART1_RS485_EN,
+	UART2_CTS, UART2_DSR, UART2_DCD, UART2_RI, UART2_SIR_IN, UART2_DTR,
+	UART2_RTS, UART2_OUT2, UART2_OUT1, UART2_SIR_OUT, UART2_BAUD, UART2_RE,
+	UART2_DE, UART2_RS485_EN, UART3_CTS, UART3_DSR, UART3_DCD, UART3_RI,
+	UART3_SIR_IN, UART3_DTR, UART3_RTS, UART3_OUT2, UART3_OUT1,
+	UART3_SIR_OUT, UART3_BAUD, UART3_RE, UART3_DE, UART3_RS485_EN,
+	TIMER0_TOGGLE1, TIMER0_TOGGLE2, TIMER0_TOGGLE3, TIMER0_TOGGLE4,
+	TIMER1_TOGGLE1, TIMER1_TOGGLE2, TIMER1_TOGGLE3, TIMER1_TOGGLE4,
+	TIMER2_TOGGLE1, TIMER2_TOGGLE2, TIMER2_TOGGLE3, TIMER2_TOGGLE4,
+	CLK_SPI2, CLK_I2C2, INTERNAL0, INTERNAL1, INTERNAL2, INTERNAL3,
+	INTERNAL4, INTERNAL5, INTERNAL6, INTERNAL7, INTERNAL8, INTERNAL9,
+	INTERNAL10, INTERNAL11, INTERNAL12, INTERNAL13, INTERNAL14, INTERNAL15,
+	INTERNAL16, INTERNAL17, CONSTANT, INTERNAL18, DEBUG0, DEBUG1, DEBUG2,
+	DEBUG3, DEBUG4, DEBUG5, DEBUG6, DEBUG7, DEBUG8, DEBUG9, DEBUG10,
+	DEBUG11, DEBUG12, DEBUG13, DEBUG14, DEBUG15, DEBUG16, DEBUG17, DEBUG18,
+	DEBUG19, DEBUG20, DEBUG21, DEBUG22, DEBUG23, DEBUG24, DEBUG25, DEBUG26,
+	DEBUG27, DEBUG28, DEBUG29, DEBUG30, DEBUG31
+
+Valid properties include:
+	bias-disable, bias-pull-down, bias-pull-up, drive-strength,
+	drive-strength-ua, input-enable, input-disable, input-schmitt-enable,
+	input-schmitt-disable, power-source, output-low, output-high,
+	output-enable, output-disable, slew-rate
+
+Notes on specific properties include:
+- bias-pull-up, -down, and -pin-default: The pull strength cannot be configured.
+- drive-strength: There are 8 drive strength settings between 11 and 50 mA
+- power-source: Controls the output voltage of a bank of pins. Either
+  K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. This property cannot
+  be specified for individual pins.
+- slew-rate: Specifying this property reduces the slew rate
+
+Example:
+fpioa: pinmux at 502B0000 {
+	compatible = "kendryte,k210-fpioa";
+	reg = <0x502B0000 0x100>;
+	kendryte,sysctl = <&sysctl>;
+	kendryte,power-offset = <K210_SYSCTL_POWER_SEL>;
+
+	fpioa_jtag: jtag {
+		voltage {
+			group = "A0";
+			power-source = <K210_PC_POWER_3V3>;
+		};
+		tck {
+			function = "JTAG_TCLK";
+			pins = "IO_0";
+		};
+		tdi {
+			function = "JTAG_TDI";
+			pins = "IO_1";
+		};
+		tms {
+			function = "JTAG_TMS";
+			pins = "IO_2";
+		};
+		tdo {
+			function = "JTAG_TDO";
+			pins = "IO_3";
+		};
+	};
+
+	fpioa_isp: isp {
+		function = "GPIOHS0";
+		pins = "IO_16";
+	};
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 83e39b9de3..9ec56c7735 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -289,6 +289,7 @@ endif
 source "drivers/pinctrl/broadcom/Kconfig"
 source "drivers/pinctrl/exynos/Kconfig"
 source "drivers/pinctrl/intel/Kconfig"
+source "drivers/pinctrl/kendryte/Kconfig"
 source "drivers/pinctrl/mediatek/Kconfig"
 source "drivers/pinctrl/meson/Kconfig"
 source "drivers/pinctrl/mscc/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 4f662c4f6d..1fe66fed3c 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_SANDBOX)	+= pinctrl-sandbox.o
 obj-$(CONFIG_PINCTRL_UNIPHIER)	+= uniphier/
 obj-$(CONFIG_PINCTRL_PIC32)	+= pinctrl_pic32.o
 obj-$(CONFIG_PINCTRL_EXYNOS)	+= exynos/
+obj-$(CONFIG_PINCTRL_K210)	+= kendryte/
 obj-$(CONFIG_PINCTRL_MESON)	+= meson/
 obj-$(CONFIG_PINCTRL_MTK)	+= mediatek/
 obj-$(CONFIG_PINCTRL_MSCC)	+= mscc/
diff --git a/drivers/pinctrl/kendryte/Kconfig b/drivers/pinctrl/kendryte/Kconfig
new file mode 100644
index 0000000000..6fc10e9516
--- /dev/null
+++ b/drivers/pinctrl/kendryte/Kconfig
@@ -0,0 +1,7 @@
+config PINCTRL_K210
+	bool "Kendryte K210 Fully-Programmable Input/Output Array driver"
+	depends on DM && PINCTRL_GENERIC
+	help
+	  Support pin multiplexing on the K210. The "FPIOA" can remap any
+	  supported function to any multifunctional IO pin. It can also perform
+	  basic GPIO functions, such as reading the current value of a pin.
diff --git a/drivers/pinctrl/kendryte/Makefile b/drivers/pinctrl/kendryte/Makefile
new file mode 100644
index 0000000000..1c10d7a929
--- /dev/null
+++ b/drivers/pinctrl/kendryte/Makefile
@@ -0,0 +1 @@
+obj-y += pinctrl.o
diff --git a/drivers/pinctrl/kendryte/pinctrl.c b/drivers/pinctrl/kendryte/pinctrl.c
new file mode 100644
index 0000000000..3f7de0e706
--- /dev/null
+++ b/drivers/pinctrl/kendryte/pinctrl.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/pinctrl/k210-pinctrl.h>
+#include <mapmem.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <linux/err.h>
+#include "pinctrl.h"
+
+struct k210_pc_priv {
+	struct clk clk;
+	struct k210_fpioa __iomem *fpioa; /* FPIOA register */
+	struct regmap *sysctl; /* Sysctl regmap */
+	u32 power_offset; /* Power bank register offset */
+};
+
+#define PIN(i) [i] = "IO_" #i
+static const char k210_pc_pin_names[][6] = {
+	PIN(0),
+	PIN(1),
+	PIN(2),
+	PIN(3),
+	PIN(4),
+	PIN(5),
+	PIN(6),
+	PIN(7),
+	PIN(8),
+	PIN(9),
+	PIN(10),
+	PIN(11),
+	PIN(12),
+	PIN(13),
+	PIN(14),
+	PIN(15),
+	PIN(16),
+	PIN(17),
+	PIN(18),
+	PIN(19),
+	PIN(20),
+	PIN(21),
+	PIN(22),
+	PIN(23),
+	PIN(24),
+	PIN(25),
+	PIN(26),
+	PIN(27),
+	PIN(28),
+	PIN(29),
+	PIN(30),
+	PIN(31),
+	PIN(32),
+	PIN(33),
+	PIN(34),
+	PIN(35),
+	PIN(36),
+	PIN(37),
+	PIN(38),
+	PIN(39),
+	PIN(40),
+	PIN(41),
+	PIN(42),
+	PIN(43),
+	PIN(44),
+	PIN(45),
+	PIN(46),
+	PIN(47),
+};
+#undef PIN
+
+static int k210_pc_get_pins_count(struct udevice *dev)
+{
+	return ARRAY_SIZE(k210_pc_pin_names);
+};
+
+static const char *k210_pc_get_pin_name(struct udevice *dev, unsigned selector)
+{
+	return k210_pc_pin_names[selector];
+}
+
+/* These are just power domains */
+static const char k210_pc_group_names[][3] = {
+	[0] = "A0",
+	[1] = "A1",
+	[2] = "A2",
+	[3] = "B0",
+	[4] = "B1",
+	[5] = "B2",
+	[6] = "C0",
+	[7] = "C1",
+};
+
+static int k210_pc_get_groups_count(struct udevice *dev)
+{
+	return ARRAY_SIZE(k210_pc_group_names);
+}
+
+static const char *k210_pc_get_group_name(struct udevice *dev,
+					  unsigned selector)
+{
+	return k210_pc_group_names[selector];
+}
+
+enum k210_pc_mode_id {
+	K210_PC_DEFAULT_DISABLED,
+	K210_PC_DEFAULT_IN,
+	K210_PC_DEFAULT_IN_TIE,
+	K210_PC_DEFAULT_OUT,
+	K210_PC_DEFAULT_I2C,
+	K210_PC_DEFAULT_SPI,
+	K210_PC_DEFAULT_GPIO,
+	K210_PC_DEFAULT_INT13,
+};
+
+#define DEFAULT(mode) [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode
+static const u32 k210_pc_mode_id_to_mode[] = {
+	[K210_PC_DEFAULT_DISABLED] = 0,
+	DEFAULT(IN),
+	[K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN,
+	DEFAULT(OUT),
+	DEFAULT(I2C),
+	DEFAULT(SPI),
+	DEFAULT(GPIO),
+	[K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU,
+};
+#undef DEFAULT
+
+/* This saves around 2K vs having a pointer+mode */
+struct k210_pcf_info {
+	char name[15];
+	u8 mode_id;
+};
+
+#define FUNC(id, mode) [K210_PCF_##id] = { \
+	.name = #id, \
+	.mode_id = K210_PC_DEFAULT_##mode \
+}
+static const struct k210_pcf_info k210_pcf_infos[] = {
+	FUNC(JTAG_TCLK,      IN),
+	FUNC(JTAG_TDI,       IN),
+	FUNC(JTAG_TMS,       IN),
+	FUNC(JTAG_TDO,       OUT),
+	FUNC(SPI0_D0,        SPI),
+	FUNC(SPI0_D1,        SPI),
+	FUNC(SPI0_D2,        SPI),
+	FUNC(SPI0_D3,        SPI),
+	FUNC(SPI0_D4,        SPI),
+	FUNC(SPI0_D5,        SPI),
+	FUNC(SPI0_D6,        SPI),
+	FUNC(SPI0_D7,        SPI),
+	FUNC(SPI0_SS0,       OUT),
+	FUNC(SPI0_SS1,       OUT),
+	FUNC(SPI0_SS2,       OUT),
+	FUNC(SPI0_SS3,       OUT),
+	FUNC(SPI0_ARB,       IN_TIE),
+	FUNC(SPI0_SCLK,      OUT),
+	FUNC(UARTHS_RX,      IN),
+	FUNC(UARTHS_TX,      OUT),
+	FUNC(RESV6,          IN),
+	FUNC(RESV7,          IN),
+	FUNC(CLK_SPI1,       OUT),
+	FUNC(CLK_I2C1,       OUT),
+	FUNC(GPIOHS0,        GPIO),
+	FUNC(GPIOHS1,        GPIO),
+	FUNC(GPIOHS2,        GPIO),
+	FUNC(GPIOHS3,        GPIO),
+	FUNC(GPIOHS4,        GPIO),
+	FUNC(GPIOHS5,        GPIO),
+	FUNC(GPIOHS6,        GPIO),
+	FUNC(GPIOHS7,        GPIO),
+	FUNC(GPIOHS8,        GPIO),
+	FUNC(GPIOHS9,        GPIO),
+	FUNC(GPIOHS10,       GPIO),
+	FUNC(GPIOHS11,       GPIO),
+	FUNC(GPIOHS12,       GPIO),
+	FUNC(GPIOHS13,       GPIO),
+	FUNC(GPIOHS14,       GPIO),
+	FUNC(GPIOHS15,       GPIO),
+	FUNC(GPIOHS16,       GPIO),
+	FUNC(GPIOHS17,       GPIO),
+	FUNC(GPIOHS18,       GPIO),
+	FUNC(GPIOHS19,       GPIO),
+	FUNC(GPIOHS20,       GPIO),
+	FUNC(GPIOHS21,       GPIO),
+	FUNC(GPIOHS22,       GPIO),
+	FUNC(GPIOHS23,       GPIO),
+	FUNC(GPIOHS24,       GPIO),
+	FUNC(GPIOHS25,       GPIO),
+	FUNC(GPIOHS26,       GPIO),
+	FUNC(GPIOHS27,       GPIO),
+	FUNC(GPIOHS28,       GPIO),
+	FUNC(GPIOHS29,       GPIO),
+	FUNC(GPIOHS30,       GPIO),
+	FUNC(GPIOHS31,       GPIO),
+	FUNC(GPIO0,          GPIO),
+	FUNC(GPIO1,          GPIO),
+	FUNC(GPIO2,          GPIO),
+	FUNC(GPIO3,          GPIO),
+	FUNC(GPIO4,          GPIO),
+	FUNC(GPIO5,          GPIO),
+	FUNC(GPIO6,          GPIO),
+	FUNC(GPIO7,          GPIO),
+	FUNC(UART1_RX,       IN),
+	FUNC(UART1_TX,       OUT),
+	FUNC(UART2_RX,       IN),
+	FUNC(UART2_TX,       OUT),
+	FUNC(UART3_RX,       IN),
+	FUNC(UART3_TX,       OUT),
+	FUNC(SPI1_D0,        SPI),
+	FUNC(SPI1_D1,        SPI),
+	FUNC(SPI1_D2,        SPI),
+	FUNC(SPI1_D3,        SPI),
+	FUNC(SPI1_D4,        SPI),
+	FUNC(SPI1_D5,        SPI),
+	FUNC(SPI1_D6,        SPI),
+	FUNC(SPI1_D7,        SPI),
+	FUNC(SPI1_SS0,       OUT),
+	FUNC(SPI1_SS1,       OUT),
+	FUNC(SPI1_SS2,       OUT),
+	FUNC(SPI1_SS3,       OUT),
+	FUNC(SPI1_ARB,       IN_TIE),
+	FUNC(SPI1_SCLK,      OUT),
+	FUNC(SPI2_D0,        SPI),
+	FUNC(SPI2_SS,        IN),
+	FUNC(SPI2_SCLK,      IN),
+	FUNC(I2S0_MCLK,      OUT),
+	FUNC(I2S0_SCLK,      OUT),
+	FUNC(I2S0_WS,        OUT),
+	FUNC(I2S0_IN_D0,     IN),
+	FUNC(I2S0_IN_D1,     IN),
+	FUNC(I2S0_IN_D2,     IN),
+	FUNC(I2S0_IN_D3,     IN),
+	FUNC(I2S0_OUT_D0,    OUT),
+	FUNC(I2S0_OUT_D1,    OUT),
+	FUNC(I2S0_OUT_D2,    OUT),
+	FUNC(I2S0_OUT_D3,    OUT),
+	FUNC(I2S1_MCLK,      OUT),
+	FUNC(I2S1_SCLK,      OUT),
+	FUNC(I2S1_WS,        OUT),
+	FUNC(I2S1_IN_D0,     IN),
+	FUNC(I2S1_IN_D1,     IN),
+	FUNC(I2S1_IN_D2,     IN),
+	FUNC(I2S1_IN_D3,     IN),
+	FUNC(I2S1_OUT_D0,    OUT),
+	FUNC(I2S1_OUT_D1,    OUT),
+	FUNC(I2S1_OUT_D2,    OUT),
+	FUNC(I2S1_OUT_D3,    OUT),
+	FUNC(I2S2_MCLK,      OUT),
+	FUNC(I2S2_SCLK,      OUT),
+	FUNC(I2S2_WS,        OUT),
+	FUNC(I2S2_IN_D0,     IN),
+	FUNC(I2S2_IN_D1,     IN),
+	FUNC(I2S2_IN_D2,     IN),
+	FUNC(I2S2_IN_D3,     IN),
+	FUNC(I2S2_OUT_D0,    OUT),
+	FUNC(I2S2_OUT_D1,    OUT),
+	FUNC(I2S2_OUT_D2,    OUT),
+	FUNC(I2S2_OUT_D3,    OUT),
+	FUNC(RESV0,          DISABLED),
+	FUNC(RESV1,          DISABLED),
+	FUNC(RESV2,          DISABLED),
+	FUNC(RESV3,          DISABLED),
+	FUNC(RESV4,          DISABLED),
+	FUNC(RESV5,          DISABLED),
+	FUNC(I2C0_SCLK,      I2C),
+	FUNC(I2C0_SDA,       I2C),
+	FUNC(I2C1_SCLK,      I2C),
+	FUNC(I2C1_SDA,       I2C),
+	FUNC(I2C2_SCLK,      I2C),
+	FUNC(I2C2_SDA,       I2C),
+	FUNC(DVP_XCLK,       OUT),
+	FUNC(DVP_RST,        OUT),
+	FUNC(DVP_PWDN,       OUT),
+	FUNC(DVP_VSYNC,      IN),
+	FUNC(DVP_HREF,       IN),
+	FUNC(DVP_PCLK,       IN),
+	FUNC(DVP_D0,         IN),
+	FUNC(DVP_D1,         IN),
+	FUNC(DVP_D2,         IN),
+	FUNC(DVP_D3,         IN),
+	FUNC(DVP_D4,         IN),
+	FUNC(DVP_D5,         IN),
+	FUNC(DVP_D6,         IN),
+	FUNC(DVP_D7,         IN),
+	FUNC(SCCB_SCLK,      I2C),
+	FUNC(SCCB_SDA,       I2C),
+	FUNC(UART1_CTS,      IN),
+	FUNC(UART1_DSR,      IN),
+	FUNC(UART1_DCD,      IN),
+	FUNC(UART1_RI,       IN),
+	FUNC(UART1_SIR_IN,   IN),
+	FUNC(UART1_DTR,      OUT),
+	FUNC(UART1_RTS,      OUT),
+	FUNC(UART1_OUT2,     OUT),
+	FUNC(UART1_OUT1,     OUT),
+	FUNC(UART1_SIR_OUT,  OUT),
+	FUNC(UART1_BAUD,     OUT),
+	FUNC(UART1_RE,       OUT),
+	FUNC(UART1_DE,       OUT),
+	FUNC(UART1_RS485_EN, OUT),
+	FUNC(UART2_CTS,      IN),
+	FUNC(UART2_DSR,      IN),
+	FUNC(UART2_DCD,      IN),
+	FUNC(UART2_RI,       IN),
+	FUNC(UART2_SIR_IN,   IN),
+	FUNC(UART2_DTR,      OUT),
+	FUNC(UART2_RTS,      OUT),
+	FUNC(UART2_OUT2,     OUT),
+	FUNC(UART2_OUT1,     OUT),
+	FUNC(UART2_SIR_OUT,  OUT),
+	FUNC(UART2_BAUD,     OUT),
+	FUNC(UART2_RE,       OUT),
+	FUNC(UART2_DE,       OUT),
+	FUNC(UART2_RS485_EN, OUT),
+	FUNC(UART3_CTS,      IN),
+	FUNC(UART3_DSR,      IN),
+	FUNC(UART3_DCD,      IN),
+	FUNC(UART3_RI,       IN),
+	FUNC(UART3_SIR_IN,   IN),
+	FUNC(UART3_DTR,      OUT),
+	FUNC(UART3_RTS,      OUT),
+	FUNC(UART3_OUT2,     OUT),
+	FUNC(UART3_OUT1,     OUT),
+	FUNC(UART3_SIR_OUT,  OUT),
+	FUNC(UART3_BAUD,     OUT),
+	FUNC(UART3_RE,       OUT),
+	FUNC(UART3_DE,       OUT),
+	FUNC(UART3_RS485_EN, OUT),
+	FUNC(TIMER0_TOGGLE1, OUT),
+	FUNC(TIMER0_TOGGLE2, OUT),
+	FUNC(TIMER0_TOGGLE3, OUT),
+	FUNC(TIMER0_TOGGLE4, OUT),
+	FUNC(TIMER1_TOGGLE1, OUT),
+	FUNC(TIMER1_TOGGLE2, OUT),
+	FUNC(TIMER1_TOGGLE3, OUT),
+	FUNC(TIMER1_TOGGLE4, OUT),
+	FUNC(TIMER2_TOGGLE1, OUT),
+	FUNC(TIMER2_TOGGLE2, OUT),
+	FUNC(TIMER2_TOGGLE3, OUT),
+	FUNC(TIMER2_TOGGLE4, OUT),
+	FUNC(CLK_SPI2,       OUT),
+	FUNC(CLK_I2C2,       OUT),
+	FUNC(INTERNAL0,      OUT),
+	FUNC(INTERNAL1,      OUT),
+	FUNC(INTERNAL2,      OUT),
+	FUNC(INTERNAL3,      OUT),
+	FUNC(INTERNAL4,      OUT),
+	FUNC(INTERNAL5,      OUT),
+	FUNC(INTERNAL6,      OUT),
+	FUNC(INTERNAL7,      OUT),
+	FUNC(INTERNAL8,      OUT),
+	FUNC(INTERNAL9,      IN),
+	FUNC(INTERNAL10,     IN),
+	FUNC(INTERNAL11,     IN),
+	FUNC(INTERNAL12,     IN),
+	FUNC(INTERNAL13,     INT13),
+	FUNC(INTERNAL14,     I2C),
+	FUNC(INTERNAL15,     IN),
+	FUNC(INTERNAL16,     IN),
+	FUNC(INTERNAL17,     IN),
+	FUNC(CONSTANT,       DISABLED),
+	FUNC(INTERNAL18,     IN),
+	FUNC(DEBUG0,         OUT),
+	FUNC(DEBUG1,         OUT),
+	FUNC(DEBUG2,         OUT),
+	FUNC(DEBUG3,         OUT),
+	FUNC(DEBUG4,         OUT),
+	FUNC(DEBUG5,         OUT),
+	FUNC(DEBUG6,         OUT),
+	FUNC(DEBUG7,         OUT),
+	FUNC(DEBUG8,         OUT),
+	FUNC(DEBUG9,         OUT),
+	FUNC(DEBUG10,        OUT),
+	FUNC(DEBUG11,        OUT),
+	FUNC(DEBUG12,        OUT),
+	FUNC(DEBUG13,        OUT),
+	FUNC(DEBUG14,        OUT),
+	FUNC(DEBUG15,        OUT),
+	FUNC(DEBUG16,        OUT),
+	FUNC(DEBUG17,        OUT),
+	FUNC(DEBUG18,        OUT),
+	FUNC(DEBUG19,        OUT),
+	FUNC(DEBUG20,        OUT),
+	FUNC(DEBUG21,        OUT),
+	FUNC(DEBUG22,        OUT),
+	FUNC(DEBUG23,        OUT),
+	FUNC(DEBUG24,        OUT),
+	FUNC(DEBUG25,        OUT),
+	FUNC(DEBUG26,        OUT),
+	FUNC(DEBUG27,        OUT),
+	FUNC(DEBUG28,        OUT),
+	FUNC(DEBUG29,        OUT),
+	FUNC(DEBUG30,        OUT),
+	FUNC(DEBUG31,        OUT),
+};
+#undef FUNC
+
+int k210_pc_get_functions_count(struct udevice *dev)
+{
+	return ARRAY_SIZE(k210_pcf_infos);
+}
+
+static const char *k210_pc_get_function_name(struct udevice *dev,
+					     unsigned selector)
+{
+	return k210_pcf_infos[selector].name;
+}
+
+static int k210_pc_pinmux_set(struct udevice *dev, unsigned pin_selector,
+			      unsigned func_selector)
+{
+	struct k210_pc_priv *priv = dev_get_priv(dev);
+	const struct k210_pcf_info *info = &k210_pcf_infos[func_selector];
+	u32 mode = k210_pc_mode_id_to_mode[info->mode_id];
+	u32 val = func_selector | mode;
+
+	log_debug("setting pin %s to %s with mode %.8x\n",
+		  k210_pc_pin_names[pin_selector], info->name, mode);
+	writel(val, &priv->fpioa->pins[pin_selector]);
+	return 0;
+}
+
+/* Max drive strength in uA */
+static const int k210_pc_drive_strength[] = {
+	[0] = 11200,
+	[1] = 16800,
+	[2] = 22300,
+	[3] = 27800,
+	[4] = 33300,
+	[5] = 38700,
+	[6] = 44100,
+	[7] = 49500,
+};
+
+static int k210_pc_get_drive(unsigned max_strength_ua)
+{
+	int i;
+
+	for (i = K210_PC_DRIVE_MAX; i; i--)
+		if (k210_pc_drive_strength[i] < max_strength_ua)
+			return i;
+
+	return -EINVAL;
+}
+
+static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector,
+			       unsigned param, unsigned argument)
+{
+	struct k210_pc_priv *priv = dev_get_priv(dev);
+	u32 val = readl(&priv->fpioa->pins[pin_selector]);
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		val &= ~K210_PC_BIAS_MASK;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (argument)
+			val |= K210_PC_PD;
+		else
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		if (argument)
+			val |= K210_PC_PD;
+		else
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		argument *= 1000;
+	case PIN_CONFIG_DRIVE_STRENGTH_UA: {
+		int drive = k210_pc_get_drive(argument);
+
+		if (IS_ERR_VALUE(drive))
+			return drive;
+		val &= ~K210_PC_DRIVE_MASK;
+		val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive);
+		break;
+	}
+	case PIN_CONFIG_INPUT_ENABLE:
+		if (argument)
+			val |= K210_PC_IE;
+		else
+			val &= ~K210_PC_IE;
+		break;
+	case PIN_CONFIG_INPUT_SCHMITT:
+		argument = 1;
+	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+		if (argument)
+			val |= K210_PC_ST;
+		else
+			val &= ~K210_PC_ST;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		k210_pc_pinmux_set(dev, pin_selector, K210_PCF_CONSTANT);
+		val = readl(&priv->fpioa->pins[pin_selector]);
+		val |= K210_PC_MODE_OUT;
+
+		if (!argument)
+			val |= K210_PC_DO_INV;
+		break;
+	case PIN_CONFIG_OUTPUT_ENABLE:
+		if (argument)
+			val |= K210_PC_OE;
+		else
+			val &= ~K210_PC_OE;
+		break;
+	case PIN_CONFIG_SLEW_RATE:
+		if (argument)
+			val |= K210_PC_SL;
+		else
+			val &= ~K210_PC_SL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(val, &priv->fpioa->pins[pin_selector]);
+	return 0;
+}
+
+static int k210_pc_pinconf_group_set(struct udevice *dev,
+				     unsigned group_selector, unsigned param,
+				     unsigned argument)
+{
+	struct k210_pc_priv *priv = dev_get_priv(dev);
+
+	if (param == PIN_CONFIG_POWER_SOURCE) {
+		u32 bit = BIT(group_selector);
+
+		regmap_update_bits(priv->sysctl, priv->power_offset, bit,
+				   argument ? bit : 0);
+	} else {
+		int i, ret;
+
+		for (i = 0; i < 6; i++) {
+			ret = k210_pc_pinconf_set(dev, group_selector * 6 + i,
+						  param, argument);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int k210_pc_get_pin_muxing(struct udevice *dev, unsigned int selector,
+				  char *buf, int size)
+{
+	struct k210_pc_priv *priv = dev_get_priv(dev);
+	u32 val = readl(&priv->fpioa->pins[selector]);
+	const struct k210_pcf_info *info = &k210_pcf_infos[val & K210_PCF_MASK];
+
+	strncpy(buf, info->name, min((size_t)size, sizeof(info->name)));
+	return 0;
+}
+
+static const struct pinconf_param k210_pc_pinconf_params[] = {
+	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+	{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+	{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX },
+	{ "drive-strength-ua", PIN_CONFIG_DRIVE_STRENGTH_UA, U32_MAX },
+	{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+	{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
+	{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+	{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+	{ "power-source", PIN_CONFIG_POWER_SOURCE, K210_PC_POWER_1V8 },
+	{ "output-low", PIN_CONFIG_OUTPUT, 0 },
+	{ "output-high", PIN_CONFIG_OUTPUT, 1 },
+	{ "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
+	{ "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
+	{ "slew-rate", PIN_CONFIG_SLEW_RATE, 1 },
+};
+
+static const struct pinctrl_ops k210_pc_pinctrl_ops = {
+	.get_pins_count = k210_pc_get_pins_count,
+	.get_pin_name = k210_pc_get_pin_name,
+	.get_groups_count = k210_pc_get_groups_count,
+	.get_group_name = k210_pc_get_group_name,
+	.get_functions_count = k210_pc_get_functions_count,
+	.get_function_name = k210_pc_get_function_name,
+	.pinmux_set = k210_pc_pinmux_set,
+	.pinconf_num_params = ARRAY_SIZE(k210_pc_pinconf_params),
+	.pinconf_params = k210_pc_pinconf_params,
+	.pinconf_set = k210_pc_pinconf_set,
+	.pinconf_group_set = k210_pc_pinconf_group_set,
+	.set_state = pinctrl_generic_set_state,
+	.get_pin_muxing = k210_pc_get_pin_muxing,
+};
+
+static int k210_pc_probe(struct udevice *dev)
+{
+	int ret, i, j;
+	struct k210_pc_priv *priv = dev_get_priv(dev);
+
+	priv->fpioa = dev_read_addr_ptr(dev);
+	if (!priv->fpioa)
+		return -EINVAL;
+
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret && ret != -ENOSYS && ret != -ENOTSUPP)
+		goto err;
+
+	priv->sysctl = syscon_regmap_lookup_by_phandle(dev, "kendryte,sysctl");
+	if (IS_ERR(priv->sysctl)) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = dev_read_u32(dev, "kendryte,power-offset", &priv->power_offset);
+	if (ret)
+		goto err;
+
+	log_debug("fpioa = %p sysctl = %p power offset = %x\n", priv->fpioa,
+		  (void *)priv->sysctl->ranges[0].start, priv->power_offset);
+
+	/* Disable all pins */
+	for (i = 6; i < ARRAY_SIZE(k210_pc_pin_names); i++)
+		k210_pc_pinmux_set(dev, i, K210_PCF_CONSTANT);
+
+	/* Init input ties */
+	for (i = 0; i < ARRAY_SIZE(priv->fpioa->tie_en); i++) {
+		u32 val = 0;
+
+		for (j = 0; j < 32; j++)
+			if (k210_pcf_infos[i * 32 + j].mode_id ==
+			    K210_PC_DEFAULT_IN_TIE)
+				val |= BIT(j);
+		writel(val, &priv->fpioa->tie_en[i]);
+		writel(val, &priv->fpioa->tie_val[i]);
+	}
+
+	return 0;
+
+err:
+	clk_free(&priv->clk);
+	return ret;
+}
+
+static const struct udevice_id k210_pc_ids[] = {
+	{ .compatible = "kendryte,k210-fpioa" },
+	{ }
+};
+
+U_BOOT_DRIVER(pinctrl_k210) = {
+	.name = "pinctrl_k210",
+	.id = UCLASS_PINCTRL,
+	.of_match = k210_pc_ids,
+	.probe = k210_pc_probe,
+	.priv_auto_alloc_size = sizeof(struct k210_pc_priv),
+	.ops = &k210_pc_pinctrl_ops,
+};
diff --git a/drivers/pinctrl/kendryte/pinctrl.h b/drivers/pinctrl/kendryte/pinctrl.h
new file mode 100644
index 0000000000..de434700c3
--- /dev/null
+++ b/drivers/pinctrl/kendryte/pinctrl.h
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef K210_PINCTRL_H
+#define K210_PINCTRL_H
+
+#include <linux/bitfield.h>
+
+/*
+ * Full list of PinCtrl Functions from
+ * kendryte-standalone-sdk/lib/drivers/include/fpioa.h
+ */
+#define K210_PCF_SHIFT 0
+#define K210_PCF_MASK GENMASK(7, 0)
+#define K210_PCF_JTAG_TCLK      0   /* JTAG Test Clock */
+#define K210_PCF_JTAG_TDI       1   /* JTAG Test Data In */
+#define K210_PCF_JTAG_TMS       2   /* JTAG Test Mode Select */
+#define K210_PCF_JTAG_TDO       3   /* JTAG Test Data Out */
+#define K210_PCF_SPI0_D0        4   /* SPI0 Data 0 */
+#define K210_PCF_SPI0_D1        5   /* SPI0 Data 1 */
+#define K210_PCF_SPI0_D2        6   /* SPI0 Data 2 */
+#define K210_PCF_SPI0_D3        7   /* SPI0 Data 3 */
+#define K210_PCF_SPI0_D4        8   /* SPI0 Data 4 */
+#define K210_PCF_SPI0_D5        9   /* SPI0 Data 5 */
+#define K210_PCF_SPI0_D6        10  /* SPI0 Data 6 */
+#define K210_PCF_SPI0_D7        11  /* SPI0 Data 7 */
+#define K210_PCF_SPI0_SS0       12  /* SPI0 Chip Select 0 */
+#define K210_PCF_SPI0_SS1       13  /* SPI0 Chip Select 1 */
+#define K210_PCF_SPI0_SS2       14  /* SPI0 Chip Select 2 */
+#define K210_PCF_SPI0_SS3       15  /* SPI0 Chip Select 3 */
+#define K210_PCF_SPI0_ARB       16  /* SPI0 Arbitration */
+#define K210_PCF_SPI0_SCLK      17  /* SPI0 Serial Clock */
+#define K210_PCF_UARTHS_RX      18  /* UART High speed Receiver */
+#define K210_PCF_UARTHS_TX      19  /* UART High speed Transmitter */
+#define K210_PCF_RESV6          20  /* Reserved function */
+#define K210_PCF_RESV7          21  /* Reserved function */
+#define K210_PCF_CLK_SPI1       22  /* Clock SPI1 */
+#define K210_PCF_CLK_I2C1       23  /* Clock I2C1 */
+#define K210_PCF_GPIOHS0        24  /* GPIO High speed 0 */
+#define K210_PCF_GPIOHS1        25  /* GPIO High speed 1 */
+#define K210_PCF_GPIOHS2        26  /* GPIO High speed 2 */
+#define K210_PCF_GPIOHS3        27  /* GPIO High speed 3 */
+#define K210_PCF_GPIOHS4        28  /* GPIO High speed 4 */
+#define K210_PCF_GPIOHS5        29  /* GPIO High speed 5 */
+#define K210_PCF_GPIOHS6        30  /* GPIO High speed 6 */
+#define K210_PCF_GPIOHS7        31  /* GPIO High speed 7 */
+#define K210_PCF_GPIOHS8        32  /* GPIO High speed 8 */
+#define K210_PCF_GPIOHS9        33  /* GPIO High speed 9 */
+#define K210_PCF_GPIOHS10       34  /* GPIO High speed 10 */
+#define K210_PCF_GPIOHS11       35  /* GPIO High speed 11 */
+#define K210_PCF_GPIOHS12       36  /* GPIO High speed 12 */
+#define K210_PCF_GPIOHS13       37  /* GPIO High speed 13 */
+#define K210_PCF_GPIOHS14       38  /* GPIO High speed 14 */
+#define K210_PCF_GPIOHS15       39  /* GPIO High speed 15 */
+#define K210_PCF_GPIOHS16       40  /* GPIO High speed 16 */
+#define K210_PCF_GPIOHS17       41  /* GPIO High speed 17 */
+#define K210_PCF_GPIOHS18       42  /* GPIO High speed 18 */
+#define K210_PCF_GPIOHS19       43  /* GPIO High speed 19 */
+#define K210_PCF_GPIOHS20       44  /* GPIO High speed 20 */
+#define K210_PCF_GPIOHS21       45  /* GPIO High speed 21 */
+#define K210_PCF_GPIOHS22       46  /* GPIO High speed 22 */
+#define K210_PCF_GPIOHS23       47  /* GPIO High speed 23 */
+#define K210_PCF_GPIOHS24       48  /* GPIO High speed 24 */
+#define K210_PCF_GPIOHS25       49  /* GPIO High speed 25 */
+#define K210_PCF_GPIOHS26       50  /* GPIO High speed 26 */
+#define K210_PCF_GPIOHS27       51  /* GPIO High speed 27 */
+#define K210_PCF_GPIOHS28       52  /* GPIO High speed 28 */
+#define K210_PCF_GPIOHS29       53  /* GPIO High speed 29 */
+#define K210_PCF_GPIOHS30       54  /* GPIO High speed 30 */
+#define K210_PCF_GPIOHS31       55  /* GPIO High speed 31 */
+#define K210_PCF_GPIO0          56  /* GPIO pin 0 */
+#define K210_PCF_GPIO1          57  /* GPIO pin 1 */
+#define K210_PCF_GPIO2          58  /* GPIO pin 2 */
+#define K210_PCF_GPIO3          59  /* GPIO pin 3 */
+#define K210_PCF_GPIO4          60  /* GPIO pin 4 */
+#define K210_PCF_GPIO5          61  /* GPIO pin 5 */
+#define K210_PCF_GPIO6          62  /* GPIO pin 6 */
+#define K210_PCF_GPIO7          63  /* GPIO pin 7 */
+#define K210_PCF_UART1_RX       64  /* UART1 Receiver */
+#define K210_PCF_UART1_TX       65  /* UART1 Transmitter */
+#define K210_PCF_UART2_RX       66  /* UART2 Receiver */
+#define K210_PCF_UART2_TX       67  /* UART2 Transmitter */
+#define K210_PCF_UART3_RX       68  /* UART3 Receiver */
+#define K210_PCF_UART3_TX       69  /* UART3 Transmitter */
+#define K210_PCF_SPI1_D0        70  /* SPI1 Data 0 */
+#define K210_PCF_SPI1_D1        71  /* SPI1 Data 1 */
+#define K210_PCF_SPI1_D2        72  /* SPI1 Data 2 */
+#define K210_PCF_SPI1_D3        73  /* SPI1 Data 3 */
+#define K210_PCF_SPI1_D4        74  /* SPI1 Data 4 */
+#define K210_PCF_SPI1_D5        75  /* SPI1 Data 5 */
+#define K210_PCF_SPI1_D6        76  /* SPI1 Data 6 */
+#define K210_PCF_SPI1_D7        77  /* SPI1 Data 7 */
+#define K210_PCF_SPI1_SS0       78  /* SPI1 Chip Select 0 */
+#define K210_PCF_SPI1_SS1       79  /* SPI1 Chip Select 1 */
+#define K210_PCF_SPI1_SS2       80  /* SPI1 Chip Select 2 */
+#define K210_PCF_SPI1_SS3       81  /* SPI1 Chip Select 3 */
+#define K210_PCF_SPI1_ARB       82  /* SPI1 Arbitration */
+#define K210_PCF_SPI1_SCLK      83  /* SPI1 Serial Clock */
+#define K210_PCF_SPI2_D0        84  /* SPI2 Data 0 */
+#define K210_PCF_SPI2_SS        85  /* SPI2 Select */
+#define K210_PCF_SPI2_SCLK      86  /* SPI2 Serial Clock */
+#define K210_PCF_I2S0_MCLK      87  /* I2S0 Master Clock */
+#define K210_PCF_I2S0_SCLK      88  /* I2S0 Serial Clock(BCLK) */
+#define K210_PCF_I2S0_WS        89  /* I2S0 Word Select(LRCLK) */
+#define K210_PCF_I2S0_IN_D0     90  /* I2S0 Serial Data Input 0 */
+#define K210_PCF_I2S0_IN_D1     91  /* I2S0 Serial Data Input 1 */
+#define K210_PCF_I2S0_IN_D2     92  /* I2S0 Serial Data Input 2 */
+#define K210_PCF_I2S0_IN_D3     93  /* I2S0 Serial Data Input 3 */
+#define K210_PCF_I2S0_OUT_D0    94  /* I2S0 Serial Data Output 0 */
+#define K210_PCF_I2S0_OUT_D1    95  /* I2S0 Serial Data Output 1 */
+#define K210_PCF_I2S0_OUT_D2    96  /* I2S0 Serial Data Output 2 */
+#define K210_PCF_I2S0_OUT_D3    97  /* I2S0 Serial Data Output 3 */
+#define K210_PCF_I2S1_MCLK      98  /* I2S1 Master Clock */
+#define K210_PCF_I2S1_SCLK      99  /* I2S1 Serial Clock(BCLK) */
+#define K210_PCF_I2S1_WS        100 /* I2S1 Word Select(LRCLK) */
+#define K210_PCF_I2S1_IN_D0     101 /* I2S1 Serial Data Input 0 */
+#define K210_PCF_I2S1_IN_D1     102 /* I2S1 Serial Data Input 1 */
+#define K210_PCF_I2S1_IN_D2     103 /* I2S1 Serial Data Input 2 */
+#define K210_PCF_I2S1_IN_D3     104 /* I2S1 Serial Data Input 3 */
+#define K210_PCF_I2S1_OUT_D0    105 /* I2S1 Serial Data Output 0 */
+#define K210_PCF_I2S1_OUT_D1    106 /* I2S1 Serial Data Output 1 */
+#define K210_PCF_I2S1_OUT_D2    107 /* I2S1 Serial Data Output 2 */
+#define K210_PCF_I2S1_OUT_D3    108 /* I2S1 Serial Data Output 3 */
+#define K210_PCF_I2S2_MCLK      109 /* I2S2 Master Clock */
+#define K210_PCF_I2S2_SCLK      110 /* I2S2 Serial Clock(BCLK) */
+#define K210_PCF_I2S2_WS        111 /* I2S2 Word Select(LRCLK) */
+#define K210_PCF_I2S2_IN_D0     112 /* I2S2 Serial Data Input 0 */
+#define K210_PCF_I2S2_IN_D1     113 /* I2S2 Serial Data Input 1 */
+#define K210_PCF_I2S2_IN_D2     114 /* I2S2 Serial Data Input 2 */
+#define K210_PCF_I2S2_IN_D3     115 /* I2S2 Serial Data Input 3 */
+#define K210_PCF_I2S2_OUT_D0    116 /* I2S2 Serial Data Output 0 */
+#define K210_PCF_I2S2_OUT_D1    117 /* I2S2 Serial Data Output 1 */
+#define K210_PCF_I2S2_OUT_D2    118 /* I2S2 Serial Data Output 2 */
+#define K210_PCF_I2S2_OUT_D3    119 /* I2S2 Serial Data Output 3 */
+#define K210_PCF_RESV0          120 /* Reserved function */
+#define K210_PCF_RESV1          121 /* Reserved function */
+#define K210_PCF_RESV2          122 /* Reserved function */
+#define K210_PCF_RESV3          123 /* Reserved function */
+#define K210_PCF_RESV4          124 /* Reserved function */
+#define K210_PCF_RESV5          125 /* Reserved function */
+#define K210_PCF_I2C0_SCLK      126 /* I2C0 Serial Clock */
+#define K210_PCF_I2C0_SDA       127 /* I2C0 Serial Data */
+#define K210_PCF_I2C1_SCLK      128 /* I2C1 Serial Clock */
+#define K210_PCF_I2C1_SDA       129 /* I2C1 Serial Data */
+#define K210_PCF_I2C2_SCLK      130 /* I2C2 Serial Clock */
+#define K210_PCF_I2C2_SDA       131 /* I2C2 Serial Data */
+#define K210_PCF_DVP_XCLK       132 /* DVP System Clock */
+#define K210_PCF_DVP_RST        133 /* DVP System Reset */
+#define K210_PCF_DVP_PWDN       134 /* DVP Power Down Mode */
+#define K210_PCF_DVP_VSYNC      135 /* DVP Vertical Sync */
+#define K210_PCF_DVP_HREF       136 /* DVP Horizontal Reference output */
+#define K210_PCF_DVP_PCLK       137 /* Pixel Clock */
+#define K210_PCF_DVP_D0         138 /* Data Bit 0 */
+#define K210_PCF_DVP_D1         139 /* Data Bit 1 */
+#define K210_PCF_DVP_D2         140 /* Data Bit 2 */
+#define K210_PCF_DVP_D3         141 /* Data Bit 3 */
+#define K210_PCF_DVP_D4         142 /* Data Bit 4 */
+#define K210_PCF_DVP_D5         143 /* Data Bit 5 */
+#define K210_PCF_DVP_D6         144 /* Data Bit 6 */
+#define K210_PCF_DVP_D7         145 /* Data Bit 7 */
+#define K210_PCF_SCCB_SCLK      146 /* Serial Camera Control Bus Clock */
+#define K210_PCF_SCCB_SDA       147 /* Serial Camera Control Bus Data */
+#define K210_PCF_UART1_CTS      148 /* UART1 Clear To Send */
+#define K210_PCF_UART1_DSR      149 /* UART1 Data Set Ready */
+#define K210_PCF_UART1_DCD      150 /* UART1 Data Carrier Detect */
+#define K210_PCF_UART1_RI       151 /* UART1 Ring Indicator */
+#define K210_PCF_UART1_SIR_IN   152 /* UART1 Serial Infrared Input */
+#define K210_PCF_UART1_DTR      153 /* UART1 Data Terminal Ready */
+#define K210_PCF_UART1_RTS      154 /* UART1 Request To Send */
+#define K210_PCF_UART1_OUT2     155 /* UART1 User-designated Output 2 */
+#define K210_PCF_UART1_OUT1     156 /* UART1 User-designated Output 1 */
+#define K210_PCF_UART1_SIR_OUT  157 /* UART1 Serial Infrared Output */
+#define K210_PCF_UART1_BAUD     158 /* UART1 Transmit Clock Output */
+#define K210_PCF_UART1_RE       159 /* UART1 Receiver Output Enable */
+#define K210_PCF_UART1_DE       160 /* UART1 Driver Output Enable */
+#define K210_PCF_UART1_RS485_EN 161 /* UART1 RS485 Enable */
+#define K210_PCF_UART2_CTS      162 /* UART2 Clear To Send */
+#define K210_PCF_UART2_DSR      163 /* UART2 Data Set Ready */
+#define K210_PCF_UART2_DCD      164 /* UART2 Data Carrier Detect */
+#define K210_PCF_UART2_RI       165 /* UART2 Ring Indicator */
+#define K210_PCF_UART2_SIR_IN   166 /* UART2 Serial Infrared Input */
+#define K210_PCF_UART2_DTR      167 /* UART2 Data Terminal Ready */
+#define K210_PCF_UART2_RTS      168 /* UART2 Request To Send */
+#define K210_PCF_UART2_OUT2     169 /* UART2 User-designated Output 2 */
+#define K210_PCF_UART2_OUT1     170 /* UART2 User-designated Output 1 */
+#define K210_PCF_UART2_SIR_OUT  171 /* UART2 Serial Infrared Output */
+#define K210_PCF_UART2_BAUD     172 /* UART2 Transmit Clock Output */
+#define K210_PCF_UART2_RE       173 /* UART2 Receiver Output Enable */
+#define K210_PCF_UART2_DE       174 /* UART2 Driver Output Enable */
+#define K210_PCF_UART2_RS485_EN 175 /* UART2 RS485 Enable */
+#define K210_PCF_UART3_CTS      176 /* UART3 Clear To Send */
+#define K210_PCF_UART3_DSR      177 /* UART3 Data Set Ready */
+#define K210_PCF_UART3_DCD      178 /* UART3 Data Carrier Detect */
+#define K210_PCF_UART3_RI       179 /* UART3 Ring Indicator */
+#define K210_PCF_UART3_SIR_IN   180 /* UART3 Serial Infrared Input */
+#define K210_PCF_UART3_DTR      181 /* UART3 Data Terminal Ready */
+#define K210_PCF_UART3_RTS      182 /* UART3 Request To Send */
+#define K210_PCF_UART3_OUT2     183 /* UART3 User-designated Output 2 */
+#define K210_PCF_UART3_OUT1     184 /* UART3 User-designated Output 1 */
+#define K210_PCF_UART3_SIR_OUT  185 /* UART3 Serial Infrared Output */
+#define K210_PCF_UART3_BAUD     186 /* UART3 Transmit Clock Output */
+#define K210_PCF_UART3_RE       187 /* UART3 Receiver Output Enable */
+#define K210_PCF_UART3_DE       188 /* UART3 Driver Output Enable */
+#define K210_PCF_UART3_RS485_EN 189 /* UART3 RS485 Enable */
+#define K210_PCF_TIMER0_TOGGLE1 190 /* TIMER0 Toggle Output 1 */
+#define K210_PCF_TIMER0_TOGGLE2 191 /* TIMER0 Toggle Output 2 */
+#define K210_PCF_TIMER0_TOGGLE3 192 /* TIMER0 Toggle Output 3 */
+#define K210_PCF_TIMER0_TOGGLE4 193 /* TIMER0 Toggle Output 4 */
+#define K210_PCF_TIMER1_TOGGLE1 194 /* TIMER1 Toggle Output 1 */
+#define K210_PCF_TIMER1_TOGGLE2 195 /* TIMER1 Toggle Output 2 */
+#define K210_PCF_TIMER1_TOGGLE3 196 /* TIMER1 Toggle Output 3 */
+#define K210_PCF_TIMER1_TOGGLE4 197 /* TIMER1 Toggle Output 4 */
+#define K210_PCF_TIMER2_TOGGLE1 198 /* TIMER2 Toggle Output 1 */
+#define K210_PCF_TIMER2_TOGGLE2 199 /* TIMER2 Toggle Output 2 */
+#define K210_PCF_TIMER2_TOGGLE3 200 /* TIMER2 Toggle Output 3 */
+#define K210_PCF_TIMER2_TOGGLE4 201 /* TIMER2 Toggle Output 4 */
+#define K210_PCF_CLK_SPI2       202 /* Clock SPI2 */
+#define K210_PCF_CLK_I2C2       203 /* Clock I2C2 */
+#define K210_PCF_INTERNAL0      204 /* Internal function signal 0 */
+#define K210_PCF_INTERNAL1      205 /* Internal function signal 1 */
+#define K210_PCF_INTERNAL2      206 /* Internal function signal 2 */
+#define K210_PCF_INTERNAL3      207 /* Internal function signal 3 */
+#define K210_PCF_INTERNAL4      208 /* Internal function signal 4 */
+#define K210_PCF_INTERNAL5      209 /* Internal function signal 5 */
+#define K210_PCF_INTERNAL6      210 /* Internal function signal 6 */
+#define K210_PCF_INTERNAL7      211 /* Internal function signal 7 */
+#define K210_PCF_INTERNAL8      212 /* Internal function signal 8 */
+#define K210_PCF_INTERNAL9      213 /* Internal function signal 9 */
+#define K210_PCF_INTERNAL10     214 /* Internal function signal 10 */
+#define K210_PCF_INTERNAL11     215 /* Internal function signal 11 */
+#define K210_PCF_INTERNAL12     216 /* Internal function signal 12 */
+#define K210_PCF_INTERNAL13     217 /* Internal function signal 13 */
+#define K210_PCF_INTERNAL14     218 /* Internal function signal 14 */
+#define K210_PCF_INTERNAL15     219 /* Internal function signal 15 */
+#define K210_PCF_INTERNAL16     220 /* Internal function signal 16 */
+#define K210_PCF_INTERNAL17     221 /* Internal function signal 17 */
+#define K210_PCF_CONSTANT       222 /* Constant function */
+#define K210_PCF_INTERNAL18     223 /* Internal function signal 18 */
+#define K210_PCF_DEBUG0         224 /* Debug function 0 */
+#define K210_PCF_DEBUG1         225 /* Debug function 1 */
+#define K210_PCF_DEBUG2         226 /* Debug function 2 */
+#define K210_PCF_DEBUG3         227 /* Debug function 3 */
+#define K210_PCF_DEBUG4         228 /* Debug function 4 */
+#define K210_PCF_DEBUG5         229 /* Debug function 5 */
+#define K210_PCF_DEBUG6         230 /* Debug function 6 */
+#define K210_PCF_DEBUG7         231 /* Debug function 7 */
+#define K210_PCF_DEBUG8         232 /* Debug function 8 */
+#define K210_PCF_DEBUG9         233 /* Debug function 9 */
+#define K210_PCF_DEBUG10        234 /* Debug function 10 */
+#define K210_PCF_DEBUG11        235 /* Debug function 11 */
+#define K210_PCF_DEBUG12        236 /* Debug function 12 */
+#define K210_PCF_DEBUG13        237 /* Debug function 13 */
+#define K210_PCF_DEBUG14        238 /* Debug function 14 */
+#define K210_PCF_DEBUG15        239 /* Debug function 15 */
+#define K210_PCF_DEBUG16        240 /* Debug function 16 */
+#define K210_PCF_DEBUG17        241 /* Debug function 17 */
+#define K210_PCF_DEBUG18        242 /* Debug function 18 */
+#define K210_PCF_DEBUG19        243 /* Debug function 19 */
+#define K210_PCF_DEBUG20        244 /* Debug function 20 */
+#define K210_PCF_DEBUG21        245 /* Debug function 21 */
+#define K210_PCF_DEBUG22        246 /* Debug function 22 */
+#define K210_PCF_DEBUG23        247 /* Debug function 23 */
+#define K210_PCF_DEBUG24        248 /* Debug function 24 */
+#define K210_PCF_DEBUG25        249 /* Debug function 25 */
+#define K210_PCF_DEBUG26        250 /* Debug function 26 */
+#define K210_PCF_DEBUG27        251 /* Debug function 27 */
+#define K210_PCF_DEBUG28        252 /* Debug function 28 */
+#define K210_PCF_DEBUG29        253 /* Debug function 29 */
+#define K210_PCF_DEBUG30        254 /* Debug function 30 */
+#define K210_PCF_DEBUG31        255 /* Debug function 31 */
+
+/*
+ * The K210 only implements 8 drive levels, even though there is register space
+ * for 16
+ */
+#define K210_PC_DRIVE_SHIFT 8
+#define K210_PC_DRIVE_MASK GENMASK(11, 8)
+#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_MAX 7
+
+#define K210_PC_MODE_MASK GENMASK(23, 12)
+/*
+ * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE) where FUNCTION_OE is a
+ * physical signal from the function
+ */
+#define K210_PC_OE       BIT(12) /* Output Enable */
+#define K210_PC_OE_INV   BIT(13) /* INVert function-controlled Output Enable */
+#define K210_PC_DO_OE    BIT(14) /* set Data Out to the Output Enable signal */
+#define K210_PC_DO_INV   BIT(15) /* INVert final Data Output */
+#define K210_PC_PU       BIT(16) /* Pull Up */
+#define K210_PC_PD       BIT(17) /* Pull Down */
+/* Strong pull up not implemented on K210 */
+#define K210_PC_SL       BIT(19) /* reduce SLew rate to prevent overshoot */
+/* Same semantics as OE above */
+#define K210_PC_IE       BIT(20) /* Input Enable */
+#define K210_PC_IE_INV   BIT(21) /* INVert function-controlled Input Enable */
+#define K210_PC_DI_INV   BIT(22) /* INVert Data Input */
+#define K210_PC_ST       BIT(23) /* Schmitt Trigger */
+#define K210_PC_DI       BIT(31) /* raw Data Input */
+#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD)
+
+#define K210_PC_MODE_IN   (K210_PC_IE | K210_PC_ST)
+#define K210_PC_MODE_OUT  (K210_PC_DRIVE_7 | K210_PC_OE)
+#define K210_PC_MODE_I2C  (K210_PC_MODE_IN | K210_PC_IE_INV | K210_PC_SL | \
+			   K210_PC_OE | K210_PC_OE_INV | K210_PC_PU)
+#define K210_PC_MODE_SPI  (K210_PC_MODE_IN | K210_PC_IE_INV | \
+			   K210_PC_MODE_OUT | K210_PC_OE_INV)
+#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT)
+
+struct k210_fpioa {
+	u32 pins[48];
+	u32 tie_en[8];
+	u32 tie_val[8];
+};
+
+#endif /* K210_PINCTRL_H */
diff --git a/include/dt-bindings/pinctrl/k210-pinctrl.h b/include/dt-bindings/pinctrl/k210-pinctrl.h
new file mode 100644
index 0000000000..6d44ea765b
--- /dev/null
+++ b/include/dt-bindings/pinctrl/k210-pinctrl.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef DT_K210_PINCTRL_H
+#define DT_K210_PINCTRL_H
+
+#define K210_PC_POWER_3V3 0
+#define K210_PC_POWER_1V8 1
+
+#endif /* DT_K210_PINCTRL_H */
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 14/33] gpio: sifive: Use generic reg read function
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (12 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  2:24   ` Bin Meng
  2020-02-28 21:05 ` [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer Sean Anderson
                   ` (18 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Using an fdt-specific function causes problems with a live tree.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/gpio/sifive-gpio.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpio/sifive-gpio.c b/drivers/gpio/sifive-gpio.c
index 76d5a1d34e..111f759d2e 100644
--- a/drivers/gpio/sifive-gpio.c
+++ b/drivers/gpio/sifive-gpio.c
@@ -158,7 +158,7 @@ static int sifive_gpio_ofdata_to_platdata(struct udevice *dev)
 	struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
 	fdt_addr_t addr;
 
-	addr = devfdt_get_addr(dev);
+	addr = dev_read_addr(dev);
 	if (addr == FDT_ADDR_T_NONE)
 		return -EINVAL;
 
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (13 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 14/33] gpio: sifive: Use generic reg read function Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  2:27   ` Bin Meng
  2020-02-28 21:05 ` [PATCH v5 16/33] gpio: dw: Add a trailing underscore to generated name Sean Anderson
                   ` (17 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Explicitly cast fdt_addr_t to a void pointer, since we pass it to readl.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/gpio/dwapb_gpio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c
index e3439eebb5..56dcad36f0 100644
--- a/drivers/gpio/dwapb_gpio.c
+++ b/drivers/gpio/dwapb_gpio.c
@@ -38,7 +38,7 @@ struct gpio_dwapb_platdata {
 	const char	*name;
 	int		bank;
 	int		pins;
-	fdt_addr_t	base;
+	void __iomem	*base;
 };
 
 static int dwapb_gpio_direction_input(struct udevice *dev, unsigned pin)
@@ -174,7 +174,7 @@ static int gpio_dwapb_bind(struct udevice *dev)
 		if (!plat)
 			return -ENOMEM;
 
-		plat->base = base;
+		plat->base = (void *)base;
 		plat->bank = bank;
 		plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0);
 
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 16/33] gpio: dw: Add a trailing underscore to generated name
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (14 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 17/33] gpio: dw: Return output value when direction is out Sean Anderson
                   ` (16 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Previously, if there was no bank-name property, it was easy to have
confusing gpio names like "gpio1 at 08", instead of "gpio1 at 0_8". This patch
follows the example of the sifive gpio driver.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/gpio/dwapb_gpio.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c
index 56dcad36f0..09abab5c39 100644
--- a/drivers/gpio/dwapb_gpio.c
+++ b/drivers/gpio/dwapb_gpio.c
@@ -184,7 +184,15 @@ static int gpio_dwapb_bind(struct udevice *dev)
 			 * Fall back to node name. This means accessing pins
 			 * via bank name won't work.
 			 */
-			plat->name = ofnode_get_name(node);
+			char name[32];
+
+			snprintf(name, sizeof(name), "%s_",
+				 ofnode_get_name(node));
+			plat->name = strdup(name);
+			if (!plat->name) {
+				kfree(plat);
+				return -ENOMEM;
+			}
 		}
 
 		ret = device_bind_ofnode(dev, dev->driver, plat->name,
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 17/33] gpio: dw: Return output value when direction is out
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (15 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 16/33] gpio: dw: Add a trailing underscore to generated name Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 18/33] led: gpio: Default to using node name if label is absent Sean Anderson
                   ` (15 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

dm_gpio_ops.get_value can be called when the gpio is either input or
output. The current dw code always returns the input value, which is
invalid if the direction is set to out.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/gpio/dwapb_gpio.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c
index 09abab5c39..058c5fbfbf 100644
--- a/drivers/gpio/dwapb_gpio.c
+++ b/drivers/gpio/dwapb_gpio.c
@@ -64,13 +64,6 @@ static int dwapb_gpio_direction_output(struct udevice *dev, unsigned pin,
 	return 0;
 }
 
-static int dwapb_gpio_get_value(struct udevice *dev, unsigned pin)
-{
-	struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
-	return !!(readl(plat->base + GPIO_EXT_PORT(plat->bank)) & (1 << pin));
-}
-
-
 static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
 {
 	struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
@@ -96,6 +89,18 @@ static int dwapb_gpio_get_function(struct udevice *dev, unsigned offset)
 		return GPIOF_INPUT;
 }
 
+static int dwapb_gpio_get_value(struct udevice *dev, unsigned pin)
+{
+	struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+	u32 value;
+
+	if (dwapb_gpio_get_function(dev, pin) == GPIOF_OUTPUT)
+		value = readl(plat->base + GPIO_SWPORT_DR(plat->bank));
+	else
+		value = readl(plat->base + GPIO_EXT_PORT(plat->bank));
+	return !!(value & BIT(pin));
+}
+
 static const struct dm_gpio_ops gpio_dwapb_ops = {
 	.direction_input	= dwapb_gpio_direction_input,
 	.direction_output	= dwapb_gpio_direction_output,
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 18/33] led: gpio: Default to using node name if label is absent
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (16 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 17/33] gpio: dw: Return output value when direction is out Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1 Sean Anderson
                   ` (14 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This more closely mirrors Linux's behaviour, and will make it easier to
transition to using function+color in the future.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/led/led_gpio.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c
index af6b8245c9..643d5e2116 100644
--- a/drivers/led/led_gpio.c
+++ b/drivers/led/led_gpio.c
@@ -98,11 +98,8 @@ static int led_gpio_bind(struct udevice *parent)
 		const char *label;
 
 		label = ofnode_read_string(node, "label");
-		if (!label) {
-			debug("%s: node %s has no label\n", __func__,
-			      ofnode_get_name(node));
-			return -EINVAL;
-		}
+		if (!label)
+			label = ofnode_get_name(node);
 		ret = device_bind_driver_to_node(parent, "gpio_led",
 						 ofnode_get_name(node),
 						 node, &dev);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (17 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 18/33] led: gpio: Default to using node name if label is absent Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:15   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 20/33] spi: dw: Rename "cs-gpio" to "cs-gpios" Sean Anderson
                   ` (13 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Some devices have different layouts for the fields in CTRL1 (e.g. the
Kendryte K210). Allow this layout to be configurable from the device tree.
The documentation has been taken from Linux.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---

Changes in v4:
- New

 .../spi/snps,dw-apb-ssi.txt                   | 43 +++++++++++++++++++
 drivers/spi/designware_spi.c                  | 40 ++++++++++-------
 2 files changed, 68 insertions(+), 15 deletions(-)
 create mode 100644 doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt

diff --git a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
new file mode 100644
index 0000000000..4b6152f6b7
--- /dev/null
+++ b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
@@ -0,0 +1,43 @@
+Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
+
+Required properties:
+- compatible : "snps,dw-apb-ssi"
+- reg : The register base for the controller. For "mscc,<soc>-spi", a second
+  register set is required (named ICPU_CFG:SPI_MST)
+- #address-cells : <1>, as required by generic SPI binding.
+- #size-cells : <0>, also as required by generic SPI binding.
+- clocks : phandles for the clocks, see the description of clock-names below.
+   The phandle for the "ssi_clk" is required. The phandle for the "pclk" clock
+   is optional. If a single clock is specified but no clock-name, it is the
+   "ssi_clk" clock. If both clocks are listed, the "ssi_clk" must be first.
+
+Optional properties:
+- clock-names : Contains the names of the clocks:
+    "ssi_clk", for the core clock used to generate the external SPI clock.
+    "pclk", the interface clock, required for register access.
+- cs-gpios : Specifies the gpio pins to be used for chipselects.
+- num-cs : The number of chipselects. If omitted, this will default to 4.
+- reg-io-width : The I/O register width (in bytes) implemented by this
+  device.  Supported values are 2 or 4 (the default).
+- snps,dfs-offset The offset in bits of the DFS field in CTRL0, defaulting to 0
+- snps,frf-offset The offset in bits of the FRF field in CTRL0, defaulting to 4
+- snps,tmod-offset The offset in bits of the tmode field in CTRL0, defaulting
+  to 6
+- snps,mode-offset The offset in bits of the work mode field in CTRL0,
+  defaulting to 8
+
+Child nodes as per the generic SPI binding.
+
+Example:
+
+	spi at fff00000 {
+		compatible = "snps,dw-apb-ssi";
+		reg = <0xfff00000 0x1000>;
+		interrupts = <0 154 4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&spi_m_clk>;
+		num-cs = <2>;
+		cs-gpios = <&gpio0 13 0>,
+			   <&gpio0 14 0>;
+	};
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 2dc16736a3..6e1c289297 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -3,6 +3,7 @@
  * Designware master SPI core controller driver
  *
  * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
  *
  * Very loosely based on the Linux driver:
  * drivers/spi/spi-dw.c, which is:
@@ -51,20 +52,14 @@
 #define DW_SPI_DR			0x60
 
 /* Bit fields in CTRLR0 */
-#define SPI_DFS_OFFSET			0
-
-#define SPI_FRF_OFFSET			4
 #define SPI_FRF_SPI			0x0
 #define SPI_FRF_SSP			0x1
 #define SPI_FRF_MICROWIRE		0x2
 #define SPI_FRF_RESV			0x3
 
-#define SPI_MODE_OFFSET			6
-#define SPI_SCPH_OFFSET			6
-#define SPI_SCOL_OFFSET			7
+#define SPI_MODE_SCPH			0x1
+#define SPI_MODE_SCOL			0x2
 
-#define SPI_TMOD_OFFSET			8
-#define SPI_TMOD_MASK			(0x3 << SPI_TMOD_OFFSET)
 #define	SPI_TMOD_TR			0x0		/* xmit & recv */
 #define SPI_TMOD_TO			0x1		/* xmit only */
 #define SPI_TMOD_RO			0x2		/* recv only */
@@ -89,6 +84,12 @@
 struct dw_spi_platdata {
 	s32 frequency;		/* Default clock frequency, -1 for none */
 	void __iomem *regs;
+
+	/* Offsets in CTRL0 */
+	u8 dfs_off;
+	u8 frf_off;
+	u8 tmod_off;
+	u8 mode_off;
 };
 
 struct dw_spi_priv {
@@ -115,6 +116,15 @@ struct dw_spi_priv {
 	struct reset_ctl_bulk	resets;
 };
 
+static inline u32 GEN_CTRL0(struct dw_spi_priv *priv,
+			    struct dw_spi_platdata *plat)
+{
+	return ((priv->bits_per_word - 1) << plat->dfs_off |
+	      (priv->type << plat->frf_off) |
+	      (priv->mode << plat->mode_off) |
+	      (priv->tmode << plat->tmod_off));
+}
+
 static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset)
 {
 	return __raw_readl(priv->regs + offset);
@@ -160,6 +170,10 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus)
 	/* Use 500KHz as a suitable default */
 	plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
 					       500000);
+	plat->dfs_off = dev_read_u32_default(bus, "snps,dfs-offset", 0);
+	plat->frf_off = dev_read_u32_default(bus, "snps,frf-offset", 4);
+	plat->mode_off = dev_read_u32_default(bus, "snps,mode-offset", 6);
+	plat->tmod_off = dev_read_u32_default(bus, "snps,tmod-offset", 8);
 	debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs,
 	      plat->frequency);
 
@@ -388,6 +402,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 		       const void *dout, void *din, unsigned long flags)
 {
 	struct udevice *bus = dev->parent;
+	struct dw_spi_platdata *plat = dev_get_platdata(bus);
 	struct dw_spi_priv *priv = dev_get_priv(bus);
 	const u8 *tx = dout;
 	u8 *rx = din;
@@ -406,10 +421,6 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	if (flags & SPI_XFER_BEGIN)
 		external_cs_manage(dev, false);
 
-	cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) |
-		(priv->mode << SPI_MODE_OFFSET) |
-		(priv->tmode << SPI_TMOD_OFFSET);
-
 	if (rx && tx)
 		priv->tmode = SPI_TMOD_TR;
 	else if (rx)
@@ -421,8 +432,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 		 */
 		priv->tmode = SPI_TMOD_TR;
 
-	cr0 &= ~SPI_TMOD_MASK;
-	cr0 |= (priv->tmode << SPI_TMOD_OFFSET);
+	cr0 = GEN_CTRL0(priv, plat);
 
 	priv->len = bitlen >> 3;
 	debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len);
@@ -476,7 +486,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
 {
-	struct dw_spi_platdata *plat = bus->platdata;
+	struct dw_spi_platdata *plat = dev_get_platdata(bus);
 	struct dw_spi_priv *priv = dev_get_priv(bus);
 	u16 clk_div;
 
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 20/33] spi: dw: Rename "cs-gpio" to "cs-gpios"
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (18 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1 Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 21/33] spi: dw: Use generic function to read reg address Sean Anderson
                   ` (12 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This property is named differently than other SPI drivers with the same
property, as well as the property as used in Linux.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 arch/arc/dts/axs10x_mb.dtsi  |  3 ++-
 arch/arc/dts/hsdk.dts        |  3 ++-
 drivers/spi/designware_spi.c | 10 +++-------
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/arch/arc/dts/axs10x_mb.dtsi b/arch/arc/dts/axs10x_mb.dtsi
index 5b77642b8d..50e62788cf 100644
--- a/arch/arc/dts/axs10x_mb.dtsi
+++ b/arch/arc/dts/axs10x_mb.dtsi
@@ -97,7 +97,8 @@
 			spi-max-frequency = <4000000>;
 			clocks = <&apbclk>;
 			clock-names = "spi_clk";
-			cs-gpio = <&cs_gpio 0>;
+			num-cs = <1>;
+			cs-gpios = <&cs_gpio 0>;
 			spi_flash at 0 {
 				compatible = "jedec,spi-nor";
 				reg = <0>;
diff --git a/arch/arc/dts/hsdk.dts b/arch/arc/dts/hsdk.dts
index 34ef3a620a..719ffdbce5 100644
--- a/arch/arc/dts/hsdk.dts
+++ b/arch/arc/dts/hsdk.dts
@@ -120,7 +120,8 @@
 		spi-max-frequency = <4000000>;
 		clocks = <&cgu_clk CLK_SYS_SPI_REF>;
 		clock-names = "spi_clk";
-		cs-gpio = <&cs_gpio 0>;
+		num-cs = <1>;
+		cs-gpios = <&cs_gpio 0>;
 		spi_flash at 0 {
 			compatible = "jedec,spi-nor";
 			reg = <0>;
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 6e1c289297..765fa2f582 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -142,7 +142,8 @@ static int request_gpio_cs(struct udevice *bus)
 	int ret;
 
 	/* External chip select gpio line is optional */
-	ret = gpio_request_by_name(bus, "cs-gpio", 0, &priv->cs_gpio, 0);
+	ret = gpio_request_by_name(bus, "cs-gpios", 0, &priv->cs_gpio,
+				   GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
 	if (ret == -ENOENT)
 		return 0;
 
@@ -151,12 +152,7 @@ static int request_gpio_cs(struct udevice *bus)
 		return ret;
 	}
 
-	if (dm_gpio_is_valid(&priv->cs_gpio)) {
-		dm_gpio_set_dir_flags(&priv->cs_gpio,
-				      GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
-	}
-
-	debug("%s: used external gpio for CS management\n", __func__);
+	debug("%s: using external gpio for CS management\n", __func__);
 #endif
 	return 0;
 }
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 21/33] spi: dw: Use generic function to read reg address
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (19 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 20/33] spi: dw: Rename "cs-gpio" to "cs-gpios" Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 22/33] spi: dw: Speed up transfer loops Sean Anderson
                   ` (11 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Using an fdt-specific function causes problems when compiled with a live
tree.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/spi/designware_spi.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 765fa2f582..38c24fe550 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -161,7 +161,9 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus)
 {
 	struct dw_spi_platdata *plat = bus->platdata;
 
-	plat->regs = (struct dw_spi *)devfdt_get_addr(bus);
+	plat->regs = dev_read_addr_ptr(bus);
+	if (!plat->regs)
+		return -EINVAL;
 
 	/* Use 500KHz as a suitable default */
 	plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 22/33] spi: dw: Speed up transfer loops
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (20 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 21/33] spi: dw: Use generic function to read reg address Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving Sean Anderson
                   ` (10 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The transfer loops are very tight on some platforms (especially on higher
speeds). If we don't read/write fast enough we can run into over-/under-
flow problems. This patch removes several divisions and log statements,
and simplifies the read logic.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/spi/designware_spi.c | 29 +++++++++--------------------
 1 file changed, 9 insertions(+), 20 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 38c24fe550..613eb0d0e6 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -304,7 +304,7 @@ static inline u32 tx_max(struct dw_spi_priv *priv)
 {
 	u32 tx_left, tx_room, rxtx_gap;
 
-	tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3);
+	tx_left = priv->tx_end - priv->tx;
 	tx_room = priv->fifo_len - dw_read(priv, DW_SPI_TXFLR);
 
 	/*
@@ -315,8 +315,7 @@ static inline u32 tx_max(struct dw_spi_priv *priv)
 	 * shift registers. So a control from sw point of
 	 * view is taken.
 	 */
-	rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx)) /
-		(priv->bits_per_word >> 3);
+	rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx));
 
 	return min3(tx_left, tx_room, (u32)(priv->fifo_len - rxtx_gap));
 }
@@ -324,7 +323,7 @@ static inline u32 tx_max(struct dw_spi_priv *priv)
 /* Return the max entries we should read out of rx fifo */
 static inline u32 rx_max(struct dw_spi_priv *priv)
 {
-	u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3);
+	u32 rx_left = priv->rx_end - priv->rx;
 
 	return min_t(u32, rx_left, dw_read(priv, DW_SPI_RXFLR));
 }
@@ -336,15 +335,10 @@ static void dw_writer(struct dw_spi_priv *priv)
 
 	while (max--) {
 		/* Set the tx word if the transfer's original "tx" is not null */
-		if (priv->tx_end - priv->len) {
-			if (priv->bits_per_word == 8)
-				txw = *(u8 *)(priv->tx);
-			else
-				txw = *(u16 *)(priv->tx);
-		}
+		if (priv->tx_end - priv->len)
+			txw = *(u8 *)(priv->tx);
 		dw_write(priv, DW_SPI_DR, txw);
-		debug("%s: tx=0x%02x\n", __func__, txw);
-		priv->tx += priv->bits_per_word >> 3;
+		priv->tx++;
 	}
 }
 
@@ -355,16 +349,11 @@ static void dw_reader(struct dw_spi_priv *priv)
 
 	while (max--) {
 		rxw = dw_read(priv, DW_SPI_DR);
-		debug("%s: rx=0x%02x\n", __func__, rxw);
 
 		/* Care about rx if the transfer's original "rx" is not null */
-		if (priv->rx_end - priv->len) {
-			if (priv->bits_per_word == 8)
-				*(u8 *)(priv->rx) = rxw;
-			else
-				*(u16 *)(priv->rx) = rxw;
-		}
-		priv->rx += priv->bits_per_word >> 3;
+		if (priv->rx_end - priv->len)
+			*(u8 *)(priv->rx) = rxw;
+		priv->rx++;
 	}
 }
 
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (21 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 22/33] spi: dw: Speed up transfer loops Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-29 17:47   ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 24/33] spi: dw: Add mem_ops Sean Anderson
                   ` (9 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The difference between rx and rx_end is used by tx_max when calculating how
much to write. If we aren't reading anything, this could cause us to let
the tx fifo bottom out.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 drivers/spi/designware_spi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 613eb0d0e6..b80e99ee7f 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -427,7 +427,8 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	priv->tx = (void *)tx;
 	priv->tx_end = priv->tx + priv->len;
 	priv->rx = rx;
-	priv->rx_end = priv->rx + priv->len;
+	/* If we aren't recieving, rx_end needs to be valid for tx_max() */
+	priv->rx_end = priv->rx + (rx ? priv->len : 0);
 
 	/* Disable controller before writing control registers */
 	spi_enable_chip(priv, 0);
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 24/33] spi: dw: Add mem_ops
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (22 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h Sean Anderson
                   ` (8 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The designware ssi device has "broken" chip select behaviour [1], and needs
specific manipulation to use the built-in chip select. The existing fix is
to use an external GPIO for chip select, but typically the K210 has SPI3
directly connected to a flash chip with dedicated pins. This makes it
impossible to use the spi_xfer function to use spi, since the CS is
de-asserted in between calls.  This patch adds an implementation of
exec_op, which gives correct behaviour when reading/writing spi flash.

Work on this device has been difficult because the only example code I have
to work off is Kendryte's sdk, and I do not have access to the datasheet
(if anyone does, I would love to have a look!). The MMC device is still not
working, but I have been making progress.

[1] https://lkml.org/lkml/2015/12/23/132

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Set rx and rx_end when writing

Changes in v4:
- New

 drivers/spi/designware_spi.c | 130 ++++++++++++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index b80e99ee7f..7a03e66530 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <malloc.h>
 #include <spi.h>
+#include <spi-mem.h>
 #include <fdtdec.h>
 #include <reset.h>
 #include <dm/device_compat.h>
@@ -108,8 +109,8 @@ struct dw_spi_priv {
 	int len;
 
 	u32 fifo_len;		/* depth of the FIFO buffer */
-	void *tx;
-	void *tx_end;
+	const void *tx;
+	const void *tx_end;
 	void *rx;
 	void *rx_end;
 
@@ -428,7 +429,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	priv->tx_end = priv->tx + priv->len;
 	priv->rx = rx;
 	/* If we aren't recieving, rx_end needs to be valid for tx_max() */
-	priv->rx_end = priv->rx + (rx ? priv->len : 0);
+	priv->rx_end = priv->rx + priv->len;
 
 	/* Disable controller before writing control registers */
 	spi_enable_chip(priv, 0);
@@ -472,6 +473,124 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	return ret;
 }
 
+/*
+ * This function is necessary for reading SPI flash with the native CS
+ * c.f. https://lkml.org/lkml/2015/12/23/132
+ */
+static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
+{
+	bool read = op->data.dir == SPI_MEM_DATA_IN;
+	int pos, i, ret = 0;
+	struct udevice *bus = slave->dev->parent;
+	struct dw_spi_platdata *plat = dev_get_platdata(bus);
+	struct dw_spi_priv *priv = dev_get_priv(bus);
+	u8 op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
+	u8 op_buf[op_len];
+	u32 cr0;
+
+	if (read)
+		priv->tmode = SPI_TMOD_EPROMREAD;
+	else
+		priv->tmode = SPI_TMOD_TO;
+
+	debug("%s: buf=%p len=%u [bytes]\n",
+	      __func__, op->data.buf.in, op->data.nbytes);
+
+	cr0 = GEN_CTRL0(priv, plat);
+	debug("%s: cr0=%08x\n", __func__, cr0);
+
+	spi_enable_chip(priv, 0);
+	dw_write(priv, DW_SPI_CTRL0, cr0);
+	if (read)
+		dw_write(priv, DW_SPI_CTRL1, op->data.nbytes - 1);
+	spi_enable_chip(priv, 1);
+
+	/* From spi_mem_exec_op */
+	pos = 0;
+	op_buf[pos++] = op->cmd.opcode;
+	if (op->addr.nbytes) {
+		for (i = 0; i < op->addr.nbytes; i++)
+			op_buf[pos + i] = op->addr.val >>
+				(8 * (op->addr.nbytes - i - 1));
+
+		pos += op->addr.nbytes;
+	}
+	if (op->dummy.nbytes)
+		memset(op_buf + pos, 0xff, op->dummy.nbytes);
+
+	priv->tx = &op_buf;
+	priv->tx_end = priv->tx + op_len;
+	priv->rx = NULL;
+	priv->rx_end = NULL;
+	while (priv->tx != priv->tx_end) {
+		dw_writer(priv);
+		/* This loop needs a delay otherwise we can hang */
+		udelay(1);
+	}
+
+	/*
+	 * XXX: The following are tight loops! Enabling debug messages may cause
+	 * them to fail because we are not reading/writing the fifo fast enough.
+	 *
+	 * We heuristically break out of the loop when we stop getting data.
+	 * This is to stop us from hanging if the device doesn't send any data
+	 * (either at all, or after sending a response). For example, one flash
+	 * chip I tested did not send anything back after the first 64K of data.
+	 */
+	if (read) {
+		/* If we have gotten any data back yet */
+		bool got_data = false;
+		/* How many times we have looped without reading anything */
+		int loops_since_read = 0;
+		struct spi_mem_op *mut_op = (struct spi_mem_op *)op;
+
+		priv->rx = op->data.buf.in;
+		priv->rx_end = priv->rx + op->data.nbytes;
+
+		dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev));
+		while (priv->rx != priv->rx_end) {
+			void *last_rx = priv->rx;
+
+			dw_reader(priv);
+			if (priv->rx == last_rx) {
+				loops_since_read++;
+				/* Thresholds are arbitrary */
+				if (loops_since_read > 256)
+					break;
+				else if (got_data && loops_since_read > 32)
+					break;
+			} else {
+				got_data = true;
+				loops_since_read = 0;
+			}
+		}
+
+		/* Update with the actual amount of data read */
+		mut_op->data.nbytes -= priv->rx_end - priv->rx;
+	} else {
+		u32 val;
+
+		priv->tx = op->data.buf.out;
+		priv->tx_end = priv->tx + op->data.nbytes;
+
+		/* Fill up the write fifo before starting the transfer */
+		dw_writer(priv);
+		dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev));
+		while (priv->tx != priv->tx_end)
+			dw_writer(priv);
+
+		if (readl_poll_timeout(priv->regs + DW_SPI_SR, val,
+				       (val & SR_TF_EMPT) && !(val & SR_BUSY),
+				       RX_TIMEOUT * 1000)) {
+			ret = -ETIMEDOUT;
+		}
+	}
+
+	dw_write(priv, DW_SPI_SER, 0);
+	debug("%s: %u bytes xfered\n", __func__, op->data.nbytes);
+	return ret;
+}
+
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
 {
 	struct dw_spi_platdata *plat = dev_get_platdata(bus);
@@ -535,8 +654,13 @@ static int dw_spi_remove(struct udevice *bus)
 	return 0;
 }
 
+static const struct spi_controller_mem_ops dw_spi_mem_ops = {
+	.exec_op = dw_spi_exec_op,
+};
+
 static const struct dm_spi_ops dw_spi_ops = {
 	.xfer		= dw_spi_xfer,
+	.mem_ops	= &dw_spi_mem_ops,
 	.set_speed	= dw_spi_set_speed,
 	.set_mode	= dw_spi_set_mode,
 	/*
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (23 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 24/33] spi: dw: Add mem_ops Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:46   ` Marek Vasut
  2020-03-03  6:58   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 26/33] riscv: Add headers for asm/global_data.h Sean Anderson
                   ` (7 subsequent siblings)
  32 siblings, 2 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This header is needed outside of the arm architecture for the designware
watchdog.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
This patch previously appeared as
https://patchwork.ozlabs.org/patch/1232411/

Changes in v5:
- New
- Include linux/err.h explicitly

 arch/arm/cpu/armv7/cache_v7.c                  | 2 +-
 arch/arm/mach-davinci/spl.c                    | 2 +-
 arch/arm/mach-omap2/clocks-common.c            | 2 +-
 arch/arm/mach-omap2/emif-common.c              | 2 +-
 arch/arm/mach-omap2/omap4/emif.c               | 2 +-
 arch/arm/mach-omap2/omap5/dra7xx_iodelay.c     | 2 +-
 arch/arm/mach-omap2/omap5/emif.c               | 2 +-
 arch/arm/mach-omap2/omap5/hwinit.c             | 2 +-
 arch/arm/mach-socfpga/spl_a10.c                | 2 +-
 arch/arm/mach-socfpga/spl_agilex.c             | 2 +-
 arch/arm/mach-socfpga/spl_gen5.c               | 2 +-
 arch/arm/mach-socfpga/spl_s10.c                | 2 +-
 drivers/watchdog/designware_wdt.c              | 3 ++-
 arch/arm/include/asm/utils.h => include/log2.h | 4 ++--
 14 files changed, 16 insertions(+), 15 deletions(-)
 rename arch/arm/include/asm/utils.h => include/log2.h (93%)

diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c
index 99eb7db342..049a27cc92 100644
--- a/arch/arm/cpu/armv7/cache_v7.c
+++ b/arch/arm/cpu/armv7/cache_v7.c
@@ -8,7 +8,7 @@
 #include <linux/types.h>
 #include <common.h>
 #include <asm/armv7.h>
-#include <asm/utils.h>
+#include <log2.h>
 
 #define ARMV7_DCACHE_INVAL_RANGE	1
 #define ARMV7_DCACHE_CLEAN_INVAL_RANGE	2
diff --git a/arch/arm/mach-davinci/spl.c b/arch/arm/mach-davinci/spl.c
index d44e840aa0..c1a62b662d 100644
--- a/arch/arm/mach-davinci/spl.c
+++ b/arch/arm/mach-davinci/spl.c
@@ -8,7 +8,7 @@
 #include <hang.h>
 #include <spl.h>
 #include <asm/u-boot.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <nand.h>
 #include <asm/arch/dm365_lowlevel.h>
 #include <ns16550.h>
diff --git a/arch/arm/mach-omap2/clocks-common.c b/arch/arm/mach-omap2/clocks-common.c
index 9aff83e9df..67c62e8eb0 100644
--- a/arch/arm/mach-omap2/clocks-common.c
+++ b/arch/arm/mach-omap2/clocks-common.c
@@ -19,7 +19,7 @@
 #include <asm/gpio.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/sys_proto.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <asm/omap_gpio.h>
 #include <asm/emif.h>
 
diff --git a/arch/arm/mach-omap2/emif-common.c b/arch/arm/mach-omap2/emif-common.c
index 4658f67e84..2762934092 100644
--- a/arch/arm/mach-omap2/emif-common.c
+++ b/arch/arm/mach-omap2/emif-common.c
@@ -17,7 +17,7 @@
 #include <asm/arch/sys_proto.h>
 #include <asm/omap_common.h>
 #include <asm/omap_sec_common.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <linux/compiler.h>
 #include <asm/ti-common/ti-edma3.h>
 
diff --git a/arch/arm/mach-omap2/omap4/emif.c b/arch/arm/mach-omap2/omap4/emif.c
index 35a51645be..d2b530535e 100644
--- a/arch/arm/mach-omap2/omap4/emif.c
+++ b/arch/arm/mach-omap2/omap4/emif.c
@@ -11,7 +11,7 @@
 #include <common.h>
 #include <asm/emif.h>
 #include <asm/arch/sys_proto.h>
-#include <asm/utils.h>
+#include <log2.h>
 
 #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
 u32 *const T_num = (u32 *)OMAP_SRAM_SCRATCH_EMIF_T_NUM;
diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
index 598074ba21..607188bcae 100644
--- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
+++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
@@ -8,7 +8,7 @@
 
 #include <common.h>
 #include <hang.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <asm/arch/dra7xx_iodelay.h>
 #include <asm/arch/omap.h>
 #include <asm/arch/sys_proto.h>
diff --git a/arch/arm/mach-omap2/omap5/emif.c b/arch/arm/mach-omap2/omap5/emif.c
index f3661a0e74..a5c74261c0 100644
--- a/arch/arm/mach-omap2/omap5/emif.c
+++ b/arch/arm/mach-omap2/omap5/emif.c
@@ -11,7 +11,7 @@
 #include <common.h>
 #include <asm/emif.h>
 #include <asm/arch/sys_proto.h>
-#include <asm/utils.h>
+#include <log2.h>
 
 #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
 #define print_timing_reg(reg) debug(#reg" - 0x%08x\n", (reg))
diff --git a/arch/arm/mach-omap2/omap5/hwinit.c b/arch/arm/mach-omap2/omap5/hwinit.c
index 3b1733099f..290449e918 100644
--- a/arch/arm/mach-omap2/omap5/hwinit.c
+++ b/arch/arm/mach-omap2/omap5/hwinit.c
@@ -19,7 +19,7 @@
 #include <asm/arch/sys_proto.h>
 #include <asm/arch/clock.h>
 #include <linux/sizes.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <asm/arch/gpio.h>
 #include <asm/emif.h>
 #include <asm/omap_common.h>
diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
index d9ef851054..0f24bec4ef 100644
--- a/arch/arm/mach-socfpga/spl_a10.c
+++ b/arch/arm/mach-socfpga/spl_a10.c
@@ -9,7 +9,7 @@
 #include <asm/io.h>
 #include <asm/pl310.h>
 #include <asm/u-boot.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <image.h>
 #include <asm/arch/reset_manager.h>
 #include <spl.h>
diff --git a/arch/arm/mach-socfpga/spl_agilex.c b/arch/arm/mach-socfpga/spl_agilex.c
index ecc1a35c49..54b16c9061 100644
--- a/arch/arm/mach-socfpga/spl_agilex.c
+++ b/arch/arm/mach-socfpga/spl_agilex.c
@@ -6,7 +6,7 @@
 
 #include <asm/io.h>
 #include <asm/u-boot.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <common.h>
 #include <hang.h>
 #include <image.h>
diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c
index a01e2a5cb9..bab609ad29 100644
--- a/arch/arm/mach-socfpga/spl_gen5.c
+++ b/arch/arm/mach-socfpga/spl_gen5.c
@@ -7,7 +7,7 @@
 #include <hang.h>
 #include <asm/io.h>
 #include <asm/u-boot.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <image.h>
 #include <asm/arch/reset_manager.h>
 #include <spl.h>
diff --git a/arch/arm/mach-socfpga/spl_s10.c b/arch/arm/mach-socfpga/spl_s10.c
index d89151d902..48c64ef47a 100644
--- a/arch/arm/mach-socfpga/spl_s10.c
+++ b/arch/arm/mach-socfpga/spl_s10.c
@@ -7,7 +7,7 @@
 #include <hang.h>
 #include <asm/io.h>
 #include <asm/u-boot.h>
-#include <asm/utils.h>
+#include <log2.h>
 #include <common.h>
 #include <debug_uart.h>
 #include <image.h>
diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
index 1024a04596..a58a2dfca4 100644
--- a/drivers/watchdog/designware_wdt.c
+++ b/drivers/watchdog/designware_wdt.c
@@ -9,7 +9,8 @@
 #include <reset.h>
 #include <wdt.h>
 #include <asm/io.h>
-#include <asm/utils.h>
+#include <log2.h>
+#include <watchdog.h>
 
 #define DW_WDT_CR	0x00
 #define DW_WDT_TORR	0x04
diff --git a/arch/arm/include/asm/utils.h b/include/log2.h
similarity index 93%
rename from arch/arm/include/asm/utils.h
rename to include/log2.h
index eee60c50e9..4f89d3dd76 100644
--- a/arch/arm/include/asm/utils.h
+++ b/include/log2.h
@@ -4,8 +4,8 @@
  * Texas Instruments, <www.ti.com>
  * Aneesh V <aneesh@ti.com>
  */
-#ifndef _UTILS_H_
-#define _UTILS_H_
+#ifndef _LOG2_H_
+#define _LOG2_H_
 
 static inline s32 log_2_n_round_up(u32 n)
 {
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 26/33] riscv: Add headers for asm/global_data.h
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (24 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI Sean Anderson
                   ` (6 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

This header depended on bd_t and ulong, but did not include the appropriate
headers.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
---

Changes in v4:
- Include compiler.h not linux/compiler.h

 arch/riscv/include/asm/global_data.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index b74bd7e738..7276d9763f 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -11,6 +11,8 @@
 #define __ASM_GBL_DATA_H
 
 #include <asm/smp.h>
+#include <asm/u-boot.h>
+#include <compiler.h>
 
 /* Architecture-specific global data */
 struct arch_global_data {
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (25 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 26/33] riscv: Add headers for asm/global_data.h Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-02  9:08   ` Rick Chen
  2020-03-02 23:17   ` Lukas Auer
  2020-02-28 21:05 ` [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9 Sean Anderson
                   ` (5 subsequent siblings)
  32 siblings, 2 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The IPI code could have race conditions in several places.
* Several harts could race on the value of gd->arch->clint/plic
* Non-boot harts could race with the main hart on the DM subsystem In
  addition, if an IPI was pending when U-Boot started, it would cause the
  IPI handler to jump to address 0.

To address these problems, a new function riscv_init_ipi is introduced. It
is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
functions may be called. Access is synchronized by gd->arch->ipi_ready.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- New

 arch/riscv/cpu/cpu.c                 |  9 ++++
 arch/riscv/include/asm/global_data.h |  1 +
 arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
 arch/riscv/lib/andes_plic.c          | 34 +++++---------
 arch/riscv/lib/sbi_ipi.c             |  5 ++
 arch/riscv/lib/sifive_clint.c        | 33 +++++---------
 arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
 7 files changed, 101 insertions(+), 92 deletions(-)

diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
index e457f6acbf..a971ec8694 100644
--- a/arch/riscv/cpu/cpu.c
+++ b/arch/riscv/cpu/cpu.c
@@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
 			csr_write(CSR_SATP, 0);
 	}
 
+#ifdef CONFIG_SMP
+	ret = riscv_init_ipi();
+	if (ret)
+		return ret;
+
+	/* Atomically set a flag enabling IPI handling */
+	WRITE_ONCE(gd->arch.ipi_ready, 1);
+#endif
+
 	return 0;
 }
 
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index 7276d9763f..b24f8fd2a7 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -28,6 +28,7 @@ struct arch_global_data {
 #endif
 #ifdef CONFIG_SMP
 	struct ipi_data ipi[CONFIG_NR_CPUS];
+	long ipi_ready; /* Set after riscv_init_ipi is called */
 #endif
 #ifndef CONFIG_XIP
 	ulong available_harts;
diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
index 74de92ed13..1b428856b2 100644
--- a/arch/riscv/include/asm/smp.h
+++ b/arch/riscv/include/asm/smp.h
@@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
  */
 int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
 
+/**
+ * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
+ *
+ * Platform code must provide this function. This function is called once after
+ * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
+ * before this function is called.
+ *
+ * @return 0 if OK, -ve on error
+ */
+int riscv_init_ipi(void);
+
+/**
+ * riscv_send_ipi() - Send inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of receiving hart
+ * @return 0 if OK, -ve on error
+ */
+int riscv_send_ipi(int hart);
+
+/**
+ * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of hart to be cleared
+ * @return 0 if OK, -ve on error
+ */
+int riscv_clear_ipi(int hart);
+
+/**
+ * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
+ *
+ * Platform code must provide this function.
+ *
+ * @hart: Hart ID of hart to be checked
+ * @pending: Pointer to variable with result of the check,
+ *           1 if IPI is pending, 0 otherwise
+ * @return 0 if OK, -ve on error
+ */
+int riscv_get_ipi(int hart, int *pending);
+
 #endif
diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
index 20529ab3eb..8484f76386 100644
--- a/arch/riscv/lib/andes_plic.c
+++ b/arch/riscv/lib/andes_plic.c
@@ -30,20 +30,6 @@
 #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
 
 DECLARE_GLOBAL_DATA_PTR;
-static int init_plic(void);
-
-#define PLIC_BASE_GET(void)						\
-	do {								\
-		long *ret;						\
-									\
-		if (!gd->arch.plic) {					\
-			ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
-			if (IS_ERR(ret))				\
-				return PTR_ERR(ret);			\
-			gd->arch.plic = ret;				\
-			init_plic();					\
-		}							\
-	} while (0)
 
 static int enable_ipi(int hart)
 {
@@ -93,13 +79,21 @@ static int init_plic(void)
 	return -ENODEV;
 }
 
+int riscv_init_ipi(void)
+{
+	int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
+
+	if (IS_ERR(ret))
+		return PTR_ERR(ret);
+	gd->arch.plic = ret;
+
+	return init_plic();
+}
+
 int riscv_send_ipi(int hart)
 {
-	unsigned int ipi;
+	unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
 
-	PLIC_BASE_GET();
-
-	ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
 	writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
 				gd->arch.boot_hart));
 
@@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
 {
 	u32 source_id;
 
-	PLIC_BASE_GET();
-
 	source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
 	writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
 
@@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
 
 int riscv_get_ipi(int hart, int *pending)
 {
-	PLIC_BASE_GET();
-
 	*pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
 						     gd->arch.boot_hart));
 	*pending = !!(*pending & SEND_IPI_TO_HART(hart));
diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
index 9a698ce74e..310d1bd2a4 100644
--- a/arch/riscv/lib/sbi_ipi.c
+++ b/arch/riscv/lib/sbi_ipi.c
@@ -7,6 +7,11 @@
 #include <common.h>
 #include <asm/sbi.h>
 
+int riscv_init_ipi(void)
+{
+	return 0;
+}
+
 int riscv_send_ipi(int hart)
 {
 	ulong mask;
diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
index 5e0d25720b..62c1b2b0ef 100644
--- a/arch/riscv/lib/sifive_clint.c
+++ b/arch/riscv/lib/sifive_clint.c
@@ -24,22 +24,8 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define CLINT_BASE_GET(void)						\
-	do {								\
-		long *ret;						\
-									\
-		if (!gd->arch.clint) {					\
-			ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
-			if (IS_ERR(ret))				\
-				return PTR_ERR(ret);			\
-			gd->arch.clint = ret;				\
-		}							\
-	} while (0)
-
 int riscv_get_time(u64 *time)
 {
-	CLINT_BASE_GET();
-
 	*time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
 
 	return 0;
@@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
 
 int riscv_set_timecmp(int hart, u64 cmp)
 {
-	CLINT_BASE_GET();
-
 	writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
 
 	return 0;
 }
 
+int riscv_init_ipi(void)
+{
+		long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
+
+		if (IS_ERR(ret))
+			return PTR_ERR(ret);
+		gd->arch.clint = ret;
+
+		return 0;
+}
+
 int riscv_send_ipi(int hart)
 {
-	CLINT_BASE_GET();
-
 	writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
 	return 0;
@@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
 
 int riscv_clear_ipi(int hart)
 {
-	CLINT_BASE_GET();
-
 	writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
 	return 0;
@@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
 
 int riscv_get_ipi(int hart, int *pending)
 {
-	CLINT_BASE_GET();
-
 	*pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
 
 	return 0;
diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
index 17adb35730..3b1e52e9b2 100644
--- a/arch/riscv/lib/smp.c
+++ b/arch/riscv/lib/smp.c
@@ -12,38 +12,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/**
- * riscv_send_ipi() - Send inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of receiving hart
- * @return 0 if OK, -ve on error
- */
-extern int riscv_send_ipi(int hart);
-
-/**
- * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of hart to be cleared
- * @return 0 if OK, -ve on error
- */
-extern int riscv_clear_ipi(int hart);
-
-/**
- * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
- *
- * Platform code must provide this function.
- *
- * @hart: Hart ID of hart to be checked
- * @pending: Pointer to variable with result of the check,
- *           1 if IPI is pending, 0 otherwise
- * @return 0 if OK, -ve on error
- */
-extern int riscv_get_ipi(int hart, int *pending);
-
 static int send_ipi_many(struct ipi_data *ipi, int wait)
 {
 	ofnode node, cpus;
@@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
 	int ret;
 	void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
 
-	if (hart >= CONFIG_NR_CPUS)
+	if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
 		return;
 
-	__smp_mb();
-
-	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
-	invalidate_icache_all();
-
 	/*
 	 * Clear the IPI to acknowledge the request before jumping to the
 	 * requested function.
 	 */
 	ret = riscv_clear_ipi(hart);
 	if (ret) {
-		pr_err("Cannot clear IPI of hart %ld\n", hart);
+		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
 		return;
 	}
 
+	__smp_mb();
+
+	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
+	/*
+	 * There may be an IPI raised before u-boot begins execution, so check
+	 * to ensure we actually have a function to call.
+	 */
+	if (!smp_function)
+		return;
+	log_debug("hart = %lu func = %p\n", hart, smp_function);
+	invalidate_icache_all();
+
 	smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
 }
 
 int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
 {
-	int ret = 0;
-	struct ipi_data ipi;
+	struct ipi_data ipi = {
+		.addr = addr,
+		.arg0 = arg0,
+		.arg1 = arg1,
+	};
 
-	ipi.addr = addr;
-	ipi.arg0 = arg0;
-	ipi.arg1 = arg1;
-
-	ret = send_ipi_many(&ipi, wait);
-
-	return ret;
+	return send_ipi_many(&ipi, wait);
 }
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (26 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:20   ` Rick Chen
  2020-02-28 21:05 ` [PATCH v5 29/33] riscv: Allow use of reset drivers Sean Anderson
                   ` (4 subsequent siblings)
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Some older processors (notably the Kendryte K210) use an older version of
the RISC-V privileged specification. The primary changes between the old
and new are in virtual memory, and in the merging of three separate counter
enable CSRs.  Using the new CSR on an old processor causes an illegal
instruction exception.  This patch adds an option to use the old CSRs
instead of the new one.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
---

Changes in v5:
- Rename to 1.9 to reflect the spec as implemented by the k210

Changes in v4:
- Fixed CSRs not being defined properly (thanks bmeng)
- Added ifdefs for all changed CSRs (e.g. for VM)
- Also properly disable VM on boot

Changes in v3:
- Renamed from "riscv: Add option to disable writes to mcounteren"
- Added original functionality back for older priv specs.

Changes in v2:
- Moved forward in the patch series

 arch/riscv/Kconfig           | 10 +++++++++
 arch/riscv/cpu/cpu.c         |  9 ++++++++
 arch/riscv/include/asm/csr.h | 40 ++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 3338b788f8..b7a5757584 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -225,6 +225,16 @@ config XIP
 config SHOW_REGS
 	bool "Show registers on unhandled exception"
 
+config RISCV_PRIV_1_9
+	bool "Use version 1.9 of the RISC-V priviledged specification"
+	help
+	  Older versions of the RISC-V priviledged specification had
+	  separate counter enable CSRs for each privilege mode. Writing
+	  to the unified mcounteren CSR on a processor implementing the
+	  old specification will result in an illegal instruction
+	  exception. In addition to counter CSR changes, the way virtual
+	  memory is configured was also changed.
+
 config STACK_SIZE_SHIFT
 	int
 	default 14
diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
index a971ec8694..263fccc675 100644
--- a/arch/riscv/cpu/cpu.c
+++ b/arch/riscv/cpu/cpu.c
@@ -89,11 +89,20 @@ int arch_cpu_init_dm(void)
 		 * Enable perf counters for cycle, time,
 		 * and instret counters only
 		 */
+#ifdef CONFIG_RISCV_PRIV_1_9
+		csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0));
+		csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0));
+#else
 		csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
+#endif
 
 		/* Disable paging */
 		if (supports_extension('s'))
+#ifdef CONFIG_RISCV_PRIV_1_9
+			csr_read_clear(CSR_MSTATUS, SR_VM);
+#else
 			csr_write(CSR_SATP, 0);
+#endif
 	}
 
 #ifdef CONFIG_SMP
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index d1520743a2..12b6146b97 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -15,7 +15,11 @@
 #define SR_SIE		_AC(0x00000002, UL) /* Supervisor Interrupt Enable */
 #define SR_SPIE		_AC(0x00000020, UL) /* Previous Supervisor IE */
 #define SR_SPP		_AC(0x00000100, UL) /* Previously Supervisor */
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_PUM		_AC(0x00040000, UL) /* Protect User Memory Access */
+#else
 #define SR_SUM		_AC(0x00040000, UL) /* Supervisor User Memory Access */
+#endif
 
 #define SR_FS		_AC(0x00006000, UL) /* Floating-point Status */
 #define SR_FS_OFF	_AC(0x00000000, UL)
@@ -29,6 +33,22 @@
 #define SR_XS_CLEAN	_AC(0x00010000, UL)
 #define SR_XS_DIRTY	_AC(0x00018000, UL)
 
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_VM		_AC(0x1F000000, UL) /* Virtualization Management */
+#define SR_VM_MODE_BARE	_AC(0x00000000, UL) /* No translation or protection */
+#define SR_VM_MODE_BB	_AC(0x01000000, UL) /* Single base-and-bound */
+#define SR_VM_MODE_BBID	_AC(0x02000000, UL) /* Separate instruction and
+					       data base-and-bound */
+#ifndef CONFIG_64BIT
+#define SR_VM_MODE_32	_AC(0x08000000, UL)
+#define SR_VM_MODE	SR_VM_MODE_32
+#else
+#define SR_VM_MODE_39	_AC(0x09000000, UL)
+#define SR_VM_MODE_48	_AC(0x0A000000, UL)
+#define SR_VM_MODE	SR_VM_MODE_39
+#endif
+#endif
+
 #ifndef CONFIG_64BIT
 #define SR_SD		_AC(0x80000000, UL) /* FS/XS dirty */
 #else
@@ -36,6 +56,7 @@
 #endif
 
 /* SATP flags */
+#ifndef CONFIG_RISCV_PRIV_1_9
 #ifndef CONFIG_64BIT
 #define SATP_PPN	_AC(0x003FFFFF, UL)
 #define SATP_MODE_32	_AC(0x80000000, UL)
@@ -45,6 +66,7 @@
 #define SATP_MODE_39	_AC(0x8000000000000000, UL)
 #define SATP_MODE	SATP_MODE_39
 #endif
+#endif
 
 /* SCAUSE */
 #define SCAUSE_IRQ_FLAG		(_AC(1, UL) << (__riscv_xlen - 1))
@@ -88,17 +110,35 @@
 #define CSR_SCAUSE		0x142
 #define CSR_STVAL		0x143
 #define CSR_SIP			0x144
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_SPTBR		0x180
+#else
 #define CSR_SATP		0x180
+#endif
 #define CSR_MSTATUS		0x300
 #define CSR_MISA		0x301
 #define CSR_MIE			0x304
 #define CSR_MTVEC		0x305
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MUCOUNTEREN         0x320
+#define CSR_MSCOUNTEREN         0x321
+#define CSR_MHCOUNTEREN         0x322
+#else
 #define CSR_MCOUNTEREN		0x306
+#endif
 #define CSR_MSCRATCH		0x340
 #define CSR_MEPC		0x341
 #define CSR_MCAUSE		0x342
 #define CSR_MTVAL		0x343
 #define CSR_MIP			0x344
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MBASE		0x380
+#define CSR_MBOUND		0x381
+#define CSR_MIBASE		0x382
+#define CSR_MIBOUND		0x383
+#define CSR_MDBASE		0x384
+#define CSR_MDBOUND		0x385
+#endif
 #define CSR_CYCLEH		0xc80
 #define CSR_TIMEH		0xc81
 #define CSR_INSTRETH		0xc82
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 29/33] riscv: Allow use of reset drivers
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (27 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9 Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 30/33] riscv: Try to get cpu frequency from a "clocks" node if it exists Sean Anderson
                   ` (3 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Currently, one cannot use a reset driver on RISC-V. Follow the MIPS
example, and disable the default reset handler when the sysreset driver is
enabled.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
---

Changes in v3:
- New

 arch/riscv/lib/reset.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/lib/reset.c b/arch/riscv/lib/reset.c
index ce3c1cf872..7622e5df43 100644
--- a/arch/riscv/lib/reset.c
+++ b/arch/riscv/lib/reset.c
@@ -7,6 +7,7 @@
 #include <command.h>
 #include <hang.h>
 
+#ifndef CONFIG_SYSRESET
 int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	printf("resetting ...\n");
@@ -16,3 +17,4 @@ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
 	return 0;
 }
+#endif
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 30/33] riscv: Try to get cpu frequency from a "clocks" node if it exists
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (28 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 29/33] riscv: Allow use of reset drivers Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 31/33] riscv: Enable cpu clock if it is present Sean Anderson
                   ` (2 subsequent siblings)
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Instead of always using the "clock-frequency" property to determine cpu
frequency, try using a clock in "clocks" if it exists. This patch also
fixes a bug where there could be spurious higher frequencies if sizeof(u32)
!= sizeof(ulong).

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
---
This patch was previously sumbitted on its own as
https://patchwork.ozlabs.org/patch/1232420/

This patch is the combination of the patches
https://patchwork.ozlabs.org/patch/1223933/
https://patchwork.ozlabs.org/patch/1224957/
"riscv: Fix incorrect cpu frequency on RV64"
"riscv: Try to get cpu frequency from device tree"

Changes in v5:
- Include linux/err.h explicitly
- Reword commit message

Changes in v4:
- New

 drivers/cpu/riscv_cpu.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/cpu/riscv_cpu.c b/drivers/cpu/riscv_cpu.c
index 28ad0aa30f..c6ed060abc 100644
--- a/drivers/cpu/riscv_cpu.c
+++ b/drivers/cpu/riscv_cpu.c
@@ -3,12 +3,14 @@
  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
  */
 
+#include <clk.h>
 #include <common.h>
 #include <cpu.h>
 #include <dm.h>
 #include <errno.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
+#include <linux/err.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -27,9 +29,24 @@ static int riscv_cpu_get_desc(struct udevice *dev, char *buf, int size)
 
 static int riscv_cpu_get_info(struct udevice *dev, struct cpu_info *info)
 {
+	int ret;
+	struct clk clk;
 	const char *mmu;
 
-	dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq);
+	/* Zero out the frequency, in case sizeof(ulong) != sizeof(u32) */
+	info->cpu_freq = 0;
+
+	/* First try getting the frequency from the assigned clock */
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (!ret) {
+		ret = clk_get_rate(&clk);
+		if (!IS_ERR_VALUE(ret))
+			info->cpu_freq = ret;
+		clk_free(&clk);
+	}
+
+	if (!info->cpu_freq)
+		dev_read_u32(dev, "clock-frequency", (u32 *)&info->cpu_freq);
 
 	mmu = dev_read_string(dev, "mmu-type");
 	if (!mmu)
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 31/33] riscv: Enable cpu clock if it is present
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (29 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 30/33] riscv: Try to get cpu frequency from a "clocks" node if it exists Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 32/33] riscv: Add device tree for K210 and Sipeed Maix BitM Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 33/33] riscv: Add Sipeed Maix support Sean Anderson
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The cpu clock is probably already enabled if we are executing code (though
we could be executing from a different core). This patch prevents the cpu
clock or its parents from being disabled.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
---
This patch was previously submitted on its own as
https://patchwork.ozlabs.org/patch/1232420/

Changes in v4:
- New

 drivers/cpu/riscv_cpu.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/cpu/riscv_cpu.c b/drivers/cpu/riscv_cpu.c
index c6ed060abc..9ce58695aa 100644
--- a/drivers/cpu/riscv_cpu.c
+++ b/drivers/cpu/riscv_cpu.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
  */
 
 #include <clk.h>
@@ -117,6 +118,24 @@ static int riscv_cpu_bind(struct udevice *dev)
 	return 0;
 }
 
+static int riscv_cpu_probe(struct udevice *dev)
+{
+	int ret = 0;
+	struct clk clk;
+
+	/* Get a clock if it exists */
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return 0;
+
+	ret = clk_enable(&clk);
+	clk_free(&clk);
+	if (ret == -ENOSYS || ret == -ENOTSUPP)
+		return 0;
+	else
+		return ret;
+}
+
 static const struct cpu_ops riscv_cpu_ops = {
 	.get_desc	= riscv_cpu_get_desc,
 	.get_info	= riscv_cpu_get_info,
@@ -133,6 +152,7 @@ U_BOOT_DRIVER(riscv_cpu) = {
 	.id = UCLASS_CPU,
 	.of_match = riscv_cpu_ids,
 	.bind = riscv_cpu_bind,
+	.probe = riscv_cpu_probe,
 	.ops = &riscv_cpu_ops,
 	.flags = DM_FLAG_PRE_RELOC,
 };
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 32/33] riscv: Add device tree for K210 and Sipeed Maix BitM
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (30 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 31/33] riscv: Enable cpu clock if it is present Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-02-28 21:05 ` [PATCH v5 33/33] riscv: Add Sipeed Maix support Sean Anderson
  32 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

Where possible, I have tried to find compatible drivers based on the layout
of registers. However, many devices remain untested. All untested devices
have been left disabled, but some tentative properties (such as compatible
strings, and clocks, interrupts, and resets properties) have been added.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Add more compatible strings
- Add cache line size
- Document CPUs as rocket cores
- Flesh out the gpio devices
- Add ports for audio and video devices
- Add fpioa pinctrl support
- Configure pins for MMC on SPI1
- Enable MMC
- Fix a couple uart properties (Thanks laanwj)
- Reorder ram now that relocation is handled with CONFIG_SYS defines
- Enable WDT
- Add pinctrl properties
- Add gpio support
- Add led support
- Add assorted AV bindings
- Add compatible strings for ram
- Use GPIO-based CS for MMC
- Limit SPI flash to 50 MHz

Changes in v4:
- Set regs sizes to full address range
- Remove clock-frequency property from cpus
- Add spi-max-frequency to spi devices from documentation
- Add more compatible strings for each device
- Add AI ram as a separate memory bank. Its clock is disabled on boot, and
  it cannot be accessed
- Reorder memory banks so u-boot relocates higher, leaving more room to
  load boot images
- Add designware ssi CTRL0 field shifts to spi devices
- Don't enable the MMC slot
- Update copyright
- Lint

Changes in v3:
- Move this patch to the end of the series
- Add a max frequency for spi3
- Remov unused compatible strings from spi-flash at 0
- Add s and u to isa string
- Fix mmu-type
- Remove cache-line size since it is unused (in u-boot) and undocumented
  (upstream)
- Add timer interrupts to clint0
- Round up various registers
- Add riscv,max-priority to plic
- Add apb* busses, since they have clocks which need to be enabled to
  access their devices
- Change uart compatible strings to "snps,dw-apb-uart", since that appears
  to match their registers
- Add compatible string for wdt*
- Add system reset device under sysctl
- Add reset device under sysctl

Changes in v2:
- Model changed to "Sipeed Maix Bit" to match file name
- Value of stdout-path fixed
- SD card slot compatible changed to "mmc-spi-slot"
- "jedec,spi-nor" added to spi flash compatible list
- Aliases for spi busses added
- timebase-frequency divided by 50 to match timer speed
- cpu-frequency renamed to clock-frequency
- CPUX_intc restyled to cpuX_intc
- "kendryte,k210-soc" added to soc compatible list for future-proofing
- PLIC handle renamed to plic0 from pic0
- K210_RST_SOC removed from sysrst, due to not being located in the reset
  register
- K210_RST_* numbers changed to match their bit offset within the reset
  register
- gpio_controller restyled to gpio-controller
- Added a second clock to the dma binding to match what the driver expects
- Changed "snps,designware-spi" compatible string to "snps,dw-apb-ssi" to
  match the correct driver
- Added a name to the spi clocks
- Added reg-io-width property to spi bindings
- Assigned a default parent to K210_CLK_SPI3
- Removed assigned clocks for ACLK and PLLs
- Removed u-boot,dm-pre-reloc bindings

 arch/riscv/dts/Makefile                 |   1 +
 arch/riscv/dts/k210-maix-bit.dts        | 333 ++++++++++++
 arch/riscv/dts/k210.dtsi                | 649 ++++++++++++++++++++++++
 include/dt-bindings/reset/k210-sysctl.h |  38 ++
 4 files changed, 1021 insertions(+)
 create mode 100644 arch/riscv/dts/k210-maix-bit.dts
 create mode 100644 arch/riscv/dts/k210.dtsi
 create mode 100644 include/dt-bindings/reset/k210-sysctl.h

diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile
index 4f30e6936f..3a6f96c67d 100644
--- a/arch/riscv/dts/Makefile
+++ b/arch/riscv/dts/Makefile
@@ -2,6 +2,7 @@
 
 dtb-$(CONFIG_TARGET_AX25_AE350) += ae350_32.dtb ae350_64.dtb
 dtb-$(CONFIG_TARGET_SIFIVE_FU540) += hifive-unleashed-a00.dtb
+dtb-$(CONFIG_TARGET_SIPEED_MAIX) += k210-maix-bit.dtb
 
 targets += $(dtb-y)
 
diff --git a/arch/riscv/dts/k210-maix-bit.dts b/arch/riscv/dts/k210-maix-bit.dts
new file mode 100644
index 0000000000..cb4dba5e13
--- /dev/null
+++ b/arch/riscv/dts/k210-maix-bit.dts
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	model = "Sipeed Maix Bit 2.0";
+	compatible = "sipeed,maix-bitm", "sipeed,maix-bit", "kendryte,k210";
+
+	chosen {
+		stdout-path = "serial0:115200";
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		green {
+			gpios = <&gpio1_0 4 GPIO_ACTIVE_LOW>;
+		};
+
+		red {
+			gpios = <&gpio1_0 5 GPIO_ACTIVE_LOW>;
+		};
+
+		blue {
+			gpios = <&gpio1_0 6 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	sound {
+		compatible = "simple-audio-card";
+		simple-audio-card,format = "i2s";
+		status = "disabled";
+
+		simple-audio-card,cpu {
+			sound-dai = <&i2s0 0>;
+		};
+
+		simple-audio-card,codec {
+			sound-dai = <&mic>;
+		};
+	};
+
+	mic: mic {
+		#sound-dai-cells = <0>;
+		compatible = "memsensing,msm61s4030h0";
+		status = "disabled";
+	};
+};
+
+&uarths0 {
+	pinctrl-0 = <&fpioa_uarths>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&gpio0 {
+	pinctrl-0 = <&fpioa_gpiohs>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&gpio1 {
+	pinctrl-0 = <&fpioa_gpio>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&i2s0 {
+	#sound-dai-cells = <1>;
+	pinctrl-0 = <&fpioa_i2s0>;
+	pinctrl-names = "default";
+};
+
+&fpioa {
+	status = "okay";
+
+	fpioa_uarths: uarths {
+		rx {
+			function = "UARTHS_RX";
+			pins = "IO_4";
+		};
+		tx {
+			function = "UARTHS_TX";
+			pins = "IO_5";
+		};
+	};
+
+	fpioa_gpiohs: gpiohs {
+		1 {
+			function = "GPIOHS1";
+			pins = "IO_6";
+		};
+		2 {
+			function = "GPIOHS2";
+			pins = "IO_7";
+		};
+		3 {
+			function = "GPIOHS3";
+			pins = "IO_17";
+		};
+		4 {
+			function = "GPIOHS4";
+			pins = "IO_21";
+		};
+		5 {
+			function = "GPIOHS5";
+			pins = "IO_22";
+		};
+		6 {
+			function = "GPIOHS6";
+			pins = "IO_23";
+		};
+		7 {
+			function = "GPIOHS7";
+			pins = "IO_24";
+		};
+		8 {
+			function = "GPIOHS8";
+			pins = "IO_25";
+		};
+		9 {
+			function = "GPIOHS9";
+			pins = "IO_30";
+		};
+		10 {
+			function = "GPIOHS10";
+			pins = "IO_31";
+		};
+		11 {
+			function = "GPIOHS11";
+			pins = "IO_32";
+		};
+		12 {
+			function = "GPIOHS12";
+			pins = "IO_33";
+		};
+		13 {
+			function = "GPIOHS13";
+			pins = "IO_34";
+		};
+		14 {
+			function = "GPIOHS14";
+			pins = "IO_35";
+		};
+	};
+
+	fpioa_gpio: gpio {
+		0 {
+			function = "GPIO0";
+			pins = "IO_8";
+		};
+		1 {
+			function = "GPIO1";
+			pins = "IO_9";
+		};
+		2 {
+			function = "GPIO2";
+			pins = "IO_10";
+		};
+		3 {
+			function = "GPIO3";
+			pins = "IO_11";
+		};
+		4 {
+			function = "GPIO4";
+			pins = "IO_12";
+		};
+		5 {
+			function = "GPIO5";
+			pins = "IO_13";
+		};
+		6 {
+			function = "GPIO6";
+			pins = "IO_14";
+		};
+		7 {
+			function = "GPIO7";
+			pins = "IO_15";
+		};
+	};
+
+	fpioa_i2s0: i2s0 {
+		bck {
+			function = "I2S0_SCLK";
+			pins = "IO_18";
+		};
+		ws {
+			function = "I2S0_WS";
+			pins = "IO_19";
+		};
+		dat {
+			function = "I2S0_IN_D0";
+			pins = "IO_20";
+		};
+	};
+
+	fpioa_dvp: dvp {
+		sda {
+			function = "SCCP_SDA";
+			pins = "IO_40";
+		};
+		sck {
+			function = "SCCP_SCLK";
+			pins = "IO_41";
+		};
+		rst {
+			function = "DVP_RST";
+			pins = "IO_42";
+		};
+		vsync {
+			function = "DVP_VSYNC";
+			pins = "IO_43";
+		};
+		pwdn {
+			function = "DVP_PWDN";
+			pins = "IO_44";
+		};
+		hsync {
+			function = "DVP_HSYNC";
+			pins = "IO_45";
+		};
+		xclk {
+			function = "DVP_XCLK";
+			pins = "IO_46";
+		};
+		pclk {
+			function = "DVP_PCLK";
+			pins = "IO_47";
+		};
+	};
+
+	fpioa_spi0: spi0 {
+		cs0 {
+			function = "GPIOHS28";
+			pins = "IO_36";
+		};
+		rst {
+			function = "GPIOHS29";
+			pins = "IO_37";
+		};
+		dc {
+			function = "GPIOHS30";
+			pins = "IO_38";
+		};
+		wr {
+			function = "SPI0_SCK";
+			pins = "IO_39";
+		};
+	};
+
+	fpioa_spi1: spi1 {
+		miso {
+			function = "SPI1_D1";
+			pins = "IO_26";
+		};
+		clk {
+			function = "SPI1_SCLK";
+			pins = "IO_27";
+		};
+		mosi {
+			function = "SPI1_D0";
+			pins = "IO_28";
+		};
+		cs0 {
+			function = "GPIOHS31";
+			pins = "IO_29";
+		};
+	};
+};
+
+&wdt0 {
+	status = "okay";
+};
+
+&dvp0 {
+	pinctrl-0 = <&fpioa_dvp>;
+	pinctrl-names = "default";
+};
+
+&spi0 {
+	pinctrl-0 = <&fpioa_spi0>;
+	pinctrl-names = "default";
+	num-cs = <1>;
+	cs-gpios = <&gpio0 28 0>;
+
+	panel at 0 {
+		compatible = "sitronix,st8898v";
+		reg = <0>;
+		reset-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
+		spi-max-frequency = <15000000>;
+		status = "disabled";
+	};
+};
+
+&spi1 {
+	pinctrl-0 = <&fpioa_spi1>;
+	pinctrl-names = "default";
+	num-cs = <1>;
+	cs-gpios = <&gpio0 31 0>;
+	status = "okay";
+
+	slot at 0 {
+		compatible = "mmc-spi-slot";
+		reg = <0>;
+		spi-max-frequency = <25000000>;
+		voltage-ranges = <3300 3300>;
+		broken-cd;
+		disable-wp;
+	};
+};
+
+&spi3 {
+	status = "okay";
+
+	spi-flash at 0 {
+		compatible = "winbond,w25q128fw", "jedec,spi-nor";
+		reg = <0>;
+		/*
+		 * Datasheet says it should work at 100 MHz with fast read, but
+		 * after testing it doesn't probe at that frequency
+		 */
+		spi-max-frequency = <50000000>;
+		m25p,fast-read;
+		broken-flash-reset;
+	};
+};
diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi
new file mode 100644
index 0000000000..125f835a80
--- /dev/null
+++ b/arch/riscv/dts/k210.dtsi
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dt-bindings/clock/k210-sysctl.h>
+#include <dt-bindings/mfd/k210-sysctl.h>
+#include <dt-bindings/reset/k210-sysctl.h>
+
+/ {
+	/*
+	 * Although the K210 is a 64-bit CPU, the address bus is only 32-bits
+	 * wide, and the upper half of all addresses is ignored.
+	 */
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "kendryte,k210";
+
+	aliases {
+		gpio0 = &gpio0;
+		gpio1 = &gpio1_0;
+		serial0 = &uarths0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		serial3 = &uart3;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		spi2 = &spi2;
+		spi3 = &spi3;
+	};
+
+	clocks {
+		in0: osc {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <26000000>;
+		};
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		timebase-frequency = <7800000>;
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "kendryte,k210", "sifive,rocket0", "riscv";
+			reg = <0>;
+			riscv,isa = "rv64imafdgc";
+			mmu-type = "sv39";
+			i-cache-block-size = <64>;
+			i-cache-size = <0x8000>;
+			d-cache-block-size = <64>;
+			d-cache-size = <0x8000>;
+			clocks = <&sysclk K210_CLK_CPU>;
+			cpu0_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "kendryte,k210", "sifive,rocket0", "riscv";
+			reg = <1>;
+			riscv,isa = "rv64imafdgc";
+			mmu-type = "sv39";
+			i-cache-block-size = <64>;
+			i-cache-size = <0x8000>;
+			d-cache-block-size = <64>;
+			d-cache-size = <0x8000>;
+			clocks = <&sysclk K210_CLK_CPU>;
+			cpu1_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+			};
+		};
+	};
+
+	sram0: memory at 80000000 {
+		device_type = "memory";
+		compatible = "kendryte,k210-sram";
+		reg = <0x80000000 0x400000>;
+		clocks = <&sysclk K210_CLK_SRAM0>;
+	};
+
+	sram1: memory at 80400000 {
+		device_type = "memory";
+		reg = <0x80400000 0x200000>;
+		compatible = "kendryte,k210-sram";
+		clocks = <&sysclk K210_CLK_SRAM1>;
+	};
+
+	airam: memory at 80600000 {
+		device_type = "memory";
+		reg = <0x80600000 0x200000>;
+		compatible = "kendryte,k210-airam";
+		clocks = <&sysclk K210_CLK_AI>;
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		ai_reserved: ai at 80600000 {
+			reg = <0x80600000 0x200000>;
+			reusable;
+		};
+	};
+
+	soc {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "kendryte,k210-soc", "simple-bus";
+		ranges;
+		interrupt-parent = <&plic0>;
+
+		debug0: debug at 0 {
+			compatible = "kendryte,k210-debug", "riscv,debug";
+			reg = <0x0 0x1000>;
+		};
+
+		rom0: nvmem at 1000 {
+			reg = <0x1000 0x1000>;
+			read-only;
+		};
+
+		clint0: interrupt-controller at 2000000 {
+			#interrupt-cells = <1>;
+			compatible = "kendryte,k210-clint", "riscv,clint0";
+			reg = <0x2000000 0x1000>;
+			interrupt-controller;
+			interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
+					      <&cpu1_intc 3>, <&cpu1_intc 7>;
+			clocks = <&sysclk K210_CLK_CPU>;
+		};
+
+		plic0: interrupt-controller at c000000 {
+			#interrupt-cells = <1>;
+			compatible = "kendryte,k210-plic", "riscv,plic0";
+			reg = <0xC000000 0x4000000>;
+			interrupt-controller;
+			interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>,
+					      <&cpu1_intc 9>, <&cpu1_intc 11>;
+			riscv,ndev = <65>;
+			riscv,max-priority = <7>;
+		};
+
+		uarths0: serial at 38000000 {
+			compatible = "kendryte,k210-uarths", "sifive,uart0";
+			reg = <0x38000000 0x1000>;
+			interrupts = <33>;
+			clocks = <&sysclk K210_CLK_CPU>;
+			status = "disabled";
+		};
+
+		gpio0: gpio-controller at 38001000 {
+			#interrupt-cells = <2>;
+			#gpio-cells = <2>;
+			compatible = "kendryte,k210-gpiohs", "sifive,gpio0";
+			reg = <0x38001000 0x1000>;
+			interrupt-controller;
+			interrupts = <34 35 36 37 38 39 40 41
+				      42 43 44 45 46 47 48 49
+				      50 51 52 53 54 55 56 57
+				      58 59 60 61 62 63 64 65>;
+			gpio-controller;
+			ngpios = <32>;
+			status = "disabled";
+		};
+
+		kpu0: ai at 40800000 {
+			compatible = "kendryte,k210-ai";
+			reg = <0x40800000 0xc00000>;
+			interrupts = <25>;
+			clocks = <&sysclk K210_CLK_AI>;
+			memory-region = <&ai_reserved>;
+			status = "disabled";
+		};
+
+		fft0: fft at 42000000 {
+			compatible = "kendryte,k210-fft";
+			reg = <0x42000000 0x400000>;
+			interrupts = <26>;
+			clocks = <&sysclk K210_CLK_FFT>;
+			resets = <&sysrst K210_RST_FFT>;
+			status = "disabled";
+		};
+
+		dmac0: dma-controller at 50000000 {
+			compatible = "kendryte,k210-dmac", "snps,axi-dma-1.01a";
+			reg = <0x50000000 0x1000>;
+			interrupts = <27 28 29 30 31 32>;
+			clocks = <&sysclk K210_CLK_DMA>, <&sysclk K210_CLK_DMA>;
+			clock-names = "core-clk", "cfgr-clk";
+			resets = <&sysrst K210_RST_DMA>;
+			dma-channels = <6>;
+			snps,dma-masters = <2>;
+			snps,data-width = <5>;
+			snps,block-size = <0x400000 0x400000 0x400000
+					   0x400000 0x400000 0x400000>;
+			snps,axi-max-burst-len = <256>;
+			status = "disabled";
+		};
+
+		apb0: bus at 50200000 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "kendryte,k210-apb", "simple-pm-bus";
+			ranges;
+			clocks = <&sysclk K210_CLK_APB0>;
+
+			gpio1: gpio-controller at 50200000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "kendryte,k210-gpio",
+					     "snps,dw-apb-gpio";
+				reg = <0x50200000 0x80>;
+				clocks = <&sysclk K210_CLK_GPIO>;
+				resets = <&sysrst K210_RST_GPIO>;
+				status = "disabled";
+
+				gpio1_0: gpio1 at 0 {
+					#gpio-cells = <2>;
+					#interrupt-cells = <2>;
+					compatible = "snps,dw-apb-gpio-port";
+					reg = <0>;
+					interrupt-controller;
+					interrupts = <23>;
+					gpio-controller;
+					snps,nr-gpios = <8>;
+
+					isp {
+						gpio-hog;
+						gpios = <0 0>;
+						input;
+						pinctrl-0 = <&fpioa_isp>;
+						pinctrl-names = "default";
+					};
+				};
+			};
+
+			uart1: serial at 50210000 {
+				compatible = "kendryte,k210-uart",
+					     "snps,dw-apb-uart";
+				reg = <0x50210000 0x100>;
+				interrupts = <11>;
+				clocks = <&sysclk K210_CLK_UART1>;
+				resets = <&sysrst K210_RST_UART1>;
+				reg-io-width = <4>;
+				reg-shift = <2>;
+				dcd-override;
+				dsr-override;
+				cts-override;
+				ri-override;
+				status = "disabled";
+			};
+
+			uart2: serial at 50220000 {
+				compatible = "kendryte,k210-uart",
+					     "snps,dw-apb-uart";
+				reg = <0x50220000 0x100>;
+				interrupts = <12>;
+				clocks = <&sysclk K210_CLK_UART2>;
+				resets = <&sysrst K210_RST_UART2>;
+				reg-io-width = <4>;
+				reg-shift = <2>;
+				dcd-override;
+				dsr-override;
+				cts-override;
+				ri-override;
+				status = "disabled";
+			};
+
+			uart3: serial at 50230000 {
+				compatible = "kendryte,k210-uart",
+					     "snps,dw-apb-uart";
+				reg = <0x50230000 0x100>;
+				interrupts = <13>;
+				clocks = <&sysclk K210_CLK_UART3>;
+				resets = <&sysrst K210_RST_UART3>;
+				reg-io-width = <4>;
+				reg-shift = <2>;
+				dcd-override;
+				dsr-override;
+				cts-override;
+				ri-override;
+				status = "disabled";
+			};
+
+			spi2: spi at 50240000 {
+				compatible = "kendryte,k120-spislave",
+					     "snps,dw-apb-ssi";
+				spi-slave;
+				reg = <0x50240000 0x100>;
+				interrupts = <2>;
+				clocks = <&sysclk K210_CLK_SPI2>;
+				resets = <&sysrst K210_RST_SPI2>;
+				spi-max-frequency = <25000000>;
+				/*
+				 * Likely not the only offset change, but this
+				 * is the only one which is documented.
+				 */
+				snps,tmod-offset = <8>;
+				status = "disabled";
+			};
+
+			i2s0: i2s at 50250000 {
+				compatible = "kendryte,k210-i2s",
+					     "snps,designware-i2s";
+				reg = <0x50250000 0x200>;
+				interrupts = <5>;
+				clocks = <&sysclk K210_CLK_I2S0>;
+				clock-names = "i2sclk";
+				resets = <&sysrst K210_RST_I2S0>;
+				status = "disabled";
+			};
+
+			apu0: sound at 520250200 {
+				compatible = "kendryte,k210-apu";
+				reg = <0x50250200 0x200>;
+				status = "disabled";
+			};
+
+			i2s1: i2s at 50260000 {
+				compatible = "kendryte,k210-i2s",
+					     "snps,designware-i2s";
+				reg = <0x50260000 0x200>;
+				interrupts = <6>;
+				clocks = <&sysclk K210_CLK_I2S1>;
+				clock-names = "i2sclk";
+				resets = <&sysrst K210_RST_I2S1>;
+				status = "disabled";
+			};
+
+			i2s2: i2s at 50270000 {
+				compatible = "kendryte,k210-i2s",
+					     "snps,designware-i2s";
+				reg = <0x50270000 0x200>;
+				interrupts = <7>;
+				clocks = <&sysclk K210_CLK_I2S2>;
+				clock-names = "i2sclk";
+				resets = <&sysrst K210_RST_I2S2>;
+				status = "disabled";
+			};
+
+			i2c0: i2c at 50280000 {
+				compatible = "kendryte,k210-i2c",
+					     "snps,designware-i2c";
+				reg = <0x50280000 0x100>;
+				interrupts = <8>;
+				clocks = <&sysclk K210_CLK_I2C0>;
+				resets = <&sysrst K210_RST_I2C0>;
+				status = "disabled";
+			};
+
+			i2c1: i2c at 50290000 {
+				compatible = "kendryte,k210-i2c",
+					     "snps,designware-i2c";
+				reg = <0x50290000 0x100>;
+				interrupts = <9>;
+				clocks = <&sysclk K210_CLK_I2C1>;
+				resets = <&sysrst K210_RST_I2C1>;
+				status = "disabled";
+			};
+
+			i2c2: i2c at 502A0000 {
+				compatible = "kendryte,k210-i2c",
+					     "snps,designware-i2c";
+				reg = <0x502A0000 0x100>;
+				interrupts = <10>;
+				clocks = <&sysclk K210_CLK_I2C2>;
+				resets = <&sysrst K210_RST_I2C2>;
+				status = "disabled";
+			};
+
+			fpioa: pinmux at 502B0000 {
+				compatible = "kendryte,k210-fpioa";
+				reg = <0x502B0000 0x100>;
+				clocks = <&sysclk K210_CLK_FPIOA>;
+				resets = <&sysrst K210_RST_FPIOA>;
+				kendryte,sysctl = <&sysctl>;
+				kendryte,power-offset = <K210_SYSCTL_POWER_SEL>;
+				pinctrl-0 = <&fpioa_jtag>;
+				pinctrl-names = "default";
+				status = "disabled";
+
+				fpioa_jtag: jtag {
+					tck {
+						function = "JTAG_TCLK";
+						pins = "IO_0";
+					};
+					tdi {
+						function = "JTAG_TDI";
+						pins = "IO_1";
+					};
+					tms {
+						function = "JTAG_TMS";
+						pins = "IO_2";
+					};
+					tdo {
+						function = "JTAG_TDO";
+						pins = "IO_3";
+					};
+				};
+
+				fpioa_isp: isp {
+					isp {
+						function = "GPIOHS0";
+						pins = "IO_16";
+					};
+				};
+			};
+
+			sha256: sha256 at 502C0000 {
+				compatible = "kendryte,k210-sha256";
+				reg = <0x502C0000 0x100>;
+				clocks = <&sysclk K210_CLK_SHA>;
+				resets = <&sysrst K210_RST_SHA>;
+				status = "disabled";
+			};
+
+			timer0: timer at 502D0000 {
+				compatible = "kendryte,k210-timer",
+					     "snps,dw-apb-timer";
+				reg = <0x502D0000 0x100>;
+				interrupts = <14 15>;
+				clocks = <&sysclk K210_CLK_TIMER0>;
+				clock-names = "timer";
+				resets = <&sysrst K210_RST_TIMER0>;
+				status = "disabled";
+			};
+
+			timer1: timer at 502E0000 {
+				compatible = "kendryte,k210-timer",
+					     "snps,dw-apb-timer";
+				reg = <0x502E0000 0x100>;
+				interrupts = <16 17>;
+				clocks = <&sysclk K210_CLK_TIMER1>;
+				clock-names = "timer";
+				resets = <&sysrst K210_RST_TIMER1>;
+				status = "disabled";
+			};
+
+			timer2: timer at 502F0000 {
+				compatible = "kendryte,k210-timer",
+					     "snps,dw-apb-timer";
+				reg = <0x502F0000 0x100>;
+				interrupts = <18 19>;
+				clocks = <&sysclk K210_CLK_TIMER2>;
+				clock-names = "timer";
+				resets = <&sysrst K210_RST_TIMER2>;
+				status = "disabled";
+			};
+		};
+
+		apb1: bus at 50400000 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "kendryte,k210-apb", "simple-pm-bus";
+			ranges;
+			clocks = <&sysclk K210_CLK_APB1>;
+
+			wdt0: watchdog at 50400000 {
+				compatible = "kendryte,k210-wdt", "snps,dw-wdt";
+				reg = <0x50400000 0x100>;
+				interrupts = <21>;
+				clocks = <&sysclk K210_CLK_WDT0>;
+				resets = <&sysrst K210_RST_WDT0>;
+				status = "disabled";
+			};
+
+			wdt1: watchdog at 50410000 {
+				compatible = "kendryte,k210-wdt", "snps,dw-wdt";
+				reg = <0x50410000 0x100>;
+				interrupts = <22>;
+				clocks = <&sysclk K210_CLK_WDT1>;
+				resets = <&sysrst K210_RST_WDT1>;
+				status = "disabled";
+			};
+
+			otp0: nvmem at 50420000 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "kendryte,k210-otp";
+				reg = <0x50420000 0x100>,
+				      <0x88000000 0x20000>;
+				reg-names = "reg", "mem";
+				clocks = <&sysclk K210_CLK_ROM>;
+				resets = <&sysrst K210_RST_ROM>;
+				read-only;
+				status = "disabled";
+
+				/* Bootloader */
+				firmware at 00000 {
+					reg = <0x00000 0xc200>;
+				};
+
+				/*
+				 * config string as described in RISC-V
+				 * privileged spec 1.9
+				 */
+				config-1-9 at 1c000 {
+					reg = <0x1c000 0x1000>;
+				};
+
+				/*
+				 * Device tree containing only registers,
+				 * interrupts, and cpus
+				 */
+				fdt at 1d000 {
+					reg = <0x1d000 0x2000>;
+				};
+
+				/* CPU/ROM credits */
+				credits at 1f000 {
+					reg = <0x1f000 0x1000>;
+				};
+			};
+
+			dvp0: camera at 50430000 {
+				compatible = "kendryte,k210-dvp";
+				reg = <0x50430000 0x100>;
+				interrupts = <24>;
+				clocks = <&sysclk K210_CLK_DVP>;
+				resets = <&sysrst K210_RST_DVP>;
+				status = "disabled";
+			};
+
+			sysctl: syscon at 50440000 {
+				compatible = "kendryte,k210-sysctl",
+					     "syscon", "simple-mfd";
+				reg = <0x50440000 0x100>;
+				reg-io-width = <4>;
+
+				sysclk: clock-controller {
+					compatible = "kendryte,k210-clk";
+					clocks = <&in0>;
+					#clock-cells = <1>;
+				};
+
+				sysrst: reset-controller {
+					compatible = "kendryte,k210-rst",
+						     "syscon-reset";
+					#reset-cells = <1>;
+					regmap = <&sysctl>;
+					offset = <K210_SYSCTL_PERI_RESET>;
+					mask = <0x27FFFFFF>;
+					assert-high = <1>;
+				};
+
+				reboot {
+					compatible = "syscon-reboot";
+					regmap = <&sysctl>;
+					offset = <K210_SYSCTL_SOFT_RESET>;
+					mask = <1>;
+					value = <1>;
+				};
+			};
+
+			aes0: aes at 50450000 {
+				compatible = "kendryte,k210-aes";
+				reg = <0x50450000 0x100>;
+				clocks = <&sysclk K210_CLK_AES>;
+				resets = <&sysrst K210_RST_AES>;
+				status = "disabled";
+			};
+
+			rtc: rtc at 50460000 {
+				compatible = "kendryte,k210-rtc";
+				reg = <0x50460000 0x100>;
+				clocks = <&in0>;
+				resets = <&sysrst K210_RST_RTC>;
+				interrupts = <20>;
+				status = "disabled";
+			};
+		};
+
+		apb2: bus at 52000000 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "kendryte,k210-apb", "simple-pm-bus";
+			ranges;
+			clocks = <&sysclk K210_CLK_APB2>;
+
+			spi0: spi at 52000000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "kendryte,k210-spi",
+					     "snps,dw-apb-ssi";
+				reg = <0x52000000 0x100>;
+				interrupts = <1>;
+				clocks = <&sysclk K210_CLK_SPI0>;
+				clock-names = "ssi_clk";
+				resets = <&sysrst K210_RST_SPI0>;
+				spi-max-frequency = <25000000>;
+				num-cs = <4>;
+				reg-io-width = <4>;
+				snps,dfs-offset = <16>;
+				snps,frf-offset = <21>;
+				snps,tmod-offset = <8>;
+				snps,mode-offset = <6>;
+				status = "disabled";
+			};
+
+			spi1: spi at 53000000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "kendryte,k210-spi",
+					     "snps,dw-apb-ssi";
+				reg = <0x53000000 0x100>;
+				interrupts = <2>;
+				clocks = <&sysclk K210_CLK_SPI1>;
+				clock-names = "ssi_clk";
+				resets = <&sysrst K210_RST_SPI1>;
+				spi-max-frequency = <25000000>;
+				num-cs = <4>;
+				reg-io-width = <4>;
+				snps,dfs-offset = <16>;
+				snps,frf-offset = <21>;
+				snps,tmod-offset = <8>;
+				snps,mode-offset = <6>;
+				status = "disabled";
+			};
+
+			spi3: spi at 54000000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "kendryte,k210-spi",
+					     "snps,dw-apb-ssi";
+				reg = <0x54000000 0x200>;
+				interrupts = <4>;
+				clocks = <&sysclk K210_CLK_SPI3>;
+				clock-names = "ssi_clk";
+				resets = <&sysrst K210_RST_SPI3>;
+				/* Could possibly go up to 200 MHz */
+				spi-max-frequency = <100000000>;
+				num-cs = <4>;
+				reg-io-width = <4>;
+				snps,dfs-offset = <0>;
+				snps,frf-offset = <22>;
+				snps,tmod-offset = <10>;
+				snps,mode-offset = <8>;
+				status = "disabled";
+			};
+		};
+	};
+};
diff --git a/include/dt-bindings/reset/k210-sysctl.h b/include/dt-bindings/reset/k210-sysctl.h
new file mode 100644
index 0000000000..12bb3880d9
--- /dev/null
+++ b/include/dt-bindings/reset/k210-sysctl.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef RESET_K210_SYSCTL_H
+#define RESET_K210_SYSCTL_H
+
+#define K210_RST_ROM    0
+#define K210_RST_DMA    1
+#define K210_RST_AI     2
+#define K210_RST_DVP    3
+#define K210_RST_FFT    4
+#define K210_RST_GPIO   5
+#define K210_RST_SPI0   6
+#define K210_RST_SPI1   7
+#define K210_RST_SPI2   8
+#define K210_RST_SPI3   9
+#define K210_RST_I2S0   10
+#define K210_RST_I2S1   11
+#define K210_RST_I2S2   12
+#define K210_RST_I2C0   13
+#define K210_RST_I2C1   14
+#define K210_RST_I2C2   15
+#define K210_RST_UART1  16
+#define K210_RST_UART2  17
+#define K210_RST_UART3  18
+#define K210_RST_AES    19
+#define K210_RST_FPIOA  20
+#define K210_RST_TIMER0 21
+#define K210_RST_TIMER1 22
+#define K210_RST_TIMER2 23
+#define K210_RST_WDT0   24
+#define K210_RST_WDT1   25
+#define K210_RST_SHA    26
+#define K210_RST_RTC    29
+
+#endif /* RESET_K210_SYSCTL_H */
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 33/33] riscv: Add Sipeed Maix support
  2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
                   ` (31 preceding siblings ...)
  2020-02-28 21:05 ` [PATCH v5 32/33] riscv: Add device tree for K210 and Sipeed Maix BitM Sean Anderson
@ 2020-02-28 21:05 ` Sean Anderson
  2020-03-04  6:04   ` Rick Chen
  32 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 21:05 UTC (permalink / raw)
  To: u-boot

The Sipeed Maix series is a collection of boards built around the RISC-V
Kendryte K210 processor. This processor contains several peripherals to
accelerate neural network processing and other "ai" tasks. This includes a
"KPU" neural network processor, an audio processor supporting beamforming
reception, and a digital video port supporting capture and output at VGA
resolution. Other peripherals include 8M of sram (accessible with and
without caching); remappable pins, including 40 GPIOs; AES, FFT, and SHA256
accelerators; a DMA controller; and I2C, I2S, and SPI controllers. Maix
peripherals vary, but include spi flash; on-board usb-serial bridges; ports
for cameras, displays, and sd cards; and ESP32 chips. Currently, only the
Sipeed Maix Bit V2.0 (bitm) is supported, but the boards are fairly
similar.

Documentation for Maix boards is located at
<http://dl.sipeed.com/MAIX/HDK/>.  Documentation for the Kendryte K210 is
located at <https://kendryte.com/downloads/>. However, hardware details are
rather lacking, so most technical reference has been taken from the
standalone sdk located at
<https://github.com/kendryte/kendryte-standalone-sdk>.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

Changes in v5:
- Configure relocation location with CONFIG_SYS_SDRAM_*
- Enable ram clocks
- Add pinmux/gpio/led support
- Remove (broken) MMC support
- Store the environment in flash
- Add partitions
- Add bootcmd
- Add docs for pinctrl and booting

Changes in v4:
- Rework documentation to be organized by board mfg not cpu mfg
- Update docs to reflect working SPI support
- Add proper spi support
- Don't define unneecessary macros in config.h
- Lower the default stack so it isn't clobbered on relocation
- Update MAINTAINERS
- Update copyright

Changes in v3:
- Reorder to be last in the patch series
- Add documentation for the board
- Generate defconfig with "make savedefconfig"
- Update Kconfig to imply most features we need
- Update MAINTAINERS

Changes in v2:
- Select CONFIG_SYS_RISCV_NOCOUNTER
- Imply CONFIG_CLK_K210
- Remove spurious references to CONFIG_ARCH_K210
- Remove many configs from defconfig where the defaults were fine
- Add a few "not set" lines to suppress unneeded defaults
- Reduce pre-reloc malloc space, now that clocks initialization happens
  later

 arch/riscv/Kconfig                 |   4 +
 board/sipeed/maix/Kconfig          |  72 ++++++++++
 board/sipeed/maix/MAINTAINERS      |  11 ++
 board/sipeed/maix/Makefile         |   5 +
 board/sipeed/maix/maix.c           |  54 +++++++
 configs/sipeed_maix_bitm_defconfig |  16 +++
 doc/board/index.rst                |   1 +
 doc/board/sipeed/index.rst         |   9 ++
 doc/board/sipeed/maix.rst          | 223 +++++++++++++++++++++++++++++
 include/configs/sipeed-maix.h      |  24 ++++
 10 files changed, 419 insertions(+)
 create mode 100644 board/sipeed/maix/Kconfig
 create mode 100644 board/sipeed/maix/MAINTAINERS
 create mode 100644 board/sipeed/maix/Makefile
 create mode 100644 board/sipeed/maix/maix.c
 create mode 100644 configs/sipeed_maix_bitm_defconfig
 create mode 100644 doc/board/sipeed/index.rst
 create mode 100644 doc/board/sipeed/maix.rst
 create mode 100644 include/configs/sipeed-maix.h

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index b7a5757584..d016dd75d7 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -20,6 +20,9 @@ config TARGET_QEMU_VIRT
 config TARGET_SIFIVE_FU540
 	bool "Support SiFive FU540 Board"
 
+config TARGET_SIPEED_MAIX
+	bool "Support Sipeed Maix Board"
+
 endchoice
 
 config SYS_ICACHE_OFF
@@ -53,6 +56,7 @@ source "board/AndesTech/ax25-ae350/Kconfig"
 source "board/emulation/qemu-riscv/Kconfig"
 source "board/microchip/mpfs_icicle/Kconfig"
 source "board/sifive/fu540/Kconfig"
+source "board/sipeed/maix/Kconfig"
 
 # platform-specific options below
 source "arch/riscv/cpu/ax25/Kconfig"
diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig
new file mode 100644
index 0000000000..939eb4829a
--- /dev/null
+++ b/board/sipeed/maix/Kconfig
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+
+if TARGET_SIPEED_MAIX
+
+config SYS_BOARD
+	default "maix"
+
+config SYS_VENDOR
+	default "sipeed"
+
+config SYS_CPU
+	default "generic"
+
+config SYS_CONFIG_NAME
+	default "sipeed-maix"
+
+config SYS_TEXT_BASE
+	default 0x80000000
+
+config DEFAULT_DEVICE_TREE
+	default "k210-maix-bit"
+
+config NR_CPUS
+	default 2
+
+config NR_DRAM_BANKS
+	default 3
+
+config SF_DEFAULT_BUS
+	default 3
+
+config BOARD_SPECIFIC_OPTIONS
+	def_bool y
+	select GENERIC_RISCV
+	select RISCV_PRIV_1_9
+	imply SMP
+	imply OF_BOARD_SETUP
+	imply DM_SERIAL
+	imply SIFIVE_SERIAL
+	imply SIFIVE_CLINT
+	imply POWER_DOMAIN
+	imply SIMPLE_PM_BUS
+	imply CLK_CCF
+	imply CLK_COMPOSITE_CCF
+	imply CLK_K210
+	imply DM_RESET
+	imply RESET_SYSCON
+	imply SYSRESET
+	imply SYSRESET_SYSCON
+	imply PINCTRL
+	imply PINCONF
+	imply PINCTRL_K210
+	imply DM_GPIO
+	imply DWAPB_GPIO
+	imply SIFIVE_GPIO
+	imply CMD_GPIO
+	imply LED
+	imply LED_GPIO
+	imply SPI
+	imply DESIGNWARE_SPI
+	imply SPI_FLASH_WINBOND
+	imply DM_MTD
+	imply SPI_FLASH_MTD
+	imply CMD_MTD
+	imply ENV_IS_IN_SPI_FLASH
+	imply MMC
+	imply MMC_BROKEN_CD
+	imply MMC_SPI
+	imply WDT
+	imply DESIGNWARE_WATCHDOG
+endif
diff --git a/board/sipeed/maix/MAINTAINERS b/board/sipeed/maix/MAINTAINERS
new file mode 100644
index 0000000000..1f33882e1e
--- /dev/null
+++ b/board/sipeed/maix/MAINTAINERS
@@ -0,0 +1,11 @@
+Sipeed Maix BOARD
+M:	Sean Anderson <seanga2@gmail.com>
+S:	Maintained
+F:	arch/riscv/dts/k210.dtsi
+F:	arch/riscv/dts/k210-maix-bit.dts
+F:	board/sipeed/maix/
+F:	configs/sipeed_maix_defconfig
+F:	doc/board/sipeed/
+F:	include/configs/sipeed-maix.h
+F:	include/dt-bindings/*/k210-sysctl.h
+F:	test/dm/k210_pll.c
diff --git a/board/sipeed/maix/Makefile b/board/sipeed/maix/Makefile
new file mode 100644
index 0000000000..4acff5b31e
--- /dev/null
+++ b/board/sipeed/maix/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2019 Western Digital Corporation or its affiliates.
+
+obj-y += maix.o
diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c
new file mode 100644
index 0000000000..c126cb5d67
--- /dev/null
+++ b/board/sipeed/maix/maix.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdt_support.h>
+
+phys_size_t get_effective_memsize(void)
+{
+	return CONFIG_SYS_SDRAM_SIZE;
+}
+
+int board_init(void)
+{
+	int ret;
+	ofnode bank = ofnode_null();
+
+	/* Enable RAM clocks */
+	while (true) {
+		struct clk clk;
+
+		bank = ofnode_by_prop_value(bank, "device_type", "memory",
+					    sizeof("memory"));
+		if (ofnode_equal(bank, ofnode_null()))
+			break;
+
+		ret = clk_get_by_index_nodev(bank, 0, &clk);
+		if (ret)
+			continue;
+
+		ret = clk_enable(&clk);
+		clk_free(&clk);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+	int i;
+	u64 base[CONFIG_NR_DRAM_BANKS];
+	u64 size[CONFIG_NR_DRAM_BANKS];
+
+	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+		base[i] = bd->bi_dram[i].start;
+		size[i] = bd->bi_dram[i].size;
+	}
+
+	return fdt_fixup_memory_banks(blob, base, size, CONFIG_NR_DRAM_BANKS);
+}
diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig
new file mode 100644
index 0000000000..7f644e7a37
--- /dev/null
+++ b/configs/sipeed_maix_bitm_defconfig
@@ -0,0 +1,16 @@
+CONFIG_RISCV=y
+CONFIG_ENV_SIZE=0x2000
+CONFIG_ENV_SECT_SIZE=0x1000
+CONFIG_ENV_OFFSET=0x7C000
+CONFIG_ENV_OFFSET_REDUND=0x7E000
+CONFIG_TARGET_SIPEED_MAIX=y
+CONFIG_ARCH_RV64I=y
+CONFIG_USE_BOOTCOMMAND=y
+CONFIG_BOOTCOMMAND="sf probe;mtd read kernel 80000000;go 80000000"
+CONFIG_MTDIDS_DEFAULT="nor0=spi3.0"
+CONFIG_MTDPARTS_DEFAULT="spi3.0:496k(u-boot),16k(env),5632k(kernel),10240k(data)"
+CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
+# CONFIG_NET is not set
+# CONFIG_INPUT is not set
+# CONFIG_DM_ETH is not set
+# CONFIG_EFI_LOADER is not set
diff --git a/doc/board/index.rst b/doc/board/index.rst
index b8b956d730..54cbc5c874 100644
--- a/doc/board/index.rst
+++ b/doc/board/index.rst
@@ -16,4 +16,5 @@ Board-specific doc
    renesas/index
    rockchip/index
    sifive/index
+   sipeed/index
    xilinx/index
diff --git a/doc/board/sipeed/index.rst b/doc/board/sipeed/index.rst
new file mode 100644
index 0000000000..92da47fddd
--- /dev/null
+++ b/doc/board/sipeed/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Kendryte
+========
+
+.. toctree::
+   :maxdepth: 2
+
+   maix
diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst
new file mode 100644
index 0000000000..c5c2e24ea5
--- /dev/null
+++ b/doc/board/sipeed/maix.rst
@@ -0,0 +1,223 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+
+Maix Bit
+========
+
+Several of the Sipeed Maix series of boards cotain the Kendryte K210 processor,
+a 64-bit RISC-V CPU. This processor contains several peripherals to accelerate
+neural network processing and other "ai" tasks. This includes a "KPU" neural
+network processor, an audio processor supporting beamforming reception, and a
+digital video port supporting capture and output at VGA resolution. Other
+peripherals include 8M of SRAM (accessible with and without caching); remappable
+pins, including 40 GPIOs; AES, FFT, and SHA256 accelerators; a DMA controller;
+and I2C, I2S, and SPI controllers. Maix peripherals vary, but include spi flash;
+on-board usb-serial bridges; ports for cameras, displays, and sd cards; and
+ESP32 chips. Currently, only the Sipeed Maix Bit V2.0 (bitm) is supported, but
+the boards are fairly similar.
+
+Documentation for Maix boards is available from
+`Sipeed's website <http://dl.sipeed.com/MAIX/HDK/>`_.
+Documentation for the Kendryte K210 is available from
+`Kendryte's website <https://kendryte.com/downloads/>`_. However, hardware
+details are rather lacking, so most technical reference has been taken from the
+`standalone sdk <https://github.com/kendryte/kendryte-standalone-sdk>`_.
+
+Build and boot steps
+--------------------
+
+To build u-boot, run
+
+.. code-block:: none
+
+    make sipeed_maix_bitm_defconfig
+    make CROSS_COMPILE=<your cross compile prefix>
+
+To flash u-boot to a maix bit, run
+
+.. code-block:: none
+
+    kflash -tp /dev/<your tty here> -B bit_mic u-boot-dtb.bin
+
+Boot output should look like the following:
+
+.. code-block:: none
+
+    
+    U-Boot 2020.04-rc2-00087-g2221cc09c1-dirty (Feb 28 2020 - 13:53:09 -0500)
+    
+    DRAM:  8 MiB
+    WDT:   Started with servicing (60s timeout)
+    MMC:   spi at 53000000:slot at 0: 0
+    In:    serial at 38000000
+    Out:   serial at 38000000
+    Err:   serial at 38000000
+    Hit any key to stop autoboot:  0
+    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
+    Reading 5242880 byte(s) at offset 0x00000000
+    ## Starting application at 0x80000000 ...
+
+Flashing Images
+---------------
+
+To flash a kernel, transfer it over serial, then write it to the kernel
+partition.
+
+.. code-block:: none
+
+    => loady 80000000 1500000
+    ## Switch baudrate to 1500000 bps and press ENTER ...
+    
+    *** baud: 1500000
+    
+    *** baud: 1500000 ***
+    ## Ready for binary (ymodem) download to 0x80000000 at 1500000 bps...
+    C
+    *** file: loader.bin
+    $ sz -vv loader.bin
+    Sending: loader.bin
+    Bytes Sent:2478208   BPS:72937                           
+    Sending: 
+    Ymodem sectors/kbytes sent:   0/ 0k
+    Transfer complete
+    
+    *** exit status: 0 ***
+    ## Total Size      = 0x0025d052 = 2478162 Bytes
+    ## Switch baudrate to 115200 bps and press ESC ...
+    
+    *** baud: 115200
+    
+    *** baud: 115200 ***
+    => sf probe
+    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
+    => mtd write kernel 80000000 0 25d052
+    Writing 2478162 byte(s) at offset 0x00000000
+
+**NB:** kflash adds a 5-byte header to payloads (and a 32-byte trailer) to all
+payloads it flashes. If you use kflash to flash your payload, you will need to
+account for this header when specifying what offset in spi flash to load from.
+
+Partition Scheme
+^^^^^^^^^^^^^^^^
+
+There is no partition scheme specified by the manufacturer. The only requirement
+imposed by the firmware is that offset 0 will be loaded and ran. The default
+partition scheme is
+
+========= ======== ======
+Partition Offset   Size
+========= ======== ======
+u-boot    0x000000 496k
+env       0x07C000 16k
+kernel    0x080000 5M
+data      0x580000 10.5M
+========= ======== ======
+
+Pin Assignment
+--------------
+
+The K210 contains a Fully Programmable I/O Array (FPIOA), which can remap any of
+its 256 input functions to any any of 48 output pins. The following table has
+the default pin assignments for the BitM.
+
+===== ========== =======
+Pin   Function   Comment
+===== ========== =======
+IO_0  JTAG_TCLK
+IO_1  JTAG_TDI
+IO_2  JTAG_TMS
+IO_3  JTAG_TDO
+IO_4  UARTHS_RX
+IO_5  UARTHS_TX
+IO_6  GPIOHS_1
+IO_7  GPIOHS_2
+IO_8  GPIO_0
+IO_9  GPIO_1
+IO_10 GPIO_2
+IO_11 GPIO_3
+IO_12 GPIO_4     Green LED
+IO_13 GPIO_5     Red LED
+IO_14 GPIO_6     Blue LED
+IO_15 GPIO_7
+IO_16 GPIOHS_0   ISP
+IO_17 GPIOHS_3
+IO_18 I2S0_SCLK  MIC CLK
+IO_19 I2S0_WS    MIC WS
+IO_20 I2S0_IN_D0 MIC SD
+IO_21 GPIOHS_4
+IO_22 GPIOHS_5
+IO_23 GPIOHS_6
+IO_24 GPIOHS_7
+IO_25 GPIOHS_8
+IO_26 SPI1_D1    MMC MISO
+IO_27 SPI1_SCLK  MMC CLK
+IO_28 SPI1_D0    MMC MOSI
+IO_29 GPIOHS_31  MMC CS
+IO_30 GPIOHS_9
+IO_31 GPIOHS_10
+IO_32 GPIOHS_11
+IO_33 GPIOHS_12
+IO_34 GPIOHS_13
+IO_35 GPIOHS_14
+IO_36 GPIOHS_28  Panel CS
+IO_37 GPIOHS_29  Panel RST
+IO_38 GPIOHS_30  Panel DC
+IO_39 SPI0_SCK   Panel WR
+IO_40 SCCP_SDA
+IO_41 SCCP_SCLK
+IO_42 DVP_RST
+IO_43 DVP_VSYNC
+IO_44 DVP_PWDN
+IO_45 DVP_HSYNC
+IO_46 DVP_XCLK
+IO_47 DVP_PCLK
+===== ========== =======
+
+Over- and Under-clocking
+------------------------
+
+To change the clock speed of the K210, you will need to enable
+``CONFIG_CLK_K210_SET_RATE`` and edit the board's device tree. To do this, add a
+section to ``arch/riscv/arch/riscv/dts/k210-maix-bit.dts`` like the following:
+
+.. code-block:: dts
+
+    &sysclk {
+ 	assigned-clocks = <&sysclk K210_CLK_PLL0>;
+ 	assigned-clock-rates = <800000000>;
+    };
+
+There are three PLLs on the K210: PLL0 is the parent of most of the components,
+including the CPU and RAM. PLL1 is the parent of the neural network coprocessor.
+PLL2 is the parent of the sound processing devices. Note that child clocks of
+PLL0 and PLL2 run at *half* the speed of the PLLs. For example, if PLL0 is
+running at 800 MHz, then the CPU will run at 400 MHz. This is the example given
+above. The CPU can be overclocked to around 600 MHz, and underclocked to 26 MHz.
+
+It is possible to set PLL2's parent to PLL0. The plls are more accurate when
+converting between similar frequencies. This makes it easier to get an accurate
+frequency for I2S. As an example, consider sampling an I2S device at 44.1 kHz.
+On this device, the I2S serial clock runs at 64 times the sample rate.
+Therefore, we would like to run PLL2 at an even multiple of 2.8224 MHz. If
+PLL2's parent is IN0, we could use a frequency of 390 MHz (the same as the CPU's
+default speed).  Dividing by 138 yields a serial clock of about 2.8261 MHz. This
+results in a sample rate of 44.158 kHz---around 50 Hz or .1% too fast. If,
+instead, we set PLL2's parent to PLL1 running at 390 MHz, and request a rate of
+2.8224 * 136 = 383.8464 MHz, the achieved rate is 383.90625 MHz. Dividing by 136
+yields a serial clock of about 2.8228 MHz. This results in a sample rate of
+44.107 kHz---just 7 Hz or .02% too fast. This configuration is shown in the
+following example:
+
+.. code-block:: dts
+
+    &sysclk {
+ 	assigned-clocks = <&sysclk K210_CLK_PLL1>, <&sysclk K210_CLK_PLL2>;
+	assigned-clock-parents = <0>, <&sysclk K210_CLK_PLL1>;
+ 	assigned-clock-rates = <390000000>, <383846400>;
+    };
+
+There are a couple of quirks to the PLLs. First, there are more frequency ratios
+just above and below 1.0, but there is a small gap around 1.0. To be explicit,
+if the input frequency is 100 MHz, it would be impossible to have an output of
+99 or 101 MHz. In addition, there is a maximum frequency for the internal VCO,
+so higher input/output frequencies will be less accurate than lower ones.
diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h
new file mode 100644
index 0000000000..a46473fc78
--- /dev/null
+++ b/include/configs/sipeed-maix.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ */
+
+#ifndef CONFIGS_SIPEED_MAIX_H
+#define CONFIGS_SIPEED_MAIX_H
+
+#include <linux/sizes.h>
+
+#define CONFIG_SYS_LOAD_ADDR 0x80000000
+/* Start just below the second bank so we don't clobber it during reloc */
+#define CONFIG_SYS_INIT_SP_ADDR 0x803FFFFF
+#define CONFIG_SYS_MALLOC_LEN SZ_128K
+#define CONFIG_SYS_CACHELINE_SIZE 64
+
+#define CONFIG_SYS_SDRAM_BASE 0x80000000
+/* Don't relocate into AI ram since it isn't set up yet */
+#define CONFIG_SYS_SDRAM_SIZE (SZ_4M + SZ_2M)
+
+/* For early init */
+#define K210_SYSCTL_BASE 0x50440000
+
+#endif /* CONFIGS_SIPEED_MAIX_H */
-- 
2.25.0

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-02-28 21:05 ` [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h Sean Anderson
@ 2020-02-28 21:46   ` Marek Vasut
  2020-02-28 22:43     ` Sean Anderson
  2020-03-03  6:58   ` Rick Chen
  1 sibling, 1 reply; 69+ messages in thread
From: Marek Vasut @ 2020-02-28 21:46 UTC (permalink / raw)
  To: u-boot

On 2/28/20 10:05 PM, Sean Anderson wrote:
> This header is needed outside of the arm architecture for the designware
> watchdog.
> 
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> ---
> This patch previously appeared as
> https://patchwork.ozlabs.org/patch/1232411/

Can't you call round_up(fls(...)) in that DW watchdog driver (and the
omap emif driver, and remove all this log2 stuff altogether) ?

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-02-28 21:46   ` Marek Vasut
@ 2020-02-28 22:43     ` Sean Anderson
  2020-02-28 23:27       ` Marek Vasut
  0 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-02-28 22:43 UTC (permalink / raw)
  To: u-boot


On 2/28/20 4:46 PM, Marek Vasut wrote:
> On 2/28/20 10:05 PM, Sean Anderson wrote:
>> This header is needed outside of the arm architecture for the designware
>> watchdog.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> ---
>> This patch previously appeared as
>> https://patchwork.ozlabs.org/patch/1232411/
> 
> Can't you call round_up(fls(...)) in that DW watchdog driver (and the
> omap emif driver, and remove all this log2 stuff altogether) ?
> 

That could work; I just wanted to make the smallest change necessary to
get everything working.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-02-28 22:43     ` Sean Anderson
@ 2020-02-28 23:27       ` Marek Vasut
  0 siblings, 0 replies; 69+ messages in thread
From: Marek Vasut @ 2020-02-28 23:27 UTC (permalink / raw)
  To: u-boot

On 2/28/20 11:43 PM, Sean Anderson wrote:
> 
> On 2/28/20 4:46 PM, Marek Vasut wrote:
>> On 2/28/20 10:05 PM, Sean Anderson wrote:
>>> This header is needed outside of the arm architecture for the designware
>>> watchdog.
>>>
>>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>> ---
>>> This patch previously appeared as
>>> https://patchwork.ozlabs.org/patch/1232411/
>>
>> Can't you call round_up(fls(...)) in that DW watchdog driver (and the
>> omap emif driver, and remove all this log2 stuff altogether) ?
>>
> 
> That could work; I just wanted to make the smallest change necessary to
> get everything working.

And yet, the smallest change could be a one-liner :-)

The removal / fix for the omap-emif should be a subsequent patch.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving
  2020-02-28 21:05 ` [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving Sean Anderson
@ 2020-02-29 17:47   ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-02-29 17:47 UTC (permalink / raw)
  To: u-boot

This patch should not be included. I meant to remove it (and it gets
reverted in the next patch), but I made an error during rebasing. Sorry
for any confustion.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 08/33] doc: Fix typo in FIT documentation
  2020-02-28 21:05 ` [PATCH v5 08/33] doc: Fix typo in FIT documentation Sean Anderson
@ 2020-03-02  2:21   ` Bin Meng
  0 siblings, 0 replies; 69+ messages in thread
From: Bin Meng @ 2020-03-02  2:21 UTC (permalink / raw)
  To: u-boot

On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>
> u_boot should be u-boot
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  doc/uImage.FIT/source_file_format.txt | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr
  2020-02-28 21:05 ` [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr Sean Anderson
@ 2020-03-02  2:22   ` Bin Meng
  2020-03-02 19:46   ` Simon Glass
  1 sibling, 0 replies; 69+ messages in thread
From: Bin Meng @ 2020-03-02  2:22 UTC (permalink / raw)
  To: u-boot

On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>
> dev_read_addr_ptr had different semantics depending on whether OF_LIVE was
> enabled. This patch converts both implementations to return NULL on error,
> and converts all call sites which check for FDT_ADDR_T_NONE to check for
> NULL instead. This patch also removes the call to map_physmem, since we
> have dev_remap_addr* for those semantics.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  drivers/clk/imx/clk-imx8mp.c                  | 2 +-
>  drivers/core/read.c                           | 2 +-
>  drivers/pinctrl/broadcom/pinctrl-bcm283x.c    | 2 +-
>  drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 2 +-
>  include/dm/read.h                             | 4 +++-
>  5 files changed, 7 insertions(+), 5 deletions(-)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 12/33] lib: Always set errno in hcreate_r
  2020-02-28 21:05 ` [PATCH v5 12/33] lib: Always set errno in hcreate_r Sean Anderson
@ 2020-03-02  2:24   ` Bin Meng
  0 siblings, 0 replies; 69+ messages in thread
From: Bin Meng @ 2020-03-02  2:24 UTC (permalink / raw)
  To: u-boot

On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>
> This could give a confusing error message if it failed and didn't set
> errno.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  lib/hashtable.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 14/33] gpio: sifive: Use generic reg read function
  2020-02-28 21:05 ` [PATCH v5 14/33] gpio: sifive: Use generic reg read function Sean Anderson
@ 2020-03-02  2:24   ` Bin Meng
  0 siblings, 0 replies; 69+ messages in thread
From: Bin Meng @ 2020-03-02  2:24 UTC (permalink / raw)
  To: u-boot

On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>
> Using an fdt-specific function causes problems with a live tree.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  drivers/gpio/sifive-gpio.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer
  2020-02-28 21:05 ` [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer Sean Anderson
@ 2020-03-02  2:27   ` Bin Meng
  2020-03-02  3:50     ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Bin Meng @ 2020-03-02  2:27 UTC (permalink / raw)
  To: u-boot

Hi Sean,

On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>
> Explicitly cast fdt_addr_t to a void pointer, since we pass it to readl.

Explicitly cast => Change?

As what the patch does

>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  drivers/gpio/dwapb_gpio.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Regards,
Bin

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer
  2020-03-02  2:27   ` Bin Meng
@ 2020-03-02  3:50     ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-03-02  3:50 UTC (permalink / raw)
  To: u-boot


On 3/1/20 9:27 PM, Bin Meng wrote:
> Hi Sean,
> 
> On Sat, Feb 29, 2020 at 5:06 AM Sean Anderson <seanga2@gmail.com> wrote:
>>
>> Explicitly cast fdt_addr_t to a void pointer, since we pass it to readl.
> 
> Explicitly cast => Change?
> 
> As what the patch does

The compiler was giving a warning since readl expects a pointer but
fdt_addr_t is typedef to some numeric type. This patch doesn't change
the variable we get from the fdt subsystem to a void pointer, but it
does change the base address we store to one.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-02-28 21:05 ` [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI Sean Anderson
@ 2020-03-02  9:08   ` Rick Chen
  2020-03-02 15:43     ` Sean Anderson
  2020-03-02 23:17   ` Lukas Auer
  1 sibling, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-02  9:08 UTC (permalink / raw)
  To: u-boot

Hi Sean

> The IPI code could have race conditions in several places.
> * Several harts could race on the value of gd->arch->clint/plic
> * Non-boot harts could race with the main hart on the DM subsystem In
>   addition, if an IPI was pending when U-Boot started, it would cause the
>   IPI handler to jump to address 0.
>
> To address these problems, a new function riscv_init_ipi is introduced. It
> is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> functions may be called. Access is synchronized by gd->arch->ipi_ready.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  arch/riscv/cpu/cpu.c                 |  9 ++++
>  arch/riscv/include/asm/global_data.h |  1 +
>  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
>  arch/riscv/lib/andes_plic.c          | 34 +++++---------
>  arch/riscv/lib/sbi_ipi.c             |  5 ++
>  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
>  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
>  7 files changed, 101 insertions(+), 92 deletions(-)
>
> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> index e457f6acbf..a971ec8694 100644
> --- a/arch/riscv/cpu/cpu.c
> +++ b/arch/riscv/cpu/cpu.c
> @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
>                         csr_write(CSR_SATP, 0);
>         }
>
> +#ifdef CONFIG_SMP
> +       ret = riscv_init_ipi();
> +       if (ret)
> +               return ret;
> +
> +       /* Atomically set a flag enabling IPI handling */
> +       WRITE_ONCE(gd->arch.ipi_ready, 1);

I think it shall not have race condition here.
Can you explain more detail why there will occur race condition ?

Hi Lukas

Do you have any comments ?

Thanks
Rick

> +#endif
> +
>         return 0;
>  }
>
> diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
> index 7276d9763f..b24f8fd2a7 100644
> --- a/arch/riscv/include/asm/global_data.h
> +++ b/arch/riscv/include/asm/global_data.h
> @@ -28,6 +28,7 @@ struct arch_global_data {
>  #endif
>  #ifdef CONFIG_SMP
>         struct ipi_data ipi[CONFIG_NR_CPUS];
> +       long ipi_ready; /* Set after riscv_init_ipi is called */
>  #endif
>  #ifndef CONFIG_XIP
>         ulong available_harts;
> diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
> index 74de92ed13..1b428856b2 100644
> --- a/arch/riscv/include/asm/smp.h
> +++ b/arch/riscv/include/asm/smp.h
> @@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
>   */
>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
>
> +/**
> + * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
> + *
> + * Platform code must provide this function. This function is called once after
> + * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
> + * before this function is called.
> + *
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_init_ipi(void);
> +
> +/**
> + * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of receiving hart
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_send_ipi(int hart);
> +
> +/**
> + * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of hart to be cleared
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_clear_ipi(int hart);
> +
> +/**
> + * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of hart to be checked
> + * @pending: Pointer to variable with result of the check,
> + *           1 if IPI is pending, 0 otherwise
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_get_ipi(int hart, int *pending);
> +
>  #endif
> diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
> index 20529ab3eb..8484f76386 100644
> --- a/arch/riscv/lib/andes_plic.c
> +++ b/arch/riscv/lib/andes_plic.c
> @@ -30,20 +30,6 @@
>  #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
>
>  DECLARE_GLOBAL_DATA_PTR;
> -static int init_plic(void);
> -
> -#define PLIC_BASE_GET(void)                                            \
> -       do {                                                            \
> -               long *ret;                                              \
> -                                                                       \
> -               if (!gd->arch.plic) {                                   \
> -                       ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
> -                       if (IS_ERR(ret))                                \
> -                               return PTR_ERR(ret);                    \
> -                       gd->arch.plic = ret;                            \
> -                       init_plic();                                    \
> -               }                                                       \
> -       } while (0)
>
>  static int enable_ipi(int hart)
>  {
> @@ -93,13 +79,21 @@ static int init_plic(void)
>         return -ENODEV;
>  }
>
> +int riscv_init_ipi(void)
> +{
> +       int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
> +
> +       if (IS_ERR(ret))
> +               return PTR_ERR(ret);
> +       gd->arch.plic = ret;
> +
> +       return init_plic();
> +}
> +
>  int riscv_send_ipi(int hart)
>  {
> -       unsigned int ipi;
> +       unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>
> -       PLIC_BASE_GET();
> -
> -       ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>         writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
>                                 gd->arch.boot_hart));
>
> @@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
>  {
>         u32 source_id;
>
> -       PLIC_BASE_GET();
> -
>         source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>         writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>
> @@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
>
>  int riscv_get_ipi(int hart, int *pending)
>  {
> -       PLIC_BASE_GET();
> -
>         *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
>                                                      gd->arch.boot_hart));
>         *pending = !!(*pending & SEND_IPI_TO_HART(hart));
> diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
> index 9a698ce74e..310d1bd2a4 100644
> --- a/arch/riscv/lib/sbi_ipi.c
> +++ b/arch/riscv/lib/sbi_ipi.c
> @@ -7,6 +7,11 @@
>  #include <common.h>
>  #include <asm/sbi.h>
>
> +int riscv_init_ipi(void)
> +{
> +       return 0;
> +}
> +
>  int riscv_send_ipi(int hart)
>  {
>         ulong mask;
> diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
> index 5e0d25720b..62c1b2b0ef 100644
> --- a/arch/riscv/lib/sifive_clint.c
> +++ b/arch/riscv/lib/sifive_clint.c
> @@ -24,22 +24,8 @@
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> -#define CLINT_BASE_GET(void)                                           \
> -       do {                                                            \
> -               long *ret;                                              \
> -                                                                       \
> -               if (!gd->arch.clint) {                                  \
> -                       ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
> -                       if (IS_ERR(ret))                                \
> -                               return PTR_ERR(ret);                    \
> -                       gd->arch.clint = ret;                           \
> -               }                                                       \
> -       } while (0)
> -
>  int riscv_get_time(u64 *time)
>  {
> -       CLINT_BASE_GET();
> -
>         *time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
>
>         return 0;
> @@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
>
>  int riscv_set_timecmp(int hart, u64 cmp)
>  {
> -       CLINT_BASE_GET();
> -
>         writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
>
>         return 0;
>  }
>
> +int riscv_init_ipi(void)
> +{
> +               long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
> +
> +               if (IS_ERR(ret))
> +                       return PTR_ERR(ret);
> +               gd->arch.clint = ret;
> +
> +               return 0;
> +}
> +
>  int riscv_send_ipi(int hart)
>  {
> -       CLINT_BASE_GET();
> -
>         writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>
>         return 0;
> @@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
>
>  int riscv_clear_ipi(int hart)
>  {
> -       CLINT_BASE_GET();
> -
>         writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>
>         return 0;
> @@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
>
>  int riscv_get_ipi(int hart, int *pending)
>  {
> -       CLINT_BASE_GET();
> -
>         *pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
>
>         return 0;
> diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
> index 17adb35730..3b1e52e9b2 100644
> --- a/arch/riscv/lib/smp.c
> +++ b/arch/riscv/lib/smp.c
> @@ -12,38 +12,6 @@
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> -/**
> - * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of receiving hart
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_send_ipi(int hart);
> -
> -/**
> - * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of hart to be cleared
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_clear_ipi(int hart);
> -
> -/**
> - * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of hart to be checked
> - * @pending: Pointer to variable with result of the check,
> - *           1 if IPI is pending, 0 otherwise
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_get_ipi(int hart, int *pending);
> -
>  static int send_ipi_many(struct ipi_data *ipi, int wait)
>  {
>         ofnode node, cpus;
> @@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
>         int ret;
>         void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
>
> -       if (hart >= CONFIG_NR_CPUS)
> +       if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
>                 return;
>
> -       __smp_mb();
> -
> -       smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> -       invalidate_icache_all();
> -
>         /*
>          * Clear the IPI to acknowledge the request before jumping to the
>          * requested function.
>          */
>         ret = riscv_clear_ipi(hart);
>         if (ret) {
> -               pr_err("Cannot clear IPI of hart %ld\n", hart);
> +               pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
>                 return;
>         }
>
> +       __smp_mb();
> +
> +       smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> +       /*
> +        * There may be an IPI raised before u-boot begins execution, so check
> +        * to ensure we actually have a function to call.
> +        */
> +       if (!smp_function)
> +               return;
> +       log_debug("hart = %lu func = %p\n", hart, smp_function);
> +       invalidate_icache_all();
> +
>         smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
>  }
>
>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
>  {
> -       int ret = 0;
> -       struct ipi_data ipi;
> +       struct ipi_data ipi = {
> +               .addr = addr,
> +               .arg0 = arg0,
> +               .arg1 = arg1,
> +       };
>
> -       ipi.addr = addr;
> -       ipi.arg0 = arg0;
> -       ipi.arg1 = arg1;
> -
> -       ret = send_ipi_many(&ipi, wait);
> -
> -       return ret;
> +       return send_ipi_many(&ipi, wait);
>  }
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-02  9:08   ` Rick Chen
@ 2020-03-02 15:43     ` Sean Anderson
  2020-03-02 23:15       ` Lukas Auer
  0 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-03-02 15:43 UTC (permalink / raw)
  To: u-boot

On 3/2/20 4:08 AM, Rick Chen wrote:
> Hi Sean
> 
>> The IPI code could have race conditions in several places.
>> * Several harts could race on the value of gd->arch->clint/plic
>> * Non-boot harts could race with the main hart on the DM subsystem In
>>   addition, if an IPI was pending when U-Boot started, it would cause the
>>   IPI handler to jump to address 0.
>>
>> To address these problems, a new function riscv_init_ipi is introduced. It
>> is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
>> functions may be called. Access is synchronized by gd->arch->ipi_ready.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> ---
>>
>> Changes in v5:
>> - New
>>
>>  arch/riscv/cpu/cpu.c                 |  9 ++++
>>  arch/riscv/include/asm/global_data.h |  1 +
>>  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
>>  arch/riscv/lib/andes_plic.c          | 34 +++++---------
>>  arch/riscv/lib/sbi_ipi.c             |  5 ++
>>  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
>>  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
>>  7 files changed, 101 insertions(+), 92 deletions(-)
>>
>> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
>> index e457f6acbf..a971ec8694 100644
>> --- a/arch/riscv/cpu/cpu.c
>> +++ b/arch/riscv/cpu/cpu.c
>> @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
>>                         csr_write(CSR_SATP, 0);
>>         }
>>
>> +#ifdef CONFIG_SMP
>> +       ret = riscv_init_ipi();
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* Atomically set a flag enabling IPI handling */
>> +       WRITE_ONCE(gd->arch.ipi_ready, 1);
> 
> I think it shall not have race condition here.
> Can you explain more detail why there will occur race condition ?
> 
> Hi Lukas
> 
> Do you have any comments ?
> 
> Thanks
> Rick

On the K210, there may already be an IPI pending when U-Boot starts.
(Perhaps the prior stage sends an IPI but does not clear it). As soon as
interrupts are enabled, the hart then tries to call riscv_clear_ipi().
Because the clint/plic has not yet been enabled, the clear_ipi function
will try and bind/probe the device. This can have really nasty effects, since
the boot hart is *also* trying to bind/probe devices.

In addition, a hart could end up trying to probe the clint/plic because
it could receive the IPI before (from its perspective) gd->arch.clint
(or plic) gets initialized.

Aside from the above, I think the macro approach is a bit confusing,
since it's unclear at first glance what function will be initializing
the clint/plic. Given U-Boot's otherwise completely SMP-unsafe design, I
think it's better to be explicit and conservative in these areas.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr
  2020-02-28 21:05 ` [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr Sean Anderson
  2020-03-02  2:22   ` Bin Meng
@ 2020-03-02 19:46   ` Simon Glass
  1 sibling, 0 replies; 69+ messages in thread
From: Simon Glass @ 2020-03-02 19:46 UTC (permalink / raw)
  To: u-boot

On Fri, 28 Feb 2020 at 14:06, Sean Anderson <seanga2@gmail.com> wrote:
>
> dev_read_addr_ptr had different semantics depending on whether OF_LIVE was
> enabled. This patch converts both implementations to return NULL on error,
> and converts all call sites which check for FDT_ADDR_T_NONE to check for
> NULL instead. This patch also removes the call to map_physmem, since we
> have dev_remap_addr* for those semantics.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  drivers/clk/imx/clk-imx8mp.c                  | 2 +-
>  drivers/core/read.c                           | 2 +-
>  drivers/pinctrl/broadcom/pinctrl-bcm283x.c    | 2 +-
>  drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 2 +-
>  include/dm/read.h                             | 4 +++-
>  5 files changed, 7 insertions(+), 5 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-02 15:43     ` Sean Anderson
@ 2020-03-02 23:15       ` Lukas Auer
  2020-03-03  8:27         ` Rick Chen
  0 siblings, 1 reply; 69+ messages in thread
From: Lukas Auer @ 2020-03-02 23:15 UTC (permalink / raw)
  To: u-boot

On Mon, 2020-03-02 at 10:43 -0500, Sean Anderson wrote:

> On 3/2/20 4:08 AM, Rick Chen wrote:
> > Hi Sean
> > 
> > > The IPI code could have race conditions in several places.
> > > * Several harts could race on the value of gd->arch->clint/plic
> > > * Non-boot harts could race with the main hart on the DM subsystem In
> > >   addition, if an IPI was pending when U-Boot started, it would cause the
> > >   IPI handler to jump to address 0.
> > > 
> > > To address these problems, a new function riscv_init_ipi is introduced. It
> > > is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> > > functions may be called. Access is synchronized by gd->arch->ipi_ready.
> > > 
> > > Signed-off-by: Sean Anderson <seanga2@gmail.com>
> > > ---
> > > 
> > > Changes in v5:
> > > - New
> > > 
> > >  arch/riscv/cpu/cpu.c                 |  9 ++++
> > >  arch/riscv/include/asm/global_data.h |  1 +
> > >  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
> > >  arch/riscv/lib/andes_plic.c          | 34 +++++---------
> > >  arch/riscv/lib/sbi_ipi.c             |  5 ++
> > >  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
> > >  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
> > >  7 files changed, 101 insertions(+), 92 deletions(-)
> > > 
> > > diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> > > index e457f6acbf..a971ec8694 100644
> > > --- a/arch/riscv/cpu/cpu.c
> > > +++ b/arch/riscv/cpu/cpu.c
> > > @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
> > >                         csr_write(CSR_SATP, 0);
> > >         }
> > > 
> > > +#ifdef CONFIG_SMP
> > > +       ret = riscv_init_ipi();
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       /* Atomically set a flag enabling IPI handling */
> > > +       WRITE_ONCE(gd->arch.ipi_ready, 1);
> > 
> > I think it shall not have race condition here.
> > Can you explain more detail why there will occur race condition ?
> > 
> > Hi Lukas
> > 
> > Do you have any comments ?
> > 
> > Thanks
> > Rick
> 
> On the K210, there may already be an IPI pending when U-Boot starts.
> (Perhaps the prior stage sends an IPI but does not clear it). As soon as
> interrupts are enabled, the hart then tries to call riscv_clear_ipi().
> Because the clint/plic has not yet been enabled, the clear_ipi function
> will try and bind/probe the device. This can have really nasty effects, since
> the boot hart is *also* trying to bind/probe devices.
> 
> In addition, a hart could end up trying to probe the clint/plic because
> it could receive the IPI before (from its perspective) gd->arch.clint
> (or plic) gets initialized.
> 

We did not have a problem with pending IPIs on other platforms. It
should suffice to clear SSIP / MSIP before enabling the interrupts.

> Aside from the above, I think the macro approach is a bit confusing,
> since it's unclear at first glance what function will be initializing
> the clint/plic. Given U-Boot's otherwise completely SMP-unsafe design, I
> think it's better to be explicit and conservative in these areas.
> 

I agree, the patch makes this more clear and helps make the code more
robust.

Thanks,
Lukas

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-02-28 21:05 ` [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI Sean Anderson
  2020-03-02  9:08   ` Rick Chen
@ 2020-03-02 23:17   ` Lukas Auer
  2020-03-02 23:43     ` Sean Anderson
  1 sibling, 1 reply; 69+ messages in thread
From: Lukas Auer @ 2020-03-02 23:17 UTC (permalink / raw)
  To: u-boot

On Fri, 2020-02-28 at 16:05 -0500, Sean Anderson wrote:

> The IPI code could have race conditions in several places.
> * Several harts could race on the value of gd->arch->clint/plic
> * Non-boot harts could race with the main hart on the DM subsystem In
>   addition, if an IPI was pending when U-Boot started, it would cause the
>   IPI handler to jump to address 0.
> 
> To address these problems, a new function riscv_init_ipi is introduced. It
> is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> functions may be called. Access is synchronized by gd->arch->ipi_ready.
> 
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
> 
> Changes in v5:
> - New
> 
>  arch/riscv/cpu/cpu.c                 |  9 ++++
>  arch/riscv/include/asm/global_data.h |  1 +
>  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
>  arch/riscv/lib/andes_plic.c          | 34 +++++---------
>  arch/riscv/lib/sbi_ipi.c             |  5 ++
>  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
>  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
>  7 files changed, 101 insertions(+), 92 deletions(-)
> 
> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> index e457f6acbf..a971ec8694 100644
> --- a/arch/riscv/cpu/cpu.c
> +++ b/arch/riscv/cpu/cpu.c
> @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
>  			csr_write(CSR_SATP, 0);
>  	}
>  
> +#ifdef CONFIG_SMP
> +	ret = riscv_init_ipi();
> +	if (ret)
> +		return ret;
> +
> +	/* Atomically set a flag enabling IPI handling */
> +	WRITE_ONCE(gd->arch.ipi_ready, 1);
> +#endif
> +
>  	return 0;
>  }
>  
> diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
> index 7276d9763f..b24f8fd2a7 100644
> --- a/arch/riscv/include/asm/global_data.h
> +++ b/arch/riscv/include/asm/global_data.h
> @@ -28,6 +28,7 @@ struct arch_global_data {
>  #endif
>  #ifdef CONFIG_SMP
>  	struct ipi_data ipi[CONFIG_NR_CPUS];
> +	long ipi_ready; /* Set after riscv_init_ipi is called */
>  #endif
>  #ifndef CONFIG_XIP
>  	ulong available_harts;
> diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
> index 74de92ed13..1b428856b2 100644
> --- a/arch/riscv/include/asm/smp.h
> +++ b/arch/riscv/include/asm/smp.h
> @@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
>   */
>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
>  
> +/**
> + * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
> + *
> + * Platform code must provide this function. This function is called once after
> + * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
> + * before this function is called.
> + *
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_init_ipi(void);
> +
> +/**
> + * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of receiving hart
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_send_ipi(int hart);
> +
> +/**
> + * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of hart to be cleared
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_clear_ipi(int hart);
> +
> +/**
> + * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> + *
> + * Platform code must provide this function.
> + *
> + * @hart: Hart ID of hart to be checked
> + * @pending: Pointer to variable with result of the check,
> + *           1 if IPI is pending, 0 otherwise
> + * @return 0 if OK, -ve on error
> + */
> +int riscv_get_ipi(int hart, int *pending);
> +
>  #endif
> diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
> index 20529ab3eb..8484f76386 100644
> --- a/arch/riscv/lib/andes_plic.c
> +++ b/arch/riscv/lib/andes_plic.c
> @@ -30,20 +30,6 @@
>  #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
>  
>  DECLARE_GLOBAL_DATA_PTR;
> -static int init_plic(void);
> -
> -#define PLIC_BASE_GET(void)						\
> -	do {								\
> -		long *ret;						\
> -									\
> -		if (!gd->arch.plic) {					\
> -			ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
> -			if (IS_ERR(ret))				\
> -				return PTR_ERR(ret);			\
> -			gd->arch.plic = ret;				\
> -			init_plic();					\
> -		}							\
> -	} while (0)
>  
>  static int enable_ipi(int hart)
>  {
> @@ -93,13 +79,21 @@ static int init_plic(void)
>  	return -ENODEV;
>  }
>  
> +int riscv_init_ipi(void)
> +{
> +	int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
> +
> +	if (IS_ERR(ret))
> +		return PTR_ERR(ret);
> +	gd->arch.plic = ret;
> +
> +	return init_plic();
> +}
> +
>  int riscv_send_ipi(int hart)
>  {
> -	unsigned int ipi;
> +	unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>  
> -	PLIC_BASE_GET();
> -
> -	ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>  	writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
>  				gd->arch.boot_hart));
>  
> @@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
>  {
>  	u32 source_id;
>  
> -	PLIC_BASE_GET();
> -
>  	source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>  	writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>  
> @@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
>  
>  int riscv_get_ipi(int hart, int *pending)
>  {
> -	PLIC_BASE_GET();
> -
>  	*pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
>  						     gd->arch.boot_hart));
>  	*pending = !!(*pending & SEND_IPI_TO_HART(hart));
> diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
> index 9a698ce74e..310d1bd2a4 100644
> --- a/arch/riscv/lib/sbi_ipi.c
> +++ b/arch/riscv/lib/sbi_ipi.c
> @@ -7,6 +7,11 @@
>  #include <common.h>
>  #include <asm/sbi.h>
>  
> +int riscv_init_ipi(void)
> +{
> +	return 0;
> +}
> +
>  int riscv_send_ipi(int hart)
>  {
>  	ulong mask;
> diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
> index 5e0d25720b..62c1b2b0ef 100644
> --- a/arch/riscv/lib/sifive_clint.c
> +++ b/arch/riscv/lib/sifive_clint.c
> @@ -24,22 +24,8 @@
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> -#define CLINT_BASE_GET(void)						\
> -	do {								\
> -		long *ret;						\
> -									\
> -		if (!gd->arch.clint) {					\
> -			ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
> -			if (IS_ERR(ret))				\
> -				return PTR_ERR(ret);			\
> -			gd->arch.clint = ret;				\
> -		}							\
> -	} while (0)
> -
>  int riscv_get_time(u64 *time)
>  {
> -	CLINT_BASE_GET();
> -
>  	*time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
>  
>  	return 0;
> @@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
>  
>  int riscv_set_timecmp(int hart, u64 cmp)
>  {
> -	CLINT_BASE_GET();
> -
>  	writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
>  
>  	return 0;
>  }
>  
> +int riscv_init_ipi(void)
> +{
> +		long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
> +
> +		if (IS_ERR(ret))
> +			return PTR_ERR(ret);
> +		gd->arch.clint = ret;
> +
> +		return 0;
> +}
> +

Please fix the indentation here.

>  int riscv_send_ipi(int hart)
>  {
> -	CLINT_BASE_GET();
> -
>  	writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>  
>  	return 0;
> @@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
>  
>  int riscv_clear_ipi(int hart)
>  {
> -	CLINT_BASE_GET();
> -
>  	writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>  
>  	return 0;
> @@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
>  
>  int riscv_get_ipi(int hart, int *pending)
>  {
> -	CLINT_BASE_GET();
> -
>  	*pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
>  
>  	return 0;
> diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
> index 17adb35730..3b1e52e9b2 100644
> --- a/arch/riscv/lib/smp.c
> +++ b/arch/riscv/lib/smp.c
> @@ -12,38 +12,6 @@
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> -/**
> - * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of receiving hart
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_send_ipi(int hart);
> -
> -/**
> - * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of hart to be cleared
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_clear_ipi(int hart);
> -
> -/**
> - * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> - *
> - * Platform code must provide this function.
> - *
> - * @hart: Hart ID of hart to be checked
> - * @pending: Pointer to variable with result of the check,
> - *           1 if IPI is pending, 0 otherwise
> - * @return 0 if OK, -ve on error
> - */
> -extern int riscv_get_ipi(int hart, int *pending);
> -
>  static int send_ipi_many(struct ipi_data *ipi, int wait)
>  {
>  	ofnode node, cpus;
> @@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
>  	int ret;
>  	void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
>  
> -	if (hart >= CONFIG_NR_CPUS)
> +	if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
>  		return;
>  
> -	__smp_mb();
> -
> -	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> -	invalidate_icache_all();
> -

Don't move this. It is intended to be run before the IPI is cleared.

>  	/*
>  	 * Clear the IPI to acknowledge the request before jumping to the
>  	 * requested function.
>  	 */
>  	ret = riscv_clear_ipi(hart);
>  	if (ret) {
> -		pr_err("Cannot clear IPI of hart %ld\n", hart);
> +		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
>  		return;
>  	}
>  
> +	__smp_mb();
> +
> +	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> +	/*
> +	 * There may be an IPI raised before u-boot begins execution, so check
> +	 * to ensure we actually have a function to call.
> +	 */
> +	if (!smp_function)
> +		return;
> +	log_debug("hart = %lu func = %p\n", hart, smp_function);

The log messages might be corrupted if multiple harts are calling the
log function here. I have not looked into the details so this might not
be an issue. In that case it is fine to keep, otherwise please remove
it.

Thanks,
Lukas

> +	invalidate_icache_all();
> +
>  	smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
>  }
>  
>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
>  {
> -	int ret = 0;
> -	struct ipi_data ipi;
> +	struct ipi_data ipi = {
> +		.addr = addr,
> +		.arg0 = arg0,
> +		.arg1 = arg1,
> +	};
>  
> -	ipi.addr = addr;
> -	ipi.arg0 = arg0;
> -	ipi.arg1 = arg1;
> -
> -	ret = send_ipi_many(&ipi, wait);
> -
> -	return ret;
> +	return send_ipi_many(&ipi, wait);
>  }

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-02 23:17   ` Lukas Auer
@ 2020-03-02 23:43     ` Sean Anderson
  2020-03-03 21:53       ` Lukas Auer
  0 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-03-02 23:43 UTC (permalink / raw)
  To: u-boot

On 3/2/20 6:17 PM, Lukas Auer wrote:
> On Fri, 2020-02-28 at 16:05 -0500, Sean Anderson wrote:
> 
>> The IPI code could have race conditions in several places.
>> * Several harts could race on the value of gd->arch->clint/plic
>> * Non-boot harts could race with the main hart on the DM subsystem In
>>   addition, if an IPI was pending when U-Boot started, it would cause the
>>   IPI handler to jump to address 0.
>>
>> To address these problems, a new function riscv_init_ipi is introduced. It
>> is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
>> functions may be called. Access is synchronized by gd->arch->ipi_ready.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> ---
>>
>> Changes in v5:
>> - New
>>
>>  arch/riscv/cpu/cpu.c                 |  9 ++++
>>  arch/riscv/include/asm/global_data.h |  1 +
>>  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
>>  arch/riscv/lib/andes_plic.c          | 34 +++++---------
>>  arch/riscv/lib/sbi_ipi.c             |  5 ++
>>  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
>>  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
>>  7 files changed, 101 insertions(+), 92 deletions(-)
>>
>> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
>> index e457f6acbf..a971ec8694 100644
>> --- a/arch/riscv/cpu/cpu.c
>> +++ b/arch/riscv/cpu/cpu.c
>> @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
>>  			csr_write(CSR_SATP, 0);
>>  	}
>>  
>> +#ifdef CONFIG_SMP
>> +	ret = riscv_init_ipi();
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Atomically set a flag enabling IPI handling */
>> +	WRITE_ONCE(gd->arch.ipi_ready, 1);
>> +#endif
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
>> index 7276d9763f..b24f8fd2a7 100644
>> --- a/arch/riscv/include/asm/global_data.h
>> +++ b/arch/riscv/include/asm/global_data.h
>> @@ -28,6 +28,7 @@ struct arch_global_data {
>>  #endif
>>  #ifdef CONFIG_SMP
>>  	struct ipi_data ipi[CONFIG_NR_CPUS];
>> +	long ipi_ready; /* Set after riscv_init_ipi is called */
>>  #endif
>>  #ifndef CONFIG_XIP
>>  	ulong available_harts;
>> diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
>> index 74de92ed13..1b428856b2 100644
>> --- a/arch/riscv/include/asm/smp.h
>> +++ b/arch/riscv/include/asm/smp.h
>> @@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
>>   */
>>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
>>  
>> +/**
>> + * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
>> + *
>> + * Platform code must provide this function. This function is called once after
>> + * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
>> + * before this function is called.
>> + *
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_init_ipi(void);
>> +
>> +/**
>> + * riscv_send_ipi() - Send inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of receiving hart
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_send_ipi(int hart);
>> +
>> +/**
>> + * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of hart to be cleared
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_clear_ipi(int hart);
>> +
>> +/**
>> + * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
>> + *
>> + * Platform code must provide this function.
>> + *
>> + * @hart: Hart ID of hart to be checked
>> + * @pending: Pointer to variable with result of the check,
>> + *           1 if IPI is pending, 0 otherwise
>> + * @return 0 if OK, -ve on error
>> + */
>> +int riscv_get_ipi(int hart, int *pending);
>> +
>>  #endif
>> diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
>> index 20529ab3eb..8484f76386 100644
>> --- a/arch/riscv/lib/andes_plic.c
>> +++ b/arch/riscv/lib/andes_plic.c
>> @@ -30,20 +30,6 @@
>>  #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>> -static int init_plic(void);
>> -
>> -#define PLIC_BASE_GET(void)						\
>> -	do {								\
>> -		long *ret;						\
>> -									\
>> -		if (!gd->arch.plic) {					\
>> -			ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
>> -			if (IS_ERR(ret))				\
>> -				return PTR_ERR(ret);			\
>> -			gd->arch.plic = ret;				\
>> -			init_plic();					\
>> -		}							\
>> -	} while (0)
>>  
>>  static int enable_ipi(int hart)
>>  {
>> @@ -93,13 +79,21 @@ static int init_plic(void)
>>  	return -ENODEV;
>>  }
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +	int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
>> +
>> +	if (IS_ERR(ret))
>> +		return PTR_ERR(ret);
>> +	gd->arch.plic = ret;
>> +
>> +	return init_plic();
>> +}
>> +
>>  int riscv_send_ipi(int hart)
>>  {
>> -	unsigned int ipi;
>> +	unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>>  
>> -	PLIC_BASE_GET();
>> -
>> -	ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
>>  	writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
>>  				gd->arch.boot_hart));
>>  
>> @@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
>>  {
>>  	u32 source_id;
>>  
>> -	PLIC_BASE_GET();
>> -
>>  	source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>>  	writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
>>  
>> @@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
>>  
>>  int riscv_get_ipi(int hart, int *pending)
>>  {
>> -	PLIC_BASE_GET();
>> -
>>  	*pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
>>  						     gd->arch.boot_hart));
>>  	*pending = !!(*pending & SEND_IPI_TO_HART(hart));
>> diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
>> index 9a698ce74e..310d1bd2a4 100644
>> --- a/arch/riscv/lib/sbi_ipi.c
>> +++ b/arch/riscv/lib/sbi_ipi.c
>> @@ -7,6 +7,11 @@
>>  #include <common.h>
>>  #include <asm/sbi.h>
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +	return 0;
>> +}
>> +
>>  int riscv_send_ipi(int hart)
>>  {
>>  	ulong mask;
>> diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
>> index 5e0d25720b..62c1b2b0ef 100644
>> --- a/arch/riscv/lib/sifive_clint.c
>> +++ b/arch/riscv/lib/sifive_clint.c
>> @@ -24,22 +24,8 @@
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>>  
>> -#define CLINT_BASE_GET(void)						\
>> -	do {								\
>> -		long *ret;						\
>> -									\
>> -		if (!gd->arch.clint) {					\
>> -			ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
>> -			if (IS_ERR(ret))				\
>> -				return PTR_ERR(ret);			\
>> -			gd->arch.clint = ret;				\
>> -		}							\
>> -	} while (0)
>> -
>>  int riscv_get_time(u64 *time)
>>  {
>> -	CLINT_BASE_GET();
>> -
>>  	*time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
>>  
>>  	return 0;
>> @@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
>>  
>>  int riscv_set_timecmp(int hart, u64 cmp)
>>  {
>> -	CLINT_BASE_GET();
>> -
>>  	writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
>>  
>>  	return 0;
>>  }
>>  
>> +int riscv_init_ipi(void)
>> +{
>> +		long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
>> +
>> +		if (IS_ERR(ret))
>> +			return PTR_ERR(ret);
>> +		gd->arch.clint = ret;
>> +
>> +		return 0;
>> +}
>> +
> 
> Please fix the indentation here.
>

Ok.

>>  int riscv_send_ipi(int hart)
>>  {
>> -	CLINT_BASE_GET();
>> -
>>  	writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>  	return 0;
>> @@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
>>  
>>  int riscv_clear_ipi(int hart)
>>  {
>> -	CLINT_BASE_GET();
>> -
>>  	writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>  	return 0;
>> @@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
>>  
>>  int riscv_get_ipi(int hart, int *pending)
>>  {
>> -	CLINT_BASE_GET();
>> -
>>  	*pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
>>  
>>  	return 0;
>> diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
>> index 17adb35730..3b1e52e9b2 100644
>> --- a/arch/riscv/lib/smp.c
>> +++ b/arch/riscv/lib/smp.c
>> @@ -12,38 +12,6 @@
>>  
>>  DECLARE_GLOBAL_DATA_PTR;
>>  
>> -/**
>> - * riscv_send_ipi() - Send inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of receiving hart
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_send_ipi(int hart);
>> -
>> -/**
>> - * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of hart to be cleared
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_clear_ipi(int hart);
>> -
>> -/**
>> - * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
>> - *
>> - * Platform code must provide this function.
>> - *
>> - * @hart: Hart ID of hart to be checked
>> - * @pending: Pointer to variable with result of the check,
>> - *           1 if IPI is pending, 0 otherwise
>> - * @return 0 if OK, -ve on error
>> - */
>> -extern int riscv_get_ipi(int hart, int *pending);
>> -
>>  static int send_ipi_many(struct ipi_data *ipi, int wait)
>>  {
>>  	ofnode node, cpus;
>> @@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
>>  	int ret;
>>  	void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
>>  
>> -	if (hart >= CONFIG_NR_CPUS)
>> +	if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
>>  		return;
>>  
>> -	__smp_mb();
>> -
>> -	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
>> -	invalidate_icache_all();
>> -
> 
> Don't move this. It is intended to be run before the IPI is cleared.

Hm, ok. I think I moved it to after because of the 'if (!smp_function)'
check, but those two don't really need to be done together.

>>  	/*
>>  	 * Clear the IPI to acknowledge the request before jumping to the
>>  	 * requested function.
>>  	 */
>>  	ret = riscv_clear_ipi(hart);
>>  	if (ret) {
>> -		pr_err("Cannot clear IPI of hart %ld\n", hart);
>> +		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
>>  		return;
>>  	}
>>  
>> +	__smp_mb();
>> +
>> +	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
>> +	/*
>> +	 * There may be an IPI raised before u-boot begins execution, so check
>> +	 * to ensure we actually have a function to call.
>> +	 */
>> +	if (!smp_function)
>> +		return;
>> +	log_debug("hart = %lu func = %p\n", hart, smp_function);
> 
> The log messages might be corrupted if multiple harts are calling the
> log function here. I have not looked into the details so this might not
> be an issue. In that case it is fine to keep, otherwise please remove
> it.

I ran into this problem a lot when debugging. I ended up implementing a
spinlock around puts/putc. I agree it's probably better to remove this,
but I worry that concurrency bugs will become much harder to track down
without some kind of feedback. (This same criticism applies to the log
message above as well).

> 
> Thanks,
> Lukas
> 
>> +	invalidate_icache_all();
>> +
>>  	smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
>>  }
>>  
>>  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait)
>>  {
>> -	int ret = 0;
>> -	struct ipi_data ipi;
>> +	struct ipi_data ipi = {
>> +		.addr = addr,
>> +		.arg0 = arg0,
>> +		.arg1 = arg1,
>> +	};
>>  
>> -	ipi.addr = addr;
>> -	ipi.arg0 = arg0;
>> -	ipi.arg1 = arg1;
>> -
>> -	ret = send_ipi_many(&ipi, wait);
>> -
>> -	return ret;
>> +	return send_ipi_many(&ipi, wait);
>>  }

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-02-28 21:05 ` [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h Sean Anderson
  2020-02-28 21:46   ` Marek Vasut
@ 2020-03-03  6:58   ` Rick Chen
  2020-03-03 14:11     ` Sean Anderson
  1 sibling, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-03  6:58 UTC (permalink / raw)
  To: u-boot

Hi Sean

> This header is needed outside of the arm architecture for the designware
> watchdog.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> ---
> This patch previously appeared as
> https://patchwork.ozlabs.org/patch/1232411/
>
> Changes in v5:
> - New

I wonder why this is ARM architecture relative but be append to RISC-V
patchsets.
Maybe it shall be send as another patch individually.

Thanks
Rick

> - Include linux/err.h explicitly
>
>  arch/arm/cpu/armv7/cache_v7.c                  | 2 +-
>  arch/arm/mach-davinci/spl.c                    | 2 +-
>  arch/arm/mach-omap2/clocks-common.c            | 2 +-
>  arch/arm/mach-omap2/emif-common.c              | 2 +-
>  arch/arm/mach-omap2/omap4/emif.c               | 2 +-
>  arch/arm/mach-omap2/omap5/dra7xx_iodelay.c     | 2 +-
>  arch/arm/mach-omap2/omap5/emif.c               | 2 +-
>  arch/arm/mach-omap2/omap5/hwinit.c             | 2 +-
>  arch/arm/mach-socfpga/spl_a10.c                | 2 +-
>  arch/arm/mach-socfpga/spl_agilex.c             | 2 +-
>  arch/arm/mach-socfpga/spl_gen5.c               | 2 +-
>  arch/arm/mach-socfpga/spl_s10.c                | 2 +-
>  drivers/watchdog/designware_wdt.c              | 3 ++-
>  arch/arm/include/asm/utils.h => include/log2.h | 4 ++--
>  14 files changed, 16 insertions(+), 15 deletions(-)
>  rename arch/arm/include/asm/utils.h => include/log2.h (93%)
>
> diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c
> index 99eb7db342..049a27cc92 100644
> --- a/arch/arm/cpu/armv7/cache_v7.c
> +++ b/arch/arm/cpu/armv7/cache_v7.c
> @@ -8,7 +8,7 @@
>  #include <linux/types.h>
>  #include <common.h>
>  #include <asm/armv7.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>
>  #define ARMV7_DCACHE_INVAL_RANGE       1
>  #define ARMV7_DCACHE_CLEAN_INVAL_RANGE 2
> diff --git a/arch/arm/mach-davinci/spl.c b/arch/arm/mach-davinci/spl.c
> index d44e840aa0..c1a62b662d 100644
> --- a/arch/arm/mach-davinci/spl.c
> +++ b/arch/arm/mach-davinci/spl.c
> @@ -8,7 +8,7 @@
>  #include <hang.h>
>  #include <spl.h>
>  #include <asm/u-boot.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <nand.h>
>  #include <asm/arch/dm365_lowlevel.h>
>  #include <ns16550.h>
> diff --git a/arch/arm/mach-omap2/clocks-common.c b/arch/arm/mach-omap2/clocks-common.c
> index 9aff83e9df..67c62e8eb0 100644
> --- a/arch/arm/mach-omap2/clocks-common.c
> +++ b/arch/arm/mach-omap2/clocks-common.c
> @@ -19,7 +19,7 @@
>  #include <asm/gpio.h>
>  #include <asm/arch/clock.h>
>  #include <asm/arch/sys_proto.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <asm/omap_gpio.h>
>  #include <asm/emif.h>
>
> diff --git a/arch/arm/mach-omap2/emif-common.c b/arch/arm/mach-omap2/emif-common.c
> index 4658f67e84..2762934092 100644
> --- a/arch/arm/mach-omap2/emif-common.c
> +++ b/arch/arm/mach-omap2/emif-common.c
> @@ -17,7 +17,7 @@
>  #include <asm/arch/sys_proto.h>
>  #include <asm/omap_common.h>
>  #include <asm/omap_sec_common.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <linux/compiler.h>
>  #include <asm/ti-common/ti-edma3.h>
>
> diff --git a/arch/arm/mach-omap2/omap4/emif.c b/arch/arm/mach-omap2/omap4/emif.c
> index 35a51645be..d2b530535e 100644
> --- a/arch/arm/mach-omap2/omap4/emif.c
> +++ b/arch/arm/mach-omap2/omap4/emif.c
> @@ -11,7 +11,7 @@
>  #include <common.h>
>  #include <asm/emif.h>
>  #include <asm/arch/sys_proto.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>
>  #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
>  u32 *const T_num = (u32 *)OMAP_SRAM_SCRATCH_EMIF_T_NUM;
> diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
> index 598074ba21..607188bcae 100644
> --- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
> +++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
> @@ -8,7 +8,7 @@
>
>  #include <common.h>
>  #include <hang.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <asm/arch/dra7xx_iodelay.h>
>  #include <asm/arch/omap.h>
>  #include <asm/arch/sys_proto.h>
> diff --git a/arch/arm/mach-omap2/omap5/emif.c b/arch/arm/mach-omap2/omap5/emif.c
> index f3661a0e74..a5c74261c0 100644
> --- a/arch/arm/mach-omap2/omap5/emif.c
> +++ b/arch/arm/mach-omap2/omap5/emif.c
> @@ -11,7 +11,7 @@
>  #include <common.h>
>  #include <asm/emif.h>
>  #include <asm/arch/sys_proto.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>
>  #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
>  #define print_timing_reg(reg) debug(#reg" - 0x%08x\n", (reg))
> diff --git a/arch/arm/mach-omap2/omap5/hwinit.c b/arch/arm/mach-omap2/omap5/hwinit.c
> index 3b1733099f..290449e918 100644
> --- a/arch/arm/mach-omap2/omap5/hwinit.c
> +++ b/arch/arm/mach-omap2/omap5/hwinit.c
> @@ -19,7 +19,7 @@
>  #include <asm/arch/sys_proto.h>
>  #include <asm/arch/clock.h>
>  #include <linux/sizes.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <asm/arch/gpio.h>
>  #include <asm/emif.h>
>  #include <asm/omap_common.h>
> diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
> index d9ef851054..0f24bec4ef 100644
> --- a/arch/arm/mach-socfpga/spl_a10.c
> +++ b/arch/arm/mach-socfpga/spl_a10.c
> @@ -9,7 +9,7 @@
>  #include <asm/io.h>
>  #include <asm/pl310.h>
>  #include <asm/u-boot.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <image.h>
>  #include <asm/arch/reset_manager.h>
>  #include <spl.h>
> diff --git a/arch/arm/mach-socfpga/spl_agilex.c b/arch/arm/mach-socfpga/spl_agilex.c
> index ecc1a35c49..54b16c9061 100644
> --- a/arch/arm/mach-socfpga/spl_agilex.c
> +++ b/arch/arm/mach-socfpga/spl_agilex.c
> @@ -6,7 +6,7 @@
>
>  #include <asm/io.h>
>  #include <asm/u-boot.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <common.h>
>  #include <hang.h>
>  #include <image.h>
> diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c
> index a01e2a5cb9..bab609ad29 100644
> --- a/arch/arm/mach-socfpga/spl_gen5.c
> +++ b/arch/arm/mach-socfpga/spl_gen5.c
> @@ -7,7 +7,7 @@
>  #include <hang.h>
>  #include <asm/io.h>
>  #include <asm/u-boot.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <image.h>
>  #include <asm/arch/reset_manager.h>
>  #include <spl.h>
> diff --git a/arch/arm/mach-socfpga/spl_s10.c b/arch/arm/mach-socfpga/spl_s10.c
> index d89151d902..48c64ef47a 100644
> --- a/arch/arm/mach-socfpga/spl_s10.c
> +++ b/arch/arm/mach-socfpga/spl_s10.c
> @@ -7,7 +7,7 @@
>  #include <hang.h>
>  #include <asm/io.h>
>  #include <asm/u-boot.h>
> -#include <asm/utils.h>
> +#include <log2.h>
>  #include <common.h>
>  #include <debug_uart.h>
>  #include <image.h>
> diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
> index 1024a04596..a58a2dfca4 100644
> --- a/drivers/watchdog/designware_wdt.c
> +++ b/drivers/watchdog/designware_wdt.c
> @@ -9,7 +9,8 @@
>  #include <reset.h>
>  #include <wdt.h>
>  #include <asm/io.h>
> -#include <asm/utils.h>
> +#include <log2.h>
> +#include <watchdog.h>
>
>  #define DW_WDT_CR      0x00
>  #define DW_WDT_TORR    0x04
> diff --git a/arch/arm/include/asm/utils.h b/include/log2.h
> similarity index 93%
> rename from arch/arm/include/asm/utils.h
> rename to include/log2.h
> index eee60c50e9..4f89d3dd76 100644
> --- a/arch/arm/include/asm/utils.h
> +++ b/include/log2.h
> @@ -4,8 +4,8 @@
>   * Texas Instruments, <www.ti.com>
>   * Aneesh V <aneesh@ti.com>
>   */
> -#ifndef _UTILS_H_
> -#define _UTILS_H_
> +#ifndef _LOG2_H_
> +#define _LOG2_H_
>
>  static inline s32 log_2_n_round_up(u32 n)
>  {
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-02 23:15       ` Lukas Auer
@ 2020-03-03  8:27         ` Rick Chen
  2020-03-05  2:18           ` Rick Chen
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-03  8:27 UTC (permalink / raw)
  To: u-boot

Hi Sean

> On Mon, 2020-03-02 at 10:43 -0500, Sean Anderson wrote:
>
> > On 3/2/20 4:08 AM, Rick Chen wrote:
> > > Hi Sean
> > >
> > > > The IPI code could have race conditions in several places.
> > > > * Several harts could race on the value of gd->arch->clint/plic
> > > > * Non-boot harts could race with the main hart on the DM subsystem In
> > > >   addition, if an IPI was pending when U-Boot started, it would cause the
> > > >   IPI handler to jump to address 0.
> > > >
> > > > To address these problems, a new function riscv_init_ipi is introduced. It
> > > > is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> > > > functions may be called. Access is synchronized by gd->arch->ipi_ready.
> > > >
> > > > Signed-off-by: Sean Anderson <seanga2@gmail.com>
> > > > ---
> > > >
> > > > Changes in v5:
> > > > - New
> > > >
> > > >  arch/riscv/cpu/cpu.c                 |  9 ++++
> > > >  arch/riscv/include/asm/global_data.h |  1 +
> > > >  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
> > > >  arch/riscv/lib/andes_plic.c          | 34 +++++---------
> > > >  arch/riscv/lib/sbi_ipi.c             |  5 ++
> > > >  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
> > > >  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
> > > >  7 files changed, 101 insertions(+), 92 deletions(-)
> > > >
> > > > diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> > > > index e457f6acbf..a971ec8694 100644
> > > > --- a/arch/riscv/cpu/cpu.c
> > > > +++ b/arch/riscv/cpu/cpu.c
> > > > @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
> > > >                         csr_write(CSR_SATP, 0);
> > > >         }
> > > >
> > > > +#ifdef CONFIG_SMP
> > > > +       ret = riscv_init_ipi();
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       /* Atomically set a flag enabling IPI handling */
> > > > +       WRITE_ONCE(gd->arch.ipi_ready, 1);
> > >
> > > I think it shall not have race condition here.
> > > Can you explain more detail why there will occur race condition ?
> > >
> > > Hi Lukas
> > >
> > > Do you have any comments ?
> > >
> > > Thanks
> > > Rick
> >
> > On the K210, there may already be an IPI pending when U-Boot starts.
> > (Perhaps the prior stage sends an IPI but does not clear it). As soon as
> > interrupts are enabled, the hart then tries to call riscv_clear_ipi().
> > Because the clint/plic has not yet been enabled, the clear_ipi function
> > will try and bind/probe the device. This can have really nasty effects, since
> > the boot hart is *also* trying to bind/probe devices.
> >
> > In addition, a hart could end up trying to probe the clint/plic because
> > it could receive the IPI before (from its perspective) gd->arch.clint
> > (or plic) gets initialized.
> >
>
> We did not have a problem with pending IPIs on other platforms. It
> should suffice to clear SSIP / MSIP before enabling the interrupts.
>

Can you try to clear mip/sip in startup flow before secondary_hart_loop:
Maybe it can help to overcome the problem of calling riscv_clear_ipi()
before riscv_init_ipi() that you added.

Thanks,
Rick

> > Aside from the above, I think the macro approach is a bit confusing,
> > since it's unclear at first glance what function will be initializing
> > the clint/plic. Given U-Boot's otherwise completely SMP-unsafe design, I
> > think it's better to be explicit and conservative in these areas.
> >
>
> I agree, the patch makes this more clear and helps make the code more
> robust.
>
> Thanks,
> Lukas

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h
  2020-03-03  6:58   ` Rick Chen
@ 2020-03-03 14:11     ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-03-03 14:11 UTC (permalink / raw)
  To: u-boot

On 3/3/20 1:58 AM, Rick Chen wrote:
> Hi Sean
> 
>> This header is needed outside of the arm architecture for the designware
>> watchdog.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> ---
>> This patch previously appeared as
>> https://patchwork.ozlabs.org/patch/1232411/
>>
>> Changes in v5:
>> - New
> 
> I wonder why this is ARM architecture relative but be append to RISC-V
> patchsets.
> Maybe it shall be send as another patch individually.
> 
> Thanks
> Rick
> 

This header is used by the designware watchdog. However, it currently is
located in arch/arm/include, so it cannot be used on riscv. Marek's
suggestion to patch the wdt to not use log2() instead may be a better
solution to not touch so many arm files.

--Sean

>> - Include linux/err.h explicitly
>>
>>  arch/arm/cpu/armv7/cache_v7.c                  | 2 +-
>>  arch/arm/mach-davinci/spl.c                    | 2 +-
>>  arch/arm/mach-omap2/clocks-common.c            | 2 +-
>>  arch/arm/mach-omap2/emif-common.c              | 2 +-
>>  arch/arm/mach-omap2/omap4/emif.c               | 2 +-
>>  arch/arm/mach-omap2/omap5/dra7xx_iodelay.c     | 2 +-
>>  arch/arm/mach-omap2/omap5/emif.c               | 2 +-
>>  arch/arm/mach-omap2/omap5/hwinit.c             | 2 +-
>>  arch/arm/mach-socfpga/spl_a10.c                | 2 +-
>>  arch/arm/mach-socfpga/spl_agilex.c             | 2 +-
>>  arch/arm/mach-socfpga/spl_gen5.c               | 2 +-
>>  arch/arm/mach-socfpga/spl_s10.c                | 2 +-
>>  drivers/watchdog/designware_wdt.c              | 3 ++-
>>  arch/arm/include/asm/utils.h => include/log2.h | 4 ++--
>>  14 files changed, 16 insertions(+), 15 deletions(-)
>>  rename arch/arm/include/asm/utils.h => include/log2.h (93%)
>>
>> diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c
>> index 99eb7db342..049a27cc92 100644
>> --- a/arch/arm/cpu/armv7/cache_v7.c
>> +++ b/arch/arm/cpu/armv7/cache_v7.c
>> @@ -8,7 +8,7 @@
>>  #include <linux/types.h>
>>  #include <common.h>
>>  #include <asm/armv7.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>
>>  #define ARMV7_DCACHE_INVAL_RANGE       1
>>  #define ARMV7_DCACHE_CLEAN_INVAL_RANGE 2
>> diff --git a/arch/arm/mach-davinci/spl.c b/arch/arm/mach-davinci/spl.c
>> index d44e840aa0..c1a62b662d 100644
>> --- a/arch/arm/mach-davinci/spl.c
>> +++ b/arch/arm/mach-davinci/spl.c
>> @@ -8,7 +8,7 @@
>>  #include <hang.h>
>>  #include <spl.h>
>>  #include <asm/u-boot.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <nand.h>
>>  #include <asm/arch/dm365_lowlevel.h>
>>  #include <ns16550.h>
>> diff --git a/arch/arm/mach-omap2/clocks-common.c b/arch/arm/mach-omap2/clocks-common.c
>> index 9aff83e9df..67c62e8eb0 100644
>> --- a/arch/arm/mach-omap2/clocks-common.c
>> +++ b/arch/arm/mach-omap2/clocks-common.c
>> @@ -19,7 +19,7 @@
>>  #include <asm/gpio.h>
>>  #include <asm/arch/clock.h>
>>  #include <asm/arch/sys_proto.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <asm/omap_gpio.h>
>>  #include <asm/emif.h>
>>
>> diff --git a/arch/arm/mach-omap2/emif-common.c b/arch/arm/mach-omap2/emif-common.c
>> index 4658f67e84..2762934092 100644
>> --- a/arch/arm/mach-omap2/emif-common.c
>> +++ b/arch/arm/mach-omap2/emif-common.c
>> @@ -17,7 +17,7 @@
>>  #include <asm/arch/sys_proto.h>
>>  #include <asm/omap_common.h>
>>  #include <asm/omap_sec_common.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <linux/compiler.h>
>>  #include <asm/ti-common/ti-edma3.h>
>>
>> diff --git a/arch/arm/mach-omap2/omap4/emif.c b/arch/arm/mach-omap2/omap4/emif.c
>> index 35a51645be..d2b530535e 100644
>> --- a/arch/arm/mach-omap2/omap4/emif.c
>> +++ b/arch/arm/mach-omap2/omap4/emif.c
>> @@ -11,7 +11,7 @@
>>  #include <common.h>
>>  #include <asm/emif.h>
>>  #include <asm/arch/sys_proto.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>
>>  #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
>>  u32 *const T_num = (u32 *)OMAP_SRAM_SCRATCH_EMIF_T_NUM;
>> diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
>> index 598074ba21..607188bcae 100644
>> --- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
>> +++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c
>> @@ -8,7 +8,7 @@
>>
>>  #include <common.h>
>>  #include <hang.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <asm/arch/dra7xx_iodelay.h>
>>  #include <asm/arch/omap.h>
>>  #include <asm/arch/sys_proto.h>
>> diff --git a/arch/arm/mach-omap2/omap5/emif.c b/arch/arm/mach-omap2/omap5/emif.c
>> index f3661a0e74..a5c74261c0 100644
>> --- a/arch/arm/mach-omap2/omap5/emif.c
>> +++ b/arch/arm/mach-omap2/omap5/emif.c
>> @@ -11,7 +11,7 @@
>>  #include <common.h>
>>  #include <asm/emif.h>
>>  #include <asm/arch/sys_proto.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>
>>  #ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
>>  #define print_timing_reg(reg) debug(#reg" - 0x%08x\n", (reg))
>> diff --git a/arch/arm/mach-omap2/omap5/hwinit.c b/arch/arm/mach-omap2/omap5/hwinit.c
>> index 3b1733099f..290449e918 100644
>> --- a/arch/arm/mach-omap2/omap5/hwinit.c
>> +++ b/arch/arm/mach-omap2/omap5/hwinit.c
>> @@ -19,7 +19,7 @@
>>  #include <asm/arch/sys_proto.h>
>>  #include <asm/arch/clock.h>
>>  #include <linux/sizes.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <asm/arch/gpio.h>
>>  #include <asm/emif.h>
>>  #include <asm/omap_common.h>
>> diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
>> index d9ef851054..0f24bec4ef 100644
>> --- a/arch/arm/mach-socfpga/spl_a10.c
>> +++ b/arch/arm/mach-socfpga/spl_a10.c
>> @@ -9,7 +9,7 @@
>>  #include <asm/io.h>
>>  #include <asm/pl310.h>
>>  #include <asm/u-boot.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <image.h>
>>  #include <asm/arch/reset_manager.h>
>>  #include <spl.h>
>> diff --git a/arch/arm/mach-socfpga/spl_agilex.c b/arch/arm/mach-socfpga/spl_agilex.c
>> index ecc1a35c49..54b16c9061 100644
>> --- a/arch/arm/mach-socfpga/spl_agilex.c
>> +++ b/arch/arm/mach-socfpga/spl_agilex.c
>> @@ -6,7 +6,7 @@
>>
>>  #include <asm/io.h>
>>  #include <asm/u-boot.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <common.h>
>>  #include <hang.h>
>>  #include <image.h>
>> diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c
>> index a01e2a5cb9..bab609ad29 100644
>> --- a/arch/arm/mach-socfpga/spl_gen5.c
>> +++ b/arch/arm/mach-socfpga/spl_gen5.c
>> @@ -7,7 +7,7 @@
>>  #include <hang.h>
>>  #include <asm/io.h>
>>  #include <asm/u-boot.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <image.h>
>>  #include <asm/arch/reset_manager.h>
>>  #include <spl.h>
>> diff --git a/arch/arm/mach-socfpga/spl_s10.c b/arch/arm/mach-socfpga/spl_s10.c
>> index d89151d902..48c64ef47a 100644
>> --- a/arch/arm/mach-socfpga/spl_s10.c
>> +++ b/arch/arm/mach-socfpga/spl_s10.c
>> @@ -7,7 +7,7 @@
>>  #include <hang.h>
>>  #include <asm/io.h>
>>  #include <asm/u-boot.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>>  #include <common.h>
>>  #include <debug_uart.h>
>>  #include <image.h>
>> diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
>> index 1024a04596..a58a2dfca4 100644
>> --- a/drivers/watchdog/designware_wdt.c
>> +++ b/drivers/watchdog/designware_wdt.c
>> @@ -9,7 +9,8 @@
>>  #include <reset.h>
>>  #include <wdt.h>
>>  #include <asm/io.h>
>> -#include <asm/utils.h>
>> +#include <log2.h>
>> +#include <watchdog.h>
>>
>>  #define DW_WDT_CR      0x00
>>  #define DW_WDT_TORR    0x04
>> diff --git a/arch/arm/include/asm/utils.h b/include/log2.h
>> similarity index 93%
>> rename from arch/arm/include/asm/utils.h
>> rename to include/log2.h
>> index eee60c50e9..4f89d3dd76 100644
>> --- a/arch/arm/include/asm/utils.h
>> +++ b/include/log2.h
>> @@ -4,8 +4,8 @@
>>   * Texas Instruments, <www.ti.com>
>>   * Aneesh V <aneesh@ti.com>
>>   */
>> -#ifndef _UTILS_H_
>> -#define _UTILS_H_
>> +#ifndef _LOG2_H_
>> +#define _LOG2_H_
>>
>>  static inline s32 log_2_n_round_up(u32 n)
>>  {
>> --
>> 2.25.0
>>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-02 23:43     ` Sean Anderson
@ 2020-03-03 21:53       ` Lukas Auer
  2020-03-03 21:57         ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Lukas Auer @ 2020-03-03 21:53 UTC (permalink / raw)
  To: u-boot

On Mon, 2020-03-02 at 18:43 -0500, Sean Anderson wrote:
> On 3/2/20 6:17 PM, Lukas Auer wrote:
> > On Fri, 2020-02-28 at 16:05 -0500, Sean Anderson wrote:
> > 
> > > The IPI code could have race conditions in several places.
> > > * Several harts could race on the value of gd->arch->clint/plic
> > > * Non-boot harts could race with the main hart on the DM subsystem In
> > >   addition, if an IPI was pending when U-Boot started, it would cause the
> > >   IPI handler to jump to address 0.
> > > 
> > > To address these problems, a new function riscv_init_ipi is introduced. It
> > > is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> > > functions may be called. Access is synchronized by gd->arch->ipi_ready.
> > > 
> > > Signed-off-by: Sean Anderson <seanga2@gmail.com>
> > > ---
> > > 
> > > Changes in v5:
> > > - New
> > > 
> > >  arch/riscv/cpu/cpu.c                 |  9 ++++
> > >  arch/riscv/include/asm/global_data.h |  1 +
> > >  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
> > >  arch/riscv/lib/andes_plic.c          | 34 +++++---------
> > >  arch/riscv/lib/sbi_ipi.c             |  5 ++
> > >  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
> > >  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
> > >  7 files changed, 101 insertions(+), 92 deletions(-)
> > > 
> > > diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> > > index e457f6acbf..a971ec8694 100644
> > > --- a/arch/riscv/cpu/cpu.c
> > > +++ b/arch/riscv/cpu/cpu.c
> > > @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
> > >  			csr_write(CSR_SATP, 0);
> > >  	}
> > >  
> > > +#ifdef CONFIG_SMP
> > > +	ret = riscv_init_ipi();
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	/* Atomically set a flag enabling IPI handling */
> > > +	WRITE_ONCE(gd->arch.ipi_ready, 1);
> > > +#endif
> > > +
> > >  	return 0;
> > >  }
> > >  
> > > diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
> > > index 7276d9763f..b24f8fd2a7 100644
> > > --- a/arch/riscv/include/asm/global_data.h
> > > +++ b/arch/riscv/include/asm/global_data.h
> > > @@ -28,6 +28,7 @@ struct arch_global_data {
> > >  #endif
> > >  #ifdef CONFIG_SMP
> > >  	struct ipi_data ipi[CONFIG_NR_CPUS];
> > > +	long ipi_ready; /* Set after riscv_init_ipi is called */
> > >  #endif
> > >  #ifndef CONFIG_XIP
> > >  	ulong available_harts;
> > > diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
> > > index 74de92ed13..1b428856b2 100644
> > > --- a/arch/riscv/include/asm/smp.h
> > > +++ b/arch/riscv/include/asm/smp.h
> > > @@ -51,4 +51,47 @@ void handle_ipi(ulong hart);
> > >   */
> > >  int smp_call_function(ulong addr, ulong arg0, ulong arg1, int wait);
> > >  
> > > +/**
> > > + * riscv_init_ipi() - Initialize inter-process interrupt (IPI) driver
> > > + *
> > > + * Platform code must provide this function. This function is called once after
> > > + * the cpu driver is initialized. No other riscv_*_ipi() calls will be made
> > > + * before this function is called.
> > > + *
> > > + * @return 0 if OK, -ve on error
> > > + */
> > > +int riscv_init_ipi(void);
> > > +
> > > +/**
> > > + * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> > > + *
> > > + * Platform code must provide this function.
> > > + *
> > > + * @hart: Hart ID of receiving hart
> > > + * @return 0 if OK, -ve on error
> > > + */
> > > +int riscv_send_ipi(int hart);
> > > +
> > > +/**
> > > + * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> > > + *
> > > + * Platform code must provide this function.
> > > + *
> > > + * @hart: Hart ID of hart to be cleared
> > > + * @return 0 if OK, -ve on error
> > > + */
> > > +int riscv_clear_ipi(int hart);
> > > +
> > > +/**
> > > + * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> > > + *
> > > + * Platform code must provide this function.
> > > + *
> > > + * @hart: Hart ID of hart to be checked
> > > + * @pending: Pointer to variable with result of the check,
> > > + *           1 if IPI is pending, 0 otherwise
> > > + * @return 0 if OK, -ve on error
> > > + */
> > > +int riscv_get_ipi(int hart, int *pending);
> > > +
> > >  #endif
> > > diff --git a/arch/riscv/lib/andes_plic.c b/arch/riscv/lib/andes_plic.c
> > > index 20529ab3eb..8484f76386 100644
> > > --- a/arch/riscv/lib/andes_plic.c
> > > +++ b/arch/riscv/lib/andes_plic.c
> > > @@ -30,20 +30,6 @@
> > >  #define SEND_IPI_TO_HART(hart)  (0x80 >> (hart))
> > >  
> > >  DECLARE_GLOBAL_DATA_PTR;
> > > -static int init_plic(void);
> > > -
> > > -#define PLIC_BASE_GET(void)						\
> > > -	do {								\
> > > -		long *ret;						\
> > > -									\
> > > -		if (!gd->arch.plic) {					\
> > > -			ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
> > > -			if (IS_ERR(ret))				\
> > > -				return PTR_ERR(ret);			\
> > > -			gd->arch.plic = ret;				\
> > > -			init_plic();					\
> > > -		}							\
> > > -	} while (0)
> > >  
> > >  static int enable_ipi(int hart)
> > >  {
> > > @@ -93,13 +79,21 @@ static int init_plic(void)
> > >  	return -ENODEV;
> > >  }
> > >  
> > > +int riscv_init_ipi(void)
> > > +{
> > > +	int ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
> > > +
> > > +	if (IS_ERR(ret))
> > > +		return PTR_ERR(ret);
> > > +	gd->arch.plic = ret;
> > > +
> > > +	return init_plic();
> > > +}
> > > +
> > >  int riscv_send_ipi(int hart)
> > >  {
> > > -	unsigned int ipi;
> > > +	unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
> > >  
> > > -	PLIC_BASE_GET();
> > > -
> > > -	ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
> > >  	writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
> > >  				gd->arch.boot_hart));
> > >  
> > > @@ -110,8 +104,6 @@ int riscv_clear_ipi(int hart)
> > >  {
> > >  	u32 source_id;
> > >  
> > > -	PLIC_BASE_GET();
> > > -
> > >  	source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
> > >  	writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
> > >  
> > > @@ -120,8 +112,6 @@ int riscv_clear_ipi(int hart)
> > >  
> > >  int riscv_get_ipi(int hart, int *pending)
> > >  {
> > > -	PLIC_BASE_GET();
> > > -
> > >  	*pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
> > >  						     gd->arch.boot_hart));
> > >  	*pending = !!(*pending & SEND_IPI_TO_HART(hart));
> > > diff --git a/arch/riscv/lib/sbi_ipi.c b/arch/riscv/lib/sbi_ipi.c
> > > index 9a698ce74e..310d1bd2a4 100644
> > > --- a/arch/riscv/lib/sbi_ipi.c
> > > +++ b/arch/riscv/lib/sbi_ipi.c
> > > @@ -7,6 +7,11 @@
> > >  #include <common.h>
> > >  #include <asm/sbi.h>
> > >  
> > > +int riscv_init_ipi(void)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > >  int riscv_send_ipi(int hart)
> > >  {
> > >  	ulong mask;
> > > diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c
> > > index 5e0d25720b..62c1b2b0ef 100644
> > > --- a/arch/riscv/lib/sifive_clint.c
> > > +++ b/arch/riscv/lib/sifive_clint.c
> > > @@ -24,22 +24,8 @@
> > >  
> > >  DECLARE_GLOBAL_DATA_PTR;
> > >  
> > > -#define CLINT_BASE_GET(void)						\
> > > -	do {								\
> > > -		long *ret;						\
> > > -									\
> > > -		if (!gd->arch.clint) {					\
> > > -			ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \
> > > -			if (IS_ERR(ret))				\
> > > -				return PTR_ERR(ret);			\
> > > -			gd->arch.clint = ret;				\
> > > -		}							\
> > > -	} while (0)
> > > -
> > >  int riscv_get_time(u64 *time)
> > >  {
> > > -	CLINT_BASE_GET();
> > > -
> > >  	*time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
> > >  
> > >  	return 0;
> > > @@ -47,17 +33,24 @@ int riscv_get_time(u64 *time)
> > >  
> > >  int riscv_set_timecmp(int hart, u64 cmp)
> > >  {
> > > -	CLINT_BASE_GET();
> > > -
> > >  	writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
> > >  
> > >  	return 0;
> > >  }
> > >  
> > > +int riscv_init_ipi(void)
> > > +{
> > > +		long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
> > > +
> > > +		if (IS_ERR(ret))
> > > +			return PTR_ERR(ret);
> > > +		gd->arch.clint = ret;
> > > +
> > > +		return 0;
> > > +}
> > > +
> > 
> > Please fix the indentation here.
> > 
> 
> Ok.
> 
> > >  int riscv_send_ipi(int hart)
> > >  {
> > > -	CLINT_BASE_GET();
> > > -
> > >  	writel(1, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
> > >  
> > >  	return 0;
> > > @@ -65,8 +58,6 @@ int riscv_send_ipi(int hart)
> > >  
> > >  int riscv_clear_ipi(int hart)
> > >  {
> > > -	CLINT_BASE_GET();
> > > -
> > >  	writel(0, (void __iomem *)MSIP_REG(gd->arch.clint, hart));
> > >  
> > >  	return 0;
> > > @@ -74,8 +65,6 @@ int riscv_clear_ipi(int hart)
> > >  
> > >  int riscv_get_ipi(int hart, int *pending)
> > >  {
> > > -	CLINT_BASE_GET();
> > > -
> > >  	*pending = readl((void __iomem *)MSIP_REG(gd->arch.clint, hart));
> > >  
> > >  	return 0;
> > > diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
> > > index 17adb35730..3b1e52e9b2 100644
> > > --- a/arch/riscv/lib/smp.c
> > > +++ b/arch/riscv/lib/smp.c
> > > @@ -12,38 +12,6 @@
> > >  
> > >  DECLARE_GLOBAL_DATA_PTR;
> > >  
> > > -/**
> > > - * riscv_send_ipi() - Send inter-processor interrupt (IPI)
> > > - *
> > > - * Platform code must provide this function.
> > > - *
> > > - * @hart: Hart ID of receiving hart
> > > - * @return 0 if OK, -ve on error
> > > - */
> > > -extern int riscv_send_ipi(int hart);
> > > -
> > > -/**
> > > - * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
> > > - *
> > > - * Platform code must provide this function.
> > > - *
> > > - * @hart: Hart ID of hart to be cleared
> > > - * @return 0 if OK, -ve on error
> > > - */
> > > -extern int riscv_clear_ipi(int hart);
> > > -
> > > -/**
> > > - * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
> > > - *
> > > - * Platform code must provide this function.
> > > - *
> > > - * @hart: Hart ID of hart to be checked
> > > - * @pending: Pointer to variable with result of the check,
> > > - *           1 if IPI is pending, 0 otherwise
> > > - * @return 0 if OK, -ve on error
> > > - */
> > > -extern int riscv_get_ipi(int hart, int *pending);
> > > -
> > >  static int send_ipi_many(struct ipi_data *ipi, int wait)
> > >  {
> > >  	ofnode node, cpus;
> > > @@ -110,37 +78,41 @@ void handle_ipi(ulong hart)
> > >  	int ret;
> > >  	void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
> > >  
> > > -	if (hart >= CONFIG_NR_CPUS)
> > > +	if (hart >= CONFIG_NR_CPUS || !READ_ONCE(gd->arch.ipi_ready))
> > >  		return;
> > >  
> > > -	__smp_mb();
> > > -
> > > -	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> > > -	invalidate_icache_all();
> > > -
> > 
> > Don't move this. It is intended to be run before the IPI is cleared.
> 
> Hm, ok. I think I moved it to after because of the 'if (!smp_function)'
> check, but those two don't really need to be done together.
> 

Thanks! We had problems with code corruption in some situations,
because some secondary harts entered OpenSBI after the main hart while
OpenSBI expected all harts to be running OpenSBI by that time. Moving
this code block was part of the fix for this situation, see [1].

[1]: 
https://gitlab.denx.de/u-boot/u-boot/commit/90ae28143700bae4edd23930a7772899ad259058

> > >  	/*
> > >  	 * Clear the IPI to acknowledge the request before jumping to the
> > >  	 * requested function.
> > >  	 */
> > >  	ret = riscv_clear_ipi(hart);
> > >  	if (ret) {
> > > -		pr_err("Cannot clear IPI of hart %ld\n", hart);
> > > +		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
> > >  		return;
> > >  	}
> > >  
> > > +	__smp_mb();
> > > +
> > > +	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> > > +	/*
> > > +	 * There may be an IPI raised before u-boot begins execution, so check
> > > +	 * to ensure we actually have a function to call.
> > > +	 */
> > > +	if (!smp_function)
> > > +		return;
> > > +	log_debug("hart = %lu func = %p\n", hart, smp_function);
> > 
> > The log messages might be corrupted if multiple harts are calling the
> > log function here. I have not looked into the details so this might not
> > be an issue. In that case it is fine to keep, otherwise please remove
> > it.
> 
> I ran into this problem a lot when debugging. I ended up implementing a
> spinlock around puts/putc. I agree it's probably better to remove this,
> but I worry that concurrency bugs will become much harder to track down
> without some kind of feedback. (This same criticism applies to the log
> message above as well).
> 

Especially with your changes, I hope we already have or will soon reach
a code robustness level where we won't have too many concurrency bugs
in the future. :)
Let's remove it for now until the logging backend can handle this
cleanly.

Thanks,
Lukas

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-03 21:53       ` Lukas Auer
@ 2020-03-03 21:57         ` Sean Anderson
  2020-03-04 15:25           ` Lukas Auer
  0 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-03-03 21:57 UTC (permalink / raw)
  To: u-boot

On 3/3/20 4:53 PM, Lukas Auer wrote:
> On Mon, 2020-03-02 at 18:43 -0500, Sean Anderson wrote:
>> On 3/2/20 6:17 PM, Lukas Auer wrote:
>>> Don't move this. It is intended to be run before the IPI is cleared.
>>
>> Hm, ok. I think I moved it to after because of the 'if (!smp_function)'
>> check, but those two don't really need to be done together.
>>
> 
> Thanks! We had problems with code corruption in some situations,
> because some secondary harts entered OpenSBI after the main hart while
> OpenSBI expected all harts to be running OpenSBI by that time. Moving
> this code block was part of the fix for this situation, see [1].
> 
> [1]: 
> https://gitlab.denx.de/u-boot/u-boot/commit/90ae28143700bae4edd23930a7772899ad259058

Ah, this makes a lot more sense why it was located where it was.

>>>>  	/*
>>>>  	 * Clear the IPI to acknowledge the request before jumping to the
>>>>  	 * requested function.
>>>>  	 */
>>>>  	ret = riscv_clear_ipi(hart);
>>>>  	if (ret) {
>>>> -		pr_err("Cannot clear IPI of hart %ld\n", hart);
>>>> +		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
>>>>  		return;
>>>>  	}
>>>>  
>>>> +	__smp_mb();
>>>> +
>>>> +	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
>>>> +	/*
>>>> +	 * There may be an IPI raised before u-boot begins execution, so check
>>>> +	 * to ensure we actually have a function to call.
>>>> +	 */
>>>> +	if (!smp_function)
>>>> +		return;
>>>> +	log_debug("hart = %lu func = %p\n", hart, smp_function);
>>>
>>> The log messages might be corrupted if multiple harts are calling the
>>> log function here. I have not looked into the details so this might not
>>> be an issue. In that case it is fine to keep, otherwise please remove
>>> it.
>>
>> I ran into this problem a lot when debugging. I ended up implementing a
>> spinlock around puts/putc. I agree it's probably better to remove this,
>> but I worry that concurrency bugs will become much harder to track down
>> without some kind of feedback. (This same criticism applies to the log
>> message above as well).
>>
> 
> Especially with your changes, I hope we already have or will soon reach
> a code robustness level where we won't have too many concurrency bugs
> in the future. :)
> Let's remove it for now until the logging backend can handle this
> cleanly.

Ok. Should the error message above ("Cannot clear IPI of hart...") also
be removed? I found it tended to corrupt the log output if it was ever
triggered.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 33/33] riscv: Add Sipeed Maix support
  2020-02-28 21:05 ` [PATCH v5 33/33] riscv: Add Sipeed Maix support Sean Anderson
@ 2020-03-04  6:04   ` Rick Chen
  2020-03-04  7:47     ` Rick Chen
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:04 UTC (permalink / raw)
  To: u-boot

Hi Sean

> The Sipeed Maix series is a collection of boards built around the RISC-V
> Kendryte K210 processor. This processor contains several peripherals to
> accelerate neural network processing and other "ai" tasks. This includes a
> "KPU" neural network processor, an audio processor supporting beamforming
> reception, and a digital video port supporting capture and output at VGA
> resolution. Other peripherals include 8M of sram (accessible with and
> without caching); remappable pins, including 40 GPIOs; AES, FFT, and SHA256
> accelerators; a DMA controller; and I2C, I2S, and SPI controllers. Maix
> peripherals vary, but include spi flash; on-board usb-serial bridges; ports
> for cameras, displays, and sd cards; and ESP32 chips. Currently, only the
> Sipeed Maix Bit V2.0 (bitm) is supported, but the boards are fairly
> similar.
>
> Documentation for Maix boards is located at
> <http://dl.sipeed.com/MAIX/HDK/>.  Documentation for the Kendryte K210 is
> located at <https://kendryte.com/downloads/>. However, hardware details are
> rather lacking, so most technical reference has been taken from the
> standalone sdk located at
> <https://github.com/kendryte/kendryte-standalone-sdk>.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---

Please check patch, it can not apply


Applying: riscv: Add Sipeed Maix support
.git/rebase-apply/patch:344: trailing whitespace.

.git/rebase-apply/patch:346: trailing whitespace.

.git/rebase-apply/patch:368: trailing whitespace.

.git/rebase-apply/patch:370: trailing whitespace.

.git/rebase-apply/patch:377: trailing whitespace.
    Bytes Sent:2478208   BPS:72937
error: patch failed: doc/board/index.rst:16
error: doc/board/index.rst: patch does not apply
Patch failed at 0001 riscv: Add Sipeed Maix support

Thanks,
Rick

>
> Changes in v5:
> - Configure relocation location with CONFIG_SYS_SDRAM_*
> - Enable ram clocks
> - Add pinmux/gpio/led support
> - Remove (broken) MMC support
> - Store the environment in flash
> - Add partitions
> - Add bootcmd
> - Add docs for pinctrl and booting
>
> Changes in v4:
> - Rework documentation to be organized by board mfg not cpu mfg
> - Update docs to reflect working SPI support
> - Add proper spi support
> - Don't define unneecessary macros in config.h
> - Lower the default stack so it isn't clobbered on relocation
> - Update MAINTAINERS
> - Update copyright
>
> Changes in v3:
> - Reorder to be last in the patch series
> - Add documentation for the board
> - Generate defconfig with "make savedefconfig"
> - Update Kconfig to imply most features we need
> - Update MAINTAINERS
>
> Changes in v2:
> - Select CONFIG_SYS_RISCV_NOCOUNTER
> - Imply CONFIG_CLK_K210
> - Remove spurious references to CONFIG_ARCH_K210
> - Remove many configs from defconfig where the defaults were fine
> - Add a few "not set" lines to suppress unneeded defaults
> - Reduce pre-reloc malloc space, now that clocks initialization happens
>   later
>
>  arch/riscv/Kconfig                 |   4 +
>  board/sipeed/maix/Kconfig          |  72 ++++++++++
>  board/sipeed/maix/MAINTAINERS      |  11 ++
>  board/sipeed/maix/Makefile         |   5 +
>  board/sipeed/maix/maix.c           |  54 +++++++
>  configs/sipeed_maix_bitm_defconfig |  16 +++
>  doc/board/index.rst                |   1 +
>  doc/board/sipeed/index.rst         |   9 ++
>  doc/board/sipeed/maix.rst          | 223 +++++++++++++++++++++++++++++
>  include/configs/sipeed-maix.h      |  24 ++++
>  10 files changed, 419 insertions(+)
>  create mode 100644 board/sipeed/maix/Kconfig
>  create mode 100644 board/sipeed/maix/MAINTAINERS
>  create mode 100644 board/sipeed/maix/Makefile
>  create mode 100644 board/sipeed/maix/maix.c
>  create mode 100644 configs/sipeed_maix_bitm_defconfig
>  create mode 100644 doc/board/sipeed/index.rst
>  create mode 100644 doc/board/sipeed/maix.rst
>  create mode 100644 include/configs/sipeed-maix.h
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index b7a5757584..d016dd75d7 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -20,6 +20,9 @@ config TARGET_QEMU_VIRT
>  config TARGET_SIFIVE_FU540
>         bool "Support SiFive FU540 Board"
>
> +config TARGET_SIPEED_MAIX
> +       bool "Support Sipeed Maix Board"
> +
>  endchoice
>
>  config SYS_ICACHE_OFF
> @@ -53,6 +56,7 @@ source "board/AndesTech/ax25-ae350/Kconfig"
>  source "board/emulation/qemu-riscv/Kconfig"
>  source "board/microchip/mpfs_icicle/Kconfig"
>  source "board/sifive/fu540/Kconfig"
> +source "board/sipeed/maix/Kconfig"
>
>  # platform-specific options below
>  source "arch/riscv/cpu/ax25/Kconfig"
> diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig
> new file mode 100644
> index 0000000000..939eb4829a
> --- /dev/null
> +++ b/board/sipeed/maix/Kconfig
> @@ -0,0 +1,72 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> +
> +if TARGET_SIPEED_MAIX
> +
> +config SYS_BOARD
> +       default "maix"
> +
> +config SYS_VENDOR
> +       default "sipeed"
> +
> +config SYS_CPU
> +       default "generic"
> +
> +config SYS_CONFIG_NAME
> +       default "sipeed-maix"
> +
> +config SYS_TEXT_BASE
> +       default 0x80000000
> +
> +config DEFAULT_DEVICE_TREE
> +       default "k210-maix-bit"
> +
> +config NR_CPUS
> +       default 2
> +
> +config NR_DRAM_BANKS
> +       default 3
> +
> +config SF_DEFAULT_BUS
> +       default 3
> +
> +config BOARD_SPECIFIC_OPTIONS
> +       def_bool y
> +       select GENERIC_RISCV
> +       select RISCV_PRIV_1_9
> +       imply SMP
> +       imply OF_BOARD_SETUP
> +       imply DM_SERIAL
> +       imply SIFIVE_SERIAL
> +       imply SIFIVE_CLINT
> +       imply POWER_DOMAIN
> +       imply SIMPLE_PM_BUS
> +       imply CLK_CCF
> +       imply CLK_COMPOSITE_CCF
> +       imply CLK_K210
> +       imply DM_RESET
> +       imply RESET_SYSCON
> +       imply SYSRESET
> +       imply SYSRESET_SYSCON
> +       imply PINCTRL
> +       imply PINCONF
> +       imply PINCTRL_K210
> +       imply DM_GPIO
> +       imply DWAPB_GPIO
> +       imply SIFIVE_GPIO
> +       imply CMD_GPIO
> +       imply LED
> +       imply LED_GPIO
> +       imply SPI
> +       imply DESIGNWARE_SPI
> +       imply SPI_FLASH_WINBOND
> +       imply DM_MTD
> +       imply SPI_FLASH_MTD
> +       imply CMD_MTD
> +       imply ENV_IS_IN_SPI_FLASH
> +       imply MMC
> +       imply MMC_BROKEN_CD
> +       imply MMC_SPI
> +       imply WDT
> +       imply DESIGNWARE_WATCHDOG
> +endif
> diff --git a/board/sipeed/maix/MAINTAINERS b/board/sipeed/maix/MAINTAINERS
> new file mode 100644
> index 0000000000..1f33882e1e
> --- /dev/null
> +++ b/board/sipeed/maix/MAINTAINERS
> @@ -0,0 +1,11 @@
> +Sipeed Maix BOARD
> +M:     Sean Anderson <seanga2@gmail.com>
> +S:     Maintained
> +F:     arch/riscv/dts/k210.dtsi
> +F:     arch/riscv/dts/k210-maix-bit.dts
> +F:     board/sipeed/maix/
> +F:     configs/sipeed_maix_defconfig
> +F:     doc/board/sipeed/
> +F:     include/configs/sipeed-maix.h
> +F:     include/dt-bindings/*/k210-sysctl.h
> +F:     test/dm/k210_pll.c
> diff --git a/board/sipeed/maix/Makefile b/board/sipeed/maix/Makefile
> new file mode 100644
> index 0000000000..4acff5b31e
> --- /dev/null
> +++ b/board/sipeed/maix/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +# Copyright (c) 2019 Western Digital Corporation or its affiliates.
> +
> +obj-y += maix.o
> diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c
> new file mode 100644
> index 0000000000..c126cb5d67
> --- /dev/null
> +++ b/board/sipeed/maix/maix.c
> @@ -0,0 +1,54 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <fdt_support.h>
> +
> +phys_size_t get_effective_memsize(void)
> +{
> +       return CONFIG_SYS_SDRAM_SIZE;
> +}
> +
> +int board_init(void)
> +{
> +       int ret;
> +       ofnode bank = ofnode_null();
> +
> +       /* Enable RAM clocks */
> +       while (true) {
> +               struct clk clk;
> +
> +               bank = ofnode_by_prop_value(bank, "device_type", "memory",
> +                                           sizeof("memory"));
> +               if (ofnode_equal(bank, ofnode_null()))
> +                       break;
> +
> +               ret = clk_get_by_index_nodev(bank, 0, &clk);
> +               if (ret)
> +                       continue;
> +
> +               ret = clk_enable(&clk);
> +               clk_free(&clk);
> +               if (ret)
> +                       return ret;
> +       }
> +       return 0;
> +}
> +
> +int ft_board_setup(void *blob, bd_t *bd)
> +{
> +       int i;
> +       u64 base[CONFIG_NR_DRAM_BANKS];
> +       u64 size[CONFIG_NR_DRAM_BANKS];
> +
> +       for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> +               base[i] = bd->bi_dram[i].start;
> +               size[i] = bd->bi_dram[i].size;
> +       }
> +
> +       return fdt_fixup_memory_banks(blob, base, size, CONFIG_NR_DRAM_BANKS);
> +}
> diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig
> new file mode 100644
> index 0000000000..7f644e7a37
> --- /dev/null
> +++ b/configs/sipeed_maix_bitm_defconfig
> @@ -0,0 +1,16 @@
> +CONFIG_RISCV=y
> +CONFIG_ENV_SIZE=0x2000
> +CONFIG_ENV_SECT_SIZE=0x1000
> +CONFIG_ENV_OFFSET=0x7C000
> +CONFIG_ENV_OFFSET_REDUND=0x7E000
> +CONFIG_TARGET_SIPEED_MAIX=y
> +CONFIG_ARCH_RV64I=y
> +CONFIG_USE_BOOTCOMMAND=y
> +CONFIG_BOOTCOMMAND="sf probe;mtd read kernel 80000000;go 80000000"
> +CONFIG_MTDIDS_DEFAULT="nor0=spi3.0"
> +CONFIG_MTDPARTS_DEFAULT="spi3.0:496k(u-boot),16k(env),5632k(kernel),10240k(data)"
> +CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
> +# CONFIG_NET is not set
> +# CONFIG_INPUT is not set
> +# CONFIG_DM_ETH is not set
> +# CONFIG_EFI_LOADER is not set
> diff --git a/doc/board/index.rst b/doc/board/index.rst
> index b8b956d730..54cbc5c874 100644
> --- a/doc/board/index.rst
> +++ b/doc/board/index.rst
> @@ -16,4 +16,5 @@ Board-specific doc
>     renesas/index
>     rockchip/index
>     sifive/index
> +   sipeed/index
>     xilinx/index
> diff --git a/doc/board/sipeed/index.rst b/doc/board/sipeed/index.rst
> new file mode 100644
> index 0000000000..92da47fddd
> --- /dev/null
> +++ b/doc/board/sipeed/index.rst
> @@ -0,0 +1,9 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +Kendryte
> +========
> +
> +.. toctree::
> +   :maxdepth: 2
> +
> +   maix
> diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst
> new file mode 100644
> index 0000000000..c5c2e24ea5
> --- /dev/null
> +++ b/doc/board/sipeed/maix.rst
> @@ -0,0 +1,223 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> +
> +Maix Bit
> +========
> +
> +Several of the Sipeed Maix series of boards cotain the Kendryte K210 processor,
> +a 64-bit RISC-V CPU. This processor contains several peripherals to accelerate
> +neural network processing and other "ai" tasks. This includes a "KPU" neural
> +network processor, an audio processor supporting beamforming reception, and a
> +digital video port supporting capture and output at VGA resolution. Other
> +peripherals include 8M of SRAM (accessible with and without caching); remappable
> +pins, including 40 GPIOs; AES, FFT, and SHA256 accelerators; a DMA controller;
> +and I2C, I2S, and SPI controllers. Maix peripherals vary, but include spi flash;
> +on-board usb-serial bridges; ports for cameras, displays, and sd cards; and
> +ESP32 chips. Currently, only the Sipeed Maix Bit V2.0 (bitm) is supported, but
> +the boards are fairly similar.
> +
> +Documentation for Maix boards is available from
> +`Sipeed's website <http://dl.sipeed.com/MAIX/HDK/>`_.
> +Documentation for the Kendryte K210 is available from
> +`Kendryte's website <https://kendryte.com/downloads/>`_. However, hardware
> +details are rather lacking, so most technical reference has been taken from the
> +`standalone sdk <https://github.com/kendryte/kendryte-standalone-sdk>`_.
> +
> +Build and boot steps
> +--------------------
> +
> +To build u-boot, run
> +
> +.. code-block:: none
> +
> +    make sipeed_maix_bitm_defconfig
> +    make CROSS_COMPILE=<your cross compile prefix>
> +
> +To flash u-boot to a maix bit, run
> +
> +.. code-block:: none
> +
> +    kflash -tp /dev/<your tty here> -B bit_mic u-boot-dtb.bin
> +
> +Boot output should look like the following:
> +
> +.. code-block:: none
> +
> +
> +    U-Boot 2020.04-rc2-00087-g2221cc09c1-dirty (Feb 28 2020 - 13:53:09 -0500)
> +
> +    DRAM:  8 MiB
> +    WDT:   Started with servicing (60s timeout)
> +    MMC:   spi at 53000000:slot at 0: 0
> +    In:    serial at 38000000
> +    Out:   serial at 38000000
> +    Err:   serial at 38000000
> +    Hit any key to stop autoboot:  0
> +    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
> +    Reading 5242880 byte(s) at offset 0x00000000
> +    ## Starting application at 0x80000000 ...
> +
> +Flashing Images
> +---------------
> +
> +To flash a kernel, transfer it over serial, then write it to the kernel
> +partition.
> +
> +.. code-block:: none
> +
> +    => loady 80000000 1500000
> +    ## Switch baudrate to 1500000 bps and press ENTER ...
> +
> +    *** baud: 1500000
> +
> +    *** baud: 1500000 ***
> +    ## Ready for binary (ymodem) download to 0x80000000 at 1500000 bps...
> +    C
> +    *** file: loader.bin
> +    $ sz -vv loader.bin
> +    Sending: loader.bin
> +    Bytes Sent:2478208   BPS:72937
> +    Sending:
> +    Ymodem sectors/kbytes sent:   0/ 0k
> +    Transfer complete
> +
> +    *** exit status: 0 ***
> +    ## Total Size      = 0x0025d052 = 2478162 Bytes
> +    ## Switch baudrate to 115200 bps and press ESC ...
> +
> +    *** baud: 115200
> +
> +    *** baud: 115200 ***
> +    => sf probe
> +    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
> +    => mtd write kernel 80000000 0 25d052
> +    Writing 2478162 byte(s) at offset 0x00000000
> +
> +**NB:** kflash adds a 5-byte header to payloads (and a 32-byte trailer) to all
> +payloads it flashes. If you use kflash to flash your payload, you will need to
> +account for this header when specifying what offset in spi flash to load from.
> +
> +Partition Scheme
> +^^^^^^^^^^^^^^^^
> +
> +There is no partition scheme specified by the manufacturer. The only requirement
> +imposed by the firmware is that offset 0 will be loaded and ran. The default
> +partition scheme is
> +
> +========= ======== ======
> +Partition Offset   Size
> +========= ======== ======
> +u-boot    0x000000 496k
> +env       0x07C000 16k
> +kernel    0x080000 5M
> +data      0x580000 10.5M
> +========= ======== ======
> +
> +Pin Assignment
> +--------------
> +
> +The K210 contains a Fully Programmable I/O Array (FPIOA), which can remap any of
> +its 256 input functions to any any of 48 output pins. The following table has
> +the default pin assignments for the BitM.
> +
> +===== ========== =======
> +Pin   Function   Comment
> +===== ========== =======
> +IO_0  JTAG_TCLK
> +IO_1  JTAG_TDI
> +IO_2  JTAG_TMS
> +IO_3  JTAG_TDO
> +IO_4  UARTHS_RX
> +IO_5  UARTHS_TX
> +IO_6  GPIOHS_1
> +IO_7  GPIOHS_2
> +IO_8  GPIO_0
> +IO_9  GPIO_1
> +IO_10 GPIO_2
> +IO_11 GPIO_3
> +IO_12 GPIO_4     Green LED
> +IO_13 GPIO_5     Red LED
> +IO_14 GPIO_6     Blue LED
> +IO_15 GPIO_7
> +IO_16 GPIOHS_0   ISP
> +IO_17 GPIOHS_3
> +IO_18 I2S0_SCLK  MIC CLK
> +IO_19 I2S0_WS    MIC WS
> +IO_20 I2S0_IN_D0 MIC SD
> +IO_21 GPIOHS_4
> +IO_22 GPIOHS_5
> +IO_23 GPIOHS_6
> +IO_24 GPIOHS_7
> +IO_25 GPIOHS_8
> +IO_26 SPI1_D1    MMC MISO
> +IO_27 SPI1_SCLK  MMC CLK
> +IO_28 SPI1_D0    MMC MOSI
> +IO_29 GPIOHS_31  MMC CS
> +IO_30 GPIOHS_9
> +IO_31 GPIOHS_10
> +IO_32 GPIOHS_11
> +IO_33 GPIOHS_12
> +IO_34 GPIOHS_13
> +IO_35 GPIOHS_14
> +IO_36 GPIOHS_28  Panel CS
> +IO_37 GPIOHS_29  Panel RST
> +IO_38 GPIOHS_30  Panel DC
> +IO_39 SPI0_SCK   Panel WR
> +IO_40 SCCP_SDA
> +IO_41 SCCP_SCLK
> +IO_42 DVP_RST
> +IO_43 DVP_VSYNC
> +IO_44 DVP_PWDN
> +IO_45 DVP_HSYNC
> +IO_46 DVP_XCLK
> +IO_47 DVP_PCLK
> +===== ========== =======
> +
> +Over- and Under-clocking
> +------------------------
> +
> +To change the clock speed of the K210, you will need to enable
> +``CONFIG_CLK_K210_SET_RATE`` and edit the board's device tree. To do this, add a
> +section to ``arch/riscv/arch/riscv/dts/k210-maix-bit.dts`` like the following:
> +
> +.. code-block:: dts
> +
> +    &sysclk {
> +       assigned-clocks = <&sysclk K210_CLK_PLL0>;
> +       assigned-clock-rates = <800000000>;
> +    };
> +
> +There are three PLLs on the K210: PLL0 is the parent of most of the components,
> +including the CPU and RAM. PLL1 is the parent of the neural network coprocessor.
> +PLL2 is the parent of the sound processing devices. Note that child clocks of
> +PLL0 and PLL2 run at *half* the speed of the PLLs. For example, if PLL0 is
> +running at 800 MHz, then the CPU will run at 400 MHz. This is the example given
> +above. The CPU can be overclocked to around 600 MHz, and underclocked to 26 MHz.
> +
> +It is possible to set PLL2's parent to PLL0. The plls are more accurate when
> +converting between similar frequencies. This makes it easier to get an accurate
> +frequency for I2S. As an example, consider sampling an I2S device at 44.1 kHz.
> +On this device, the I2S serial clock runs at 64 times the sample rate.
> +Therefore, we would like to run PLL2 at an even multiple of 2.8224 MHz. If
> +PLL2's parent is IN0, we could use a frequency of 390 MHz (the same as the CPU's
> +default speed).  Dividing by 138 yields a serial clock of about 2.8261 MHz. This
> +results in a sample rate of 44.158 kHz---around 50 Hz or .1% too fast. If,
> +instead, we set PLL2's parent to PLL1 running at 390 MHz, and request a rate of
> +2.8224 * 136 = 383.8464 MHz, the achieved rate is 383.90625 MHz. Dividing by 136
> +yields a serial clock of about 2.8228 MHz. This results in a sample rate of
> +44.107 kHz---just 7 Hz or .02% too fast. This configuration is shown in the
> +following example:
> +
> +.. code-block:: dts
> +
> +    &sysclk {
> +       assigned-clocks = <&sysclk K210_CLK_PLL1>, <&sysclk K210_CLK_PLL2>;
> +       assigned-clock-parents = <0>, <&sysclk K210_CLK_PLL1>;
> +       assigned-clock-rates = <390000000>, <383846400>;
> +    };
> +
> +There are a couple of quirks to the PLLs. First, there are more frequency ratios
> +just above and below 1.0, but there is a small gap around 1.0. To be explicit,
> +if the input frequency is 100 MHz, it would be impossible to have an output of
> +99 or 101 MHz. In addition, there is a maximum frequency for the internal VCO,
> +so higher input/output frequencies will be less accurate than lower ones.
> diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h
> new file mode 100644
> index 0000000000..a46473fc78
> --- /dev/null
> +++ b/include/configs/sipeed-maix.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef CONFIGS_SIPEED_MAIX_H
> +#define CONFIGS_SIPEED_MAIX_H
> +
> +#include <linux/sizes.h>
> +
> +#define CONFIG_SYS_LOAD_ADDR 0x80000000
> +/* Start just below the second bank so we don't clobber it during reloc */
> +#define CONFIG_SYS_INIT_SP_ADDR 0x803FFFFF
> +#define CONFIG_SYS_MALLOC_LEN SZ_128K
> +#define CONFIG_SYS_CACHELINE_SIZE 64
> +
> +#define CONFIG_SYS_SDRAM_BASE 0x80000000
> +/* Don't relocate into AI ram since it isn't set up yet */
> +#define CONFIG_SYS_SDRAM_SIZE (SZ_4M + SZ_2M)
> +
> +/* For early init */
> +#define K210_SYSCTL_BASE 0x50440000
> +
> +#endif /* CONFIGS_SIPEED_MAIX_H */
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1
  2020-02-28 21:05 ` [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1 Sean Anderson
@ 2020-03-04  6:15   ` Rick Chen
  2020-03-04 15:01     ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:15 UTC (permalink / raw)
  To: u-boot

Hi Sean

> Some devices have different layouts for the fields in CTRL1 (e.g. the

Still not fix this typo in commit message
CTRL1 -> CTRL0

Thanks,
Rick


> Kendryte K210). Allow this layout to be configurable from the device tree.
> The documentation has been taken from Linux.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> ---
>
> Changes in v4:
> - New
>
>  .../spi/snps,dw-apb-ssi.txt                   | 43 +++++++++++++++++++
>  drivers/spi/designware_spi.c                  | 40 ++++++++++-------
>  2 files changed, 68 insertions(+), 15 deletions(-)
>  create mode 100644 doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
>
> diff --git a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
> new file mode 100644
> index 0000000000..4b6152f6b7
> --- /dev/null
> +++ b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
> @@ -0,0 +1,43 @@
> +Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
> +
> +Required properties:
> +- compatible : "snps,dw-apb-ssi"
> +- reg : The register base for the controller. For "mscc,<soc>-spi", a second
> +  register set is required (named ICPU_CFG:SPI_MST)
> +- #address-cells : <1>, as required by generic SPI binding.
> +- #size-cells : <0>, also as required by generic SPI binding.
> +- clocks : phandles for the clocks, see the description of clock-names below.
> +   The phandle for the "ssi_clk" is required. The phandle for the "pclk" clock
> +   is optional. If a single clock is specified but no clock-name, it is the
> +   "ssi_clk" clock. If both clocks are listed, the "ssi_clk" must be first.
> +
> +Optional properties:
> +- clock-names : Contains the names of the clocks:
> +    "ssi_clk", for the core clock used to generate the external SPI clock.
> +    "pclk", the interface clock, required for register access.
> +- cs-gpios : Specifies the gpio pins to be used for chipselects.
> +- num-cs : The number of chipselects. If omitted, this will default to 4.
> +- reg-io-width : The I/O register width (in bytes) implemented by this
> +  device.  Supported values are 2 or 4 (the default).
> +- snps,dfs-offset The offset in bits of the DFS field in CTRL0, defaulting to 0
> +- snps,frf-offset The offset in bits of the FRF field in CTRL0, defaulting to 4
> +- snps,tmod-offset The offset in bits of the tmode field in CTRL0, defaulting
> +  to 6
> +- snps,mode-offset The offset in bits of the work mode field in CTRL0,
> +  defaulting to 8
> +
> +Child nodes as per the generic SPI binding.
> +
> +Example:
> +
> +       spi at fff00000 {
> +               compatible = "snps,dw-apb-ssi";
> +               reg = <0xfff00000 0x1000>;
> +               interrupts = <0 154 4>;
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +               clocks = <&spi_m_clk>;
> +               num-cs = <2>;
> +               cs-gpios = <&gpio0 13 0>,
> +                          <&gpio0 14 0>;
> +       };
> diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
> index 2dc16736a3..6e1c289297 100644
> --- a/drivers/spi/designware_spi.c
> +++ b/drivers/spi/designware_spi.c
> @@ -3,6 +3,7 @@
>   * Designware master SPI core controller driver
>   *
>   * Copyright (C) 2014 Stefan Roese <sr@denx.de>
> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
>   *
>   * Very loosely based on the Linux driver:
>   * drivers/spi/spi-dw.c, which is:
> @@ -51,20 +52,14 @@
>  #define DW_SPI_DR                      0x60
>
>  /* Bit fields in CTRLR0 */
> -#define SPI_DFS_OFFSET                 0
> -
> -#define SPI_FRF_OFFSET                 4
>  #define SPI_FRF_SPI                    0x0
>  #define SPI_FRF_SSP                    0x1
>  #define SPI_FRF_MICROWIRE              0x2
>  #define SPI_FRF_RESV                   0x3
>
> -#define SPI_MODE_OFFSET                        6
> -#define SPI_SCPH_OFFSET                        6
> -#define SPI_SCOL_OFFSET                        7
> +#define SPI_MODE_SCPH                  0x1
> +#define SPI_MODE_SCOL                  0x2
>
> -#define SPI_TMOD_OFFSET                        8
> -#define SPI_TMOD_MASK                  (0x3 << SPI_TMOD_OFFSET)
>  #define        SPI_TMOD_TR                     0x0             /* xmit & recv */
>  #define SPI_TMOD_TO                    0x1             /* xmit only */
>  #define SPI_TMOD_RO                    0x2             /* recv only */
> @@ -89,6 +84,12 @@
>  struct dw_spi_platdata {
>         s32 frequency;          /* Default clock frequency, -1 for none */
>         void __iomem *regs;
> +
> +       /* Offsets in CTRL0 */
> +       u8 dfs_off;
> +       u8 frf_off;
> +       u8 tmod_off;
> +       u8 mode_off;
>  };
>
>  struct dw_spi_priv {
> @@ -115,6 +116,15 @@ struct dw_spi_priv {
>         struct reset_ctl_bulk   resets;
>  };
>
> +static inline u32 GEN_CTRL0(struct dw_spi_priv *priv,
> +                           struct dw_spi_platdata *plat)
> +{
> +       return ((priv->bits_per_word - 1) << plat->dfs_off |
> +             (priv->type << plat->frf_off) |
> +             (priv->mode << plat->mode_off) |
> +             (priv->tmode << plat->tmod_off));
> +}
> +
>  static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset)
>  {
>         return __raw_readl(priv->regs + offset);
> @@ -160,6 +170,10 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus)
>         /* Use 500KHz as a suitable default */
>         plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
>                                                500000);
> +       plat->dfs_off = dev_read_u32_default(bus, "snps,dfs-offset", 0);
> +       plat->frf_off = dev_read_u32_default(bus, "snps,frf-offset", 4);
> +       plat->mode_off = dev_read_u32_default(bus, "snps,mode-offset", 6);
> +       plat->tmod_off = dev_read_u32_default(bus, "snps,tmod-offset", 8);
>         debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs,
>               plat->frequency);
>
> @@ -388,6 +402,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>                        const void *dout, void *din, unsigned long flags)
>  {
>         struct udevice *bus = dev->parent;
> +       struct dw_spi_platdata *plat = dev_get_platdata(bus);
>         struct dw_spi_priv *priv = dev_get_priv(bus);
>         const u8 *tx = dout;
>         u8 *rx = din;
> @@ -406,10 +421,6 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>         if (flags & SPI_XFER_BEGIN)
>                 external_cs_manage(dev, false);
>
> -       cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) |
> -               (priv->mode << SPI_MODE_OFFSET) |
> -               (priv->tmode << SPI_TMOD_OFFSET);
> -
>         if (rx && tx)
>                 priv->tmode = SPI_TMOD_TR;
>         else if (rx)
> @@ -421,8 +432,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>                  */
>                 priv->tmode = SPI_TMOD_TR;
>
> -       cr0 &= ~SPI_TMOD_MASK;
> -       cr0 |= (priv->tmode << SPI_TMOD_OFFSET);
> +       cr0 = GEN_CTRL0(priv, plat);
>
>         priv->len = bitlen >> 3;
>         debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len);
> @@ -476,7 +486,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>
>  static int dw_spi_set_speed(struct udevice *bus, uint speed)
>  {
> -       struct dw_spi_platdata *plat = bus->platdata;
> +       struct dw_spi_platdata *plat = dev_get_platdata(bus);
>         struct dw_spi_priv *priv = dev_get_priv(bus);
>         u16 clk_div;
>
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9
  2020-02-28 21:05 ` [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9 Sean Anderson
@ 2020-03-04  6:20   ` Rick Chen
  0 siblings, 0 replies; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:20 UTC (permalink / raw)
  To: u-boot

Hi Sean

> Some older processors (notably the Kendryte K210) use an older version of
> the RISC-V privileged specification. The primary changes between the old
> and new are in virtual memory, and in the merging of three separate counter
> enable CSRs.  Using the new CSR on an old processor causes an illegal
> instruction exception.  This patch adds an option to use the old CSRs
> instead of the new one.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>

Please check patch and fix the trailing.

WARNING: Block comments use * on subsequent lines
#220: FILE: arch/riscv/include/asm/csr.h:41:
+#define SR_VM_MODE_BBID        _AC(0x02000000, UL) /* Separate instruction and
+                                              data base-and-bound */

WARNING: Block comments use a trailing */ on a separate line
#220: FILE: arch/riscv/include/asm/csr.h:41:
+                                              data base-and-bound */

total: 0 errors, 2 warnings, 0 checks, 118 lines checked

Thanks,
Rick

> Changes in v5:
> - Rename to 1.9 to reflect the spec as implemented by the k210
>
> Changes in v4:
> - Fixed CSRs not being defined properly (thanks bmeng)
> - Added ifdefs for all changed CSRs (e.g. for VM)
> - Also properly disable VM on boot
>
> Changes in v3:
> - Renamed from "riscv: Add option to disable writes to mcounteren"
> - Added original functionality back for older priv specs.
>
> Changes in v2:
> - Moved forward in the patch series
>
>  arch/riscv/Kconfig           | 10 +++++++++
>  arch/riscv/cpu/cpu.c         |  9 ++++++++
>  arch/riscv/include/asm/csr.h | 40 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 59 insertions(+)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 3338b788f8..b7a5757584 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -225,6 +225,16 @@ config XIP
>  config SHOW_REGS
>         bool "Show registers on unhandled exception"
>
> +config RISCV_PRIV_1_9
> +       bool "Use version 1.9 of the RISC-V priviledged specification"
> +       help
> +         Older versions of the RISC-V priviledged specification had
> +         separate counter enable CSRs for each privilege mode. Writing
> +         to the unified mcounteren CSR on a processor implementing the
> +         old specification will result in an illegal instruction
> +         exception. In addition to counter CSR changes, the way virtual
> +         memory is configured was also changed.
> +
>  config STACK_SIZE_SHIFT
>         int
>         default 14
> diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> index a971ec8694..263fccc675 100644
> --- a/arch/riscv/cpu/cpu.c
> +++ b/arch/riscv/cpu/cpu.c
> @@ -89,11 +89,20 @@ int arch_cpu_init_dm(void)
>                  * Enable perf counters for cycle, time,
>                  * and instret counters only
>                  */
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +               csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0));
> +               csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0));
> +#else
>                 csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
> +#endif
>
>                 /* Disable paging */
>                 if (supports_extension('s'))
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +                       csr_read_clear(CSR_MSTATUS, SR_VM);
> +#else
>                         csr_write(CSR_SATP, 0);
> +#endif
>         }
>
>  #ifdef CONFIG_SMP
> diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
> index d1520743a2..12b6146b97 100644
> --- a/arch/riscv/include/asm/csr.h
> +++ b/arch/riscv/include/asm/csr.h
> @@ -15,7 +15,11 @@
>  #define SR_SIE         _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
>  #define SR_SPIE                _AC(0x00000020, UL) /* Previous Supervisor IE */
>  #define SR_SPP         _AC(0x00000100, UL) /* Previously Supervisor */
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +#define SR_PUM         _AC(0x00040000, UL) /* Protect User Memory Access */
> +#else
>  #define SR_SUM         _AC(0x00040000, UL) /* Supervisor User Memory Access */
> +#endif
>
>  #define SR_FS          _AC(0x00006000, UL) /* Floating-point Status */
>  #define SR_FS_OFF      _AC(0x00000000, UL)
> @@ -29,6 +33,22 @@
>  #define SR_XS_CLEAN    _AC(0x00010000, UL)
>  #define SR_XS_DIRTY    _AC(0x00018000, UL)
>
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +#define SR_VM          _AC(0x1F000000, UL) /* Virtualization Management */
> +#define SR_VM_MODE_BARE        _AC(0x00000000, UL) /* No translation or protection */
> +#define SR_VM_MODE_BB  _AC(0x01000000, UL) /* Single base-and-bound */
> +#define SR_VM_MODE_BBID        _AC(0x02000000, UL) /* Separate instruction and
> +                                              data base-and-bound */
> +#ifndef CONFIG_64BIT
> +#define SR_VM_MODE_32  _AC(0x08000000, UL)
> +#define SR_VM_MODE     SR_VM_MODE_32
> +#else
> +#define SR_VM_MODE_39  _AC(0x09000000, UL)
> +#define SR_VM_MODE_48  _AC(0x0A000000, UL)
> +#define SR_VM_MODE     SR_VM_MODE_39
> +#endif
> +#endif
> +
>  #ifndef CONFIG_64BIT
>  #define SR_SD          _AC(0x80000000, UL) /* FS/XS dirty */
>  #else
> @@ -36,6 +56,7 @@
>  #endif
>
>  /* SATP flags */
> +#ifndef CONFIG_RISCV_PRIV_1_9
>  #ifndef CONFIG_64BIT
>  #define SATP_PPN       _AC(0x003FFFFF, UL)
>  #define SATP_MODE_32   _AC(0x80000000, UL)
> @@ -45,6 +66,7 @@
>  #define SATP_MODE_39   _AC(0x8000000000000000, UL)
>  #define SATP_MODE      SATP_MODE_39
>  #endif
> +#endif
>
>  /* SCAUSE */
>  #define SCAUSE_IRQ_FLAG                (_AC(1, UL) << (__riscv_xlen - 1))
> @@ -88,17 +110,35 @@
>  #define CSR_SCAUSE             0x142
>  #define CSR_STVAL              0x143
>  #define CSR_SIP                        0x144
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +#define CSR_SPTBR              0x180
> +#else
>  #define CSR_SATP               0x180
> +#endif
>  #define CSR_MSTATUS            0x300
>  #define CSR_MISA               0x301
>  #define CSR_MIE                        0x304
>  #define CSR_MTVEC              0x305
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +#define CSR_MUCOUNTEREN         0x320
> +#define CSR_MSCOUNTEREN         0x321
> +#define CSR_MHCOUNTEREN         0x322
> +#else
>  #define CSR_MCOUNTEREN         0x306
> +#endif
>  #define CSR_MSCRATCH           0x340
>  #define CSR_MEPC               0x341
>  #define CSR_MCAUSE             0x342
>  #define CSR_MTVAL              0x343
>  #define CSR_MIP                        0x344
> +#ifdef CONFIG_RISCV_PRIV_1_9
> +#define CSR_MBASE              0x380
> +#define CSR_MBOUND             0x381
> +#define CSR_MIBASE             0x382
> +#define CSR_MIBOUND            0x383
> +#define CSR_MDBASE             0x384
> +#define CSR_MDBOUND            0x385
> +#endif
>  #define CSR_CYCLEH             0xc80
>  #define CSR_TIMEH              0xc81
>  #define CSR_INSTRETH           0xc82
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA
  2020-02-28 21:05 ` [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA Sean Anderson
@ 2020-03-04  6:47   ` Rick Chen
  2020-03-04 15:00     ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:47 UTC (permalink / raw)
  To: u-boot

Hi Sean

> The Fully-Programmable Input/Output Array (FPIOA) device controls pin
> multiplexing on the K210. The FPIOA can remap any supported function to any
> multifunctional IO pin. It can also perform basic GPIO functions, such as
> reading the current value of a pin.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
> Changes in v5:
> - New
>
>  MAINTAINERS                                   |   2 +
>  .../pinctrl/kendryte,k210-fpioa.txt           | 116 +++
>  drivers/pinctrl/Kconfig                       |   1 +
>  drivers/pinctrl/Makefile                      |   1 +
>  drivers/pinctrl/kendryte/Kconfig              |   7 +
>  drivers/pinctrl/kendryte/Makefile             |   1 +
>  drivers/pinctrl/kendryte/pinctrl.c            | 663 ++++++++++++++++++
>  drivers/pinctrl/kendryte/pinctrl.h            | 325 +++++++++
>  include/dt-bindings/pinctrl/k210-pinctrl.h    |  12 +
>  9 files changed, 1128 insertions(+)
>  create mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
>  create mode 100644 drivers/pinctrl/kendryte/Kconfig
>  create mode 100644 drivers/pinctrl/kendryte/Makefile
>  create mode 100644 drivers/pinctrl/kendryte/pinctrl.c
>  create mode 100644 drivers/pinctrl/kendryte/pinctrl.h
>  create mode 100644 include/dt-bindings/pinctrl/k210-pinctrl.h
>

Please checkpatch and fix

total: 3 errors, 13 warnings, 5 checks, 1147 lines checked

Thanks
Rick


> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8e9e0569ba..8725bcf9af 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -809,7 +809,9 @@ RISC-V KENDRYTE
>  M:     Sean Anderson <seanga2@gmail.com>
>  S:     Maintained
>  F:     doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
> +F:     doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
>  F:     drivers/clk/kendryte/
> +F:     drivers/pinctrl/kendryte/
>  F:     include/kendryte/
>
>  RNG
> diff --git a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
> new file mode 100644
> index 0000000000..299ce65304
> --- /dev/null
> +++ b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
> @@ -0,0 +1,116 @@
> +Kendryte K210 FPIOA
> +
> +This binding describes the Fully-Programmable Input/Output Array found on
> +Kendryte K210 SoCs. Any of the 256 functions can be mapped to any of the 48
> +pins.
> +
> +Required properties:
> +- compatible: should be "kendryte,k210-fpioa"
> +- reg: address and length of the FPIOA registers
> +- kendryte,sysctl: phandle to the "sysctl" register map node
> +- kendryte,power-offset: offset in the register map of the power bank control
> +  register (in bytes)
> +
> +Configuration nodes
> +
> +Pin configuration nodes are documented in pinctrl-bindings.txt
> +
> +Valid values for pins, groups, and function names:
> +
> +pins:
> +       "IO_X", where X is a number from 0 to 47.
> +
> +groups:
> +       A0, A1, A2, B0, B1, B2, C0, C1
> +
> +functions:
> +       JTAG_TCLK, JTAG_TDI, JTAG_TMS, JTAG_TDO, SPI0_D0, SPI0_D1, SPI0_D2,
> +       SPI0_D3, SPI0_D4, SPI0_D5, SPI0_D6, SPI0_D7, SPI0_SS0, SPI0_SS1,
> +       SPI0_SS2, SPI0_SS3, SPI0_ARB, SPI0_SCLK, UARTHS_RX, UARTHS_TX, RESV6,
> +       RESV7, CLK_SPI1, CLK_I2C1, GPIOHS0, GPIOHS1, GPIOHS2, GPIOHS3, GPIOHS4,
> +       GPIOHS5, GPIOHS6, GPIOHS7, GPIOHS8, GPIOHS9, GPIOHS10, GPIOHS11,
> +       GPIOHS12, GPIOHS13, GPIOHS14, GPIOHS15, GPIOHS16, GPIOHS17, GPIOHS18,
> +       GPIOHS19, GPIOHS20, GPIOHS21, GPIOHS22, GPIOHS23, GPIOHS24, GPIOHS25,
> +       GPIOHS26, GPIOHS27, GPIOHS28, GPIOHS29, GPIOHS30, GPIOHS31, GPIO0,
> +       GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, UART1_RX, UART1_TX,
> +       UART2_RX, UART2_TX, UART3_RX, UART3_TX, SPI1_D0, SPI1_D1, SPI1_D2,
> +       SPI1_D3, SPI1_D4, SPI1_D5, SPI1_D6, SPI1_D7, SPI1_SS0, SPI1_SS1,
> +       SPI1_SS2, SPI1_SS3, SPI1_ARB, SPI1_SCLK, SPI2_D0, SPI2_SS, SPI2_SCLK,
> +       I2S0_MCLK, I2S0_SCLK, I2S0_WS, I2S0_IN_D0, I2S0_IN_D1, I2S0_IN_D2,
> +       I2S0_IN_D3, I2S0_OUT_D0, I2S0_OUT_D1, I2S0_OUT_D2, I2S0_OUT_D3,
> +       I2S1_MCLK, I2S1_SCLK, I2S1_WS, I2S1_IN_D0, I2S1_IN_D1, I2S1_IN_D2,
> +       I2S1_IN_D3, I2S1_OUT_D0, I2S1_OUT_D1, I2S1_OUT_D2, I2S1_OUT_D3,
> +       I2S2_MCLK, I2S2_SCLK, I2S2_WS, I2S2_IN_D0, I2S2_IN_D1, I2S2_IN_D2,
> +       I2S2_IN_D3, I2S2_OUT_D0, I2S2_OUT_D1, I2S2_OUT_D2, I2S2_OUT_D3, RESV0,
> +       RESV1, RESV2, RESV3, RESV4, RESV5, I2C0_SCLK, I2C0_SDA, I2C1_SCLK,
> +       I2C1_SDA, I2C2_SCLK, I2C2_SDA, DVP_XCLK, DVP_RST, DVP_PWDN, DVP_VSYNC,
> +       DVP_HREF, DVP_PCLK, DVP_D0, DVP_D1, DVP_D2, DVP_D3, DVP_D4, DVP_D5,
> +       DVP_D6, DVP_D7, SCCB_SCLK, SCCB_SDA, UART1_CTS, UART1_DSR, UART1_DCD,
> +       UART1_RI, UART1_SIR_IN, UART1_DTR, UART1_RTS, UART1_OUT2, UART1_OUT1,
> +       UART1_SIR_OUT, UART1_BAUD, UART1_RE, UART1_DE, UART1_RS485_EN,
> +       UART2_CTS, UART2_DSR, UART2_DCD, UART2_RI, UART2_SIR_IN, UART2_DTR,
> +       UART2_RTS, UART2_OUT2, UART2_OUT1, UART2_SIR_OUT, UART2_BAUD, UART2_RE,
> +       UART2_DE, UART2_RS485_EN, UART3_CTS, UART3_DSR, UART3_DCD, UART3_RI,
> +       UART3_SIR_IN, UART3_DTR, UART3_RTS, UART3_OUT2, UART3_OUT1,
> +       UART3_SIR_OUT, UART3_BAUD, UART3_RE, UART3_DE, UART3_RS485_EN,
> +       TIMER0_TOGGLE1, TIMER0_TOGGLE2, TIMER0_TOGGLE3, TIMER0_TOGGLE4,
> +       TIMER1_TOGGLE1, TIMER1_TOGGLE2, TIMER1_TOGGLE3, TIMER1_TOGGLE4,
> +       TIMER2_TOGGLE1, TIMER2_TOGGLE2, TIMER2_TOGGLE3, TIMER2_TOGGLE4,
> +       CLK_SPI2, CLK_I2C2, INTERNAL0, INTERNAL1, INTERNAL2, INTERNAL3,
> +       INTERNAL4, INTERNAL5, INTERNAL6, INTERNAL7, INTERNAL8, INTERNAL9,
> +       INTERNAL10, INTERNAL11, INTERNAL12, INTERNAL13, INTERNAL14, INTERNAL15,
> +       INTERNAL16, INTERNAL17, CONSTANT, INTERNAL18, DEBUG0, DEBUG1, DEBUG2,
> +       DEBUG3, DEBUG4, DEBUG5, DEBUG6, DEBUG7, DEBUG8, DEBUG9, DEBUG10,
> +       DEBUG11, DEBUG12, DEBUG13, DEBUG14, DEBUG15, DEBUG16, DEBUG17, DEBUG18,
> +       DEBUG19, DEBUG20, DEBUG21, DEBUG22, DEBUG23, DEBUG24, DEBUG25, DEBUG26,
> +       DEBUG27, DEBUG28, DEBUG29, DEBUG30, DEBUG31
> +
> +Valid properties include:
> +       bias-disable, bias-pull-down, bias-pull-up, drive-strength,
> +       drive-strength-ua, input-enable, input-disable, input-schmitt-enable,
> +       input-schmitt-disable, power-source, output-low, output-high,
> +       output-enable, output-disable, slew-rate
> +
> +Notes on specific properties include:
> +- bias-pull-up, -down, and -pin-default: The pull strength cannot be configured.
> +- drive-strength: There are 8 drive strength settings between 11 and 50 mA
> +- power-source: Controls the output voltage of a bank of pins. Either
> +  K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. This property cannot
> +  be specified for individual pins.
> +- slew-rate: Specifying this property reduces the slew rate
> +
> +Example:
> +fpioa: pinmux at 502B0000 {
> +       compatible = "kendryte,k210-fpioa";
> +       reg = <0x502B0000 0x100>;
> +       kendryte,sysctl = <&sysctl>;
> +       kendryte,power-offset = <K210_SYSCTL_POWER_SEL>;
> +
> +       fpioa_jtag: jtag {
> +               voltage {
> +                       group = "A0";
> +                       power-source = <K210_PC_POWER_3V3>;
> +               };
> +               tck {
> +                       function = "JTAG_TCLK";
> +                       pins = "IO_0";
> +               };
> +               tdi {
> +                       function = "JTAG_TDI";
> +                       pins = "IO_1";
> +               };
> +               tms {
> +                       function = "JTAG_TMS";
> +                       pins = "IO_2";
> +               };
> +               tdo {
> +                       function = "JTAG_TDO";
> +                       pins = "IO_3";
> +               };
> +       };
> +
> +       fpioa_isp: isp {
> +               function = "GPIOHS0";
> +               pins = "IO_16";
> +       };
> +};
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 83e39b9de3..9ec56c7735 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -289,6 +289,7 @@ endif
>  source "drivers/pinctrl/broadcom/Kconfig"
>  source "drivers/pinctrl/exynos/Kconfig"
>  source "drivers/pinctrl/intel/Kconfig"
> +source "drivers/pinctrl/kendryte/Kconfig"
>  source "drivers/pinctrl/mediatek/Kconfig"
>  source "drivers/pinctrl/meson/Kconfig"
>  source "drivers/pinctrl/mscc/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 4f662c4f6d..1fe66fed3c 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
>  obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
>  obj-$(CONFIG_PINCTRL_PIC32)    += pinctrl_pic32.o
>  obj-$(CONFIG_PINCTRL_EXYNOS)   += exynos/
> +obj-$(CONFIG_PINCTRL_K210)     += kendryte/
>  obj-$(CONFIG_PINCTRL_MESON)    += meson/
>  obj-$(CONFIG_PINCTRL_MTK)      += mediatek/
>  obj-$(CONFIG_PINCTRL_MSCC)     += mscc/
> diff --git a/drivers/pinctrl/kendryte/Kconfig b/drivers/pinctrl/kendryte/Kconfig
> new file mode 100644
> index 0000000000..6fc10e9516
> --- /dev/null
> +++ b/drivers/pinctrl/kendryte/Kconfig
> @@ -0,0 +1,7 @@
> +config PINCTRL_K210
> +       bool "Kendryte K210 Fully-Programmable Input/Output Array driver"
> +       depends on DM && PINCTRL_GENERIC
> +       help
> +         Support pin multiplexing on the K210. The "FPIOA" can remap any
> +         supported function to any multifunctional IO pin. It can also perform
> +         basic GPIO functions, such as reading the current value of a pin.
> diff --git a/drivers/pinctrl/kendryte/Makefile b/drivers/pinctrl/kendryte/Makefile
> new file mode 100644
> index 0000000000..1c10d7a929
> --- /dev/null
> +++ b/drivers/pinctrl/kendryte/Makefile
> @@ -0,0 +1 @@
> +obj-y += pinctrl.o
> diff --git a/drivers/pinctrl/kendryte/pinctrl.c b/drivers/pinctrl/kendryte/pinctrl.c
> new file mode 100644
> index 0000000000..3f7de0e706
> --- /dev/null
> +++ b/drivers/pinctrl/kendryte/pinctrl.c
> @@ -0,0 +1,663 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dm/pinctrl.h>
> +#include <dt-bindings/pinctrl/k210-pinctrl.h>
> +#include <mapmem.h>
> +#include <regmap.h>
> +#include <syscon.h>
> +#include <asm/io.h>
> +#include <linux/err.h>
> +#include "pinctrl.h"
> +
> +struct k210_pc_priv {
> +       struct clk clk;
> +       struct k210_fpioa __iomem *fpioa; /* FPIOA register */
> +       struct regmap *sysctl; /* Sysctl regmap */
> +       u32 power_offset; /* Power bank register offset */
> +};
> +
> +#define PIN(i) [i] = "IO_" #i
> +static const char k210_pc_pin_names[][6] = {
> +       PIN(0),
> +       PIN(1),
> +       PIN(2),
> +       PIN(3),
> +       PIN(4),
> +       PIN(5),
> +       PIN(6),
> +       PIN(7),
> +       PIN(8),
> +       PIN(9),
> +       PIN(10),
> +       PIN(11),
> +       PIN(12),
> +       PIN(13),
> +       PIN(14),
> +       PIN(15),
> +       PIN(16),
> +       PIN(17),
> +       PIN(18),
> +       PIN(19),
> +       PIN(20),
> +       PIN(21),
> +       PIN(22),
> +       PIN(23),
> +       PIN(24),
> +       PIN(25),
> +       PIN(26),
> +       PIN(27),
> +       PIN(28),
> +       PIN(29),
> +       PIN(30),
> +       PIN(31),
> +       PIN(32),
> +       PIN(33),
> +       PIN(34),
> +       PIN(35),
> +       PIN(36),
> +       PIN(37),
> +       PIN(38),
> +       PIN(39),
> +       PIN(40),
> +       PIN(41),
> +       PIN(42),
> +       PIN(43),
> +       PIN(44),
> +       PIN(45),
> +       PIN(46),
> +       PIN(47),
> +};
> +#undef PIN
> +
> +static int k210_pc_get_pins_count(struct udevice *dev)
> +{
> +       return ARRAY_SIZE(k210_pc_pin_names);
> +};
> +
> +static const char *k210_pc_get_pin_name(struct udevice *dev, unsigned selector)
> +{
> +       return k210_pc_pin_names[selector];
> +}
> +
> +/* These are just power domains */
> +static const char k210_pc_group_names[][3] = {
> +       [0] = "A0",
> +       [1] = "A1",
> +       [2] = "A2",
> +       [3] = "B0",
> +       [4] = "B1",
> +       [5] = "B2",
> +       [6] = "C0",
> +       [7] = "C1",
> +};
> +
> +static int k210_pc_get_groups_count(struct udevice *dev)
> +{
> +       return ARRAY_SIZE(k210_pc_group_names);
> +}
> +
> +static const char *k210_pc_get_group_name(struct udevice *dev,
> +                                         unsigned selector)
> +{
> +       return k210_pc_group_names[selector];
> +}
> +
> +enum k210_pc_mode_id {
> +       K210_PC_DEFAULT_DISABLED,
> +       K210_PC_DEFAULT_IN,
> +       K210_PC_DEFAULT_IN_TIE,
> +       K210_PC_DEFAULT_OUT,
> +       K210_PC_DEFAULT_I2C,
> +       K210_PC_DEFAULT_SPI,
> +       K210_PC_DEFAULT_GPIO,
> +       K210_PC_DEFAULT_INT13,
> +};
> +
> +#define DEFAULT(mode) [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode
> +static const u32 k210_pc_mode_id_to_mode[] = {
> +       [K210_PC_DEFAULT_DISABLED] = 0,
> +       DEFAULT(IN),
> +       [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN,
> +       DEFAULT(OUT),
> +       DEFAULT(I2C),
> +       DEFAULT(SPI),
> +       DEFAULT(GPIO),
> +       [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU,
> +};
> +#undef DEFAULT
> +
> +/* This saves around 2K vs having a pointer+mode */
> +struct k210_pcf_info {
> +       char name[15];
> +       u8 mode_id;
> +};
> +
> +#define FUNC(id, mode) [K210_PCF_##id] = { \
> +       .name = #id, \
> +       .mode_id = K210_PC_DEFAULT_##mode \
> +}
> +static const struct k210_pcf_info k210_pcf_infos[] = {
> +       FUNC(JTAG_TCLK,      IN),
> +       FUNC(JTAG_TDI,       IN),
> +       FUNC(JTAG_TMS,       IN),
> +       FUNC(JTAG_TDO,       OUT),
> +       FUNC(SPI0_D0,        SPI),
> +       FUNC(SPI0_D1,        SPI),
> +       FUNC(SPI0_D2,        SPI),
> +       FUNC(SPI0_D3,        SPI),
> +       FUNC(SPI0_D4,        SPI),
> +       FUNC(SPI0_D5,        SPI),
> +       FUNC(SPI0_D6,        SPI),
> +       FUNC(SPI0_D7,        SPI),
> +       FUNC(SPI0_SS0,       OUT),
> +       FUNC(SPI0_SS1,       OUT),
> +       FUNC(SPI0_SS2,       OUT),
> +       FUNC(SPI0_SS3,       OUT),
> +       FUNC(SPI0_ARB,       IN_TIE),
> +       FUNC(SPI0_SCLK,      OUT),
> +       FUNC(UARTHS_RX,      IN),
> +       FUNC(UARTHS_TX,      OUT),
> +       FUNC(RESV6,          IN),
> +       FUNC(RESV7,          IN),
> +       FUNC(CLK_SPI1,       OUT),
> +       FUNC(CLK_I2C1,       OUT),
> +       FUNC(GPIOHS0,        GPIO),
> +       FUNC(GPIOHS1,        GPIO),
> +       FUNC(GPIOHS2,        GPIO),
> +       FUNC(GPIOHS3,        GPIO),
> +       FUNC(GPIOHS4,        GPIO),
> +       FUNC(GPIOHS5,        GPIO),
> +       FUNC(GPIOHS6,        GPIO),
> +       FUNC(GPIOHS7,        GPIO),
> +       FUNC(GPIOHS8,        GPIO),
> +       FUNC(GPIOHS9,        GPIO),
> +       FUNC(GPIOHS10,       GPIO),
> +       FUNC(GPIOHS11,       GPIO),
> +       FUNC(GPIOHS12,       GPIO),
> +       FUNC(GPIOHS13,       GPIO),
> +       FUNC(GPIOHS14,       GPIO),
> +       FUNC(GPIOHS15,       GPIO),
> +       FUNC(GPIOHS16,       GPIO),
> +       FUNC(GPIOHS17,       GPIO),
> +       FUNC(GPIOHS18,       GPIO),
> +       FUNC(GPIOHS19,       GPIO),
> +       FUNC(GPIOHS20,       GPIO),
> +       FUNC(GPIOHS21,       GPIO),
> +       FUNC(GPIOHS22,       GPIO),
> +       FUNC(GPIOHS23,       GPIO),
> +       FUNC(GPIOHS24,       GPIO),
> +       FUNC(GPIOHS25,       GPIO),
> +       FUNC(GPIOHS26,       GPIO),
> +       FUNC(GPIOHS27,       GPIO),
> +       FUNC(GPIOHS28,       GPIO),
> +       FUNC(GPIOHS29,       GPIO),
> +       FUNC(GPIOHS30,       GPIO),
> +       FUNC(GPIOHS31,       GPIO),
> +       FUNC(GPIO0,          GPIO),
> +       FUNC(GPIO1,          GPIO),
> +       FUNC(GPIO2,          GPIO),
> +       FUNC(GPIO3,          GPIO),
> +       FUNC(GPIO4,          GPIO),
> +       FUNC(GPIO5,          GPIO),
> +       FUNC(GPIO6,          GPIO),
> +       FUNC(GPIO7,          GPIO),
> +       FUNC(UART1_RX,       IN),
> +       FUNC(UART1_TX,       OUT),
> +       FUNC(UART2_RX,       IN),
> +       FUNC(UART2_TX,       OUT),
> +       FUNC(UART3_RX,       IN),
> +       FUNC(UART3_TX,       OUT),
> +       FUNC(SPI1_D0,        SPI),
> +       FUNC(SPI1_D1,        SPI),
> +       FUNC(SPI1_D2,        SPI),
> +       FUNC(SPI1_D3,        SPI),
> +       FUNC(SPI1_D4,        SPI),
> +       FUNC(SPI1_D5,        SPI),
> +       FUNC(SPI1_D6,        SPI),
> +       FUNC(SPI1_D7,        SPI),
> +       FUNC(SPI1_SS0,       OUT),
> +       FUNC(SPI1_SS1,       OUT),
> +       FUNC(SPI1_SS2,       OUT),
> +       FUNC(SPI1_SS3,       OUT),
> +       FUNC(SPI1_ARB,       IN_TIE),
> +       FUNC(SPI1_SCLK,      OUT),
> +       FUNC(SPI2_D0,        SPI),
> +       FUNC(SPI2_SS,        IN),
> +       FUNC(SPI2_SCLK,      IN),
> +       FUNC(I2S0_MCLK,      OUT),
> +       FUNC(I2S0_SCLK,      OUT),
> +       FUNC(I2S0_WS,        OUT),
> +       FUNC(I2S0_IN_D0,     IN),
> +       FUNC(I2S0_IN_D1,     IN),
> +       FUNC(I2S0_IN_D2,     IN),
> +       FUNC(I2S0_IN_D3,     IN),
> +       FUNC(I2S0_OUT_D0,    OUT),
> +       FUNC(I2S0_OUT_D1,    OUT),
> +       FUNC(I2S0_OUT_D2,    OUT),
> +       FUNC(I2S0_OUT_D3,    OUT),
> +       FUNC(I2S1_MCLK,      OUT),
> +       FUNC(I2S1_SCLK,      OUT),
> +       FUNC(I2S1_WS,        OUT),
> +       FUNC(I2S1_IN_D0,     IN),
> +       FUNC(I2S1_IN_D1,     IN),
> +       FUNC(I2S1_IN_D2,     IN),
> +       FUNC(I2S1_IN_D3,     IN),
> +       FUNC(I2S1_OUT_D0,    OUT),
> +       FUNC(I2S1_OUT_D1,    OUT),
> +       FUNC(I2S1_OUT_D2,    OUT),
> +       FUNC(I2S1_OUT_D3,    OUT),
> +       FUNC(I2S2_MCLK,      OUT),
> +       FUNC(I2S2_SCLK,      OUT),
> +       FUNC(I2S2_WS,        OUT),
> +       FUNC(I2S2_IN_D0,     IN),
> +       FUNC(I2S2_IN_D1,     IN),
> +       FUNC(I2S2_IN_D2,     IN),
> +       FUNC(I2S2_IN_D3,     IN),
> +       FUNC(I2S2_OUT_D0,    OUT),
> +       FUNC(I2S2_OUT_D1,    OUT),
> +       FUNC(I2S2_OUT_D2,    OUT),
> +       FUNC(I2S2_OUT_D3,    OUT),
> +       FUNC(RESV0,          DISABLED),
> +       FUNC(RESV1,          DISABLED),
> +       FUNC(RESV2,          DISABLED),
> +       FUNC(RESV3,          DISABLED),
> +       FUNC(RESV4,          DISABLED),
> +       FUNC(RESV5,          DISABLED),
> +       FUNC(I2C0_SCLK,      I2C),
> +       FUNC(I2C0_SDA,       I2C),
> +       FUNC(I2C1_SCLK,      I2C),
> +       FUNC(I2C1_SDA,       I2C),
> +       FUNC(I2C2_SCLK,      I2C),
> +       FUNC(I2C2_SDA,       I2C),
> +       FUNC(DVP_XCLK,       OUT),
> +       FUNC(DVP_RST,        OUT),
> +       FUNC(DVP_PWDN,       OUT),
> +       FUNC(DVP_VSYNC,      IN),
> +       FUNC(DVP_HREF,       IN),
> +       FUNC(DVP_PCLK,       IN),
> +       FUNC(DVP_D0,         IN),
> +       FUNC(DVP_D1,         IN),
> +       FUNC(DVP_D2,         IN),
> +       FUNC(DVP_D3,         IN),
> +       FUNC(DVP_D4,         IN),
> +       FUNC(DVP_D5,         IN),
> +       FUNC(DVP_D6,         IN),
> +       FUNC(DVP_D7,         IN),
> +       FUNC(SCCB_SCLK,      I2C),
> +       FUNC(SCCB_SDA,       I2C),
> +       FUNC(UART1_CTS,      IN),
> +       FUNC(UART1_DSR,      IN),
> +       FUNC(UART1_DCD,      IN),
> +       FUNC(UART1_RI,       IN),
> +       FUNC(UART1_SIR_IN,   IN),
> +       FUNC(UART1_DTR,      OUT),
> +       FUNC(UART1_RTS,      OUT),
> +       FUNC(UART1_OUT2,     OUT),
> +       FUNC(UART1_OUT1,     OUT),
> +       FUNC(UART1_SIR_OUT,  OUT),
> +       FUNC(UART1_BAUD,     OUT),
> +       FUNC(UART1_RE,       OUT),
> +       FUNC(UART1_DE,       OUT),
> +       FUNC(UART1_RS485_EN, OUT),
> +       FUNC(UART2_CTS,      IN),
> +       FUNC(UART2_DSR,      IN),
> +       FUNC(UART2_DCD,      IN),
> +       FUNC(UART2_RI,       IN),
> +       FUNC(UART2_SIR_IN,   IN),
> +       FUNC(UART2_DTR,      OUT),
> +       FUNC(UART2_RTS,      OUT),
> +       FUNC(UART2_OUT2,     OUT),
> +       FUNC(UART2_OUT1,     OUT),
> +       FUNC(UART2_SIR_OUT,  OUT),
> +       FUNC(UART2_BAUD,     OUT),
> +       FUNC(UART2_RE,       OUT),
> +       FUNC(UART2_DE,       OUT),
> +       FUNC(UART2_RS485_EN, OUT),
> +       FUNC(UART3_CTS,      IN),
> +       FUNC(UART3_DSR,      IN),
> +       FUNC(UART3_DCD,      IN),
> +       FUNC(UART3_RI,       IN),
> +       FUNC(UART3_SIR_IN,   IN),
> +       FUNC(UART3_DTR,      OUT),
> +       FUNC(UART3_RTS,      OUT),
> +       FUNC(UART3_OUT2,     OUT),
> +       FUNC(UART3_OUT1,     OUT),
> +       FUNC(UART3_SIR_OUT,  OUT),
> +       FUNC(UART3_BAUD,     OUT),
> +       FUNC(UART3_RE,       OUT),
> +       FUNC(UART3_DE,       OUT),
> +       FUNC(UART3_RS485_EN, OUT),
> +       FUNC(TIMER0_TOGGLE1, OUT),
> +       FUNC(TIMER0_TOGGLE2, OUT),
> +       FUNC(TIMER0_TOGGLE3, OUT),
> +       FUNC(TIMER0_TOGGLE4, OUT),
> +       FUNC(TIMER1_TOGGLE1, OUT),
> +       FUNC(TIMER1_TOGGLE2, OUT),
> +       FUNC(TIMER1_TOGGLE3, OUT),
> +       FUNC(TIMER1_TOGGLE4, OUT),
> +       FUNC(TIMER2_TOGGLE1, OUT),
> +       FUNC(TIMER2_TOGGLE2, OUT),
> +       FUNC(TIMER2_TOGGLE3, OUT),
> +       FUNC(TIMER2_TOGGLE4, OUT),
> +       FUNC(CLK_SPI2,       OUT),
> +       FUNC(CLK_I2C2,       OUT),
> +       FUNC(INTERNAL0,      OUT),
> +       FUNC(INTERNAL1,      OUT),
> +       FUNC(INTERNAL2,      OUT),
> +       FUNC(INTERNAL3,      OUT),
> +       FUNC(INTERNAL4,      OUT),
> +       FUNC(INTERNAL5,      OUT),
> +       FUNC(INTERNAL6,      OUT),
> +       FUNC(INTERNAL7,      OUT),
> +       FUNC(INTERNAL8,      OUT),
> +       FUNC(INTERNAL9,      IN),
> +       FUNC(INTERNAL10,     IN),
> +       FUNC(INTERNAL11,     IN),
> +       FUNC(INTERNAL12,     IN),
> +       FUNC(INTERNAL13,     INT13),
> +       FUNC(INTERNAL14,     I2C),
> +       FUNC(INTERNAL15,     IN),
> +       FUNC(INTERNAL16,     IN),
> +       FUNC(INTERNAL17,     IN),
> +       FUNC(CONSTANT,       DISABLED),
> +       FUNC(INTERNAL18,     IN),
> +       FUNC(DEBUG0,         OUT),
> +       FUNC(DEBUG1,         OUT),
> +       FUNC(DEBUG2,         OUT),
> +       FUNC(DEBUG3,         OUT),
> +       FUNC(DEBUG4,         OUT),
> +       FUNC(DEBUG5,         OUT),
> +       FUNC(DEBUG6,         OUT),
> +       FUNC(DEBUG7,         OUT),
> +       FUNC(DEBUG8,         OUT),
> +       FUNC(DEBUG9,         OUT),
> +       FUNC(DEBUG10,        OUT),
> +       FUNC(DEBUG11,        OUT),
> +       FUNC(DEBUG12,        OUT),
> +       FUNC(DEBUG13,        OUT),
> +       FUNC(DEBUG14,        OUT),
> +       FUNC(DEBUG15,        OUT),
> +       FUNC(DEBUG16,        OUT),
> +       FUNC(DEBUG17,        OUT),
> +       FUNC(DEBUG18,        OUT),
> +       FUNC(DEBUG19,        OUT),
> +       FUNC(DEBUG20,        OUT),
> +       FUNC(DEBUG21,        OUT),
> +       FUNC(DEBUG22,        OUT),
> +       FUNC(DEBUG23,        OUT),
> +       FUNC(DEBUG24,        OUT),
> +       FUNC(DEBUG25,        OUT),
> +       FUNC(DEBUG26,        OUT),
> +       FUNC(DEBUG27,        OUT),
> +       FUNC(DEBUG28,        OUT),
> +       FUNC(DEBUG29,        OUT),
> +       FUNC(DEBUG30,        OUT),
> +       FUNC(DEBUG31,        OUT),
> +};
> +#undef FUNC
> +
> +int k210_pc_get_functions_count(struct udevice *dev)
> +{
> +       return ARRAY_SIZE(k210_pcf_infos);
> +}
> +
> +static const char *k210_pc_get_function_name(struct udevice *dev,
> +                                            unsigned selector)
> +{
> +       return k210_pcf_infos[selector].name;
> +}
> +
> +static int k210_pc_pinmux_set(struct udevice *dev, unsigned pin_selector,
> +                             unsigned func_selector)
> +{
> +       struct k210_pc_priv *priv = dev_get_priv(dev);
> +       const struct k210_pcf_info *info = &k210_pcf_infos[func_selector];
> +       u32 mode = k210_pc_mode_id_to_mode[info->mode_id];
> +       u32 val = func_selector | mode;
> +
> +       log_debug("setting pin %s to %s with mode %.8x\n",
> +                 k210_pc_pin_names[pin_selector], info->name, mode);
> +       writel(val, &priv->fpioa->pins[pin_selector]);
> +       return 0;
> +}
> +
> +/* Max drive strength in uA */
> +static const int k210_pc_drive_strength[] = {
> +       [0] = 11200,
> +       [1] = 16800,
> +       [2] = 22300,
> +       [3] = 27800,
> +       [4] = 33300,
> +       [5] = 38700,
> +       [6] = 44100,
> +       [7] = 49500,
> +};
> +
> +static int k210_pc_get_drive(unsigned max_strength_ua)
> +{
> +       int i;
> +
> +       for (i = K210_PC_DRIVE_MAX; i; i--)
> +               if (k210_pc_drive_strength[i] < max_strength_ua)
> +                       return i;
> +
> +       return -EINVAL;
> +}
> +
> +static int k210_pc_pinconf_set(struct udevice *dev, unsigned pin_selector,
> +                              unsigned param, unsigned argument)
> +{
> +       struct k210_pc_priv *priv = dev_get_priv(dev);
> +       u32 val = readl(&priv->fpioa->pins[pin_selector]);
> +
> +       switch (param) {
> +       case PIN_CONFIG_BIAS_DISABLE:
> +               val &= ~K210_PC_BIAS_MASK;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               if (argument)
> +                       val |= K210_PC_PD;
> +               else
> +                       return -EINVAL;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               if (argument)
> +                       val |= K210_PC_PD;
> +               else
> +                       return -EINVAL;
> +               break;
> +       case PIN_CONFIG_DRIVE_STRENGTH:
> +               argument *= 1000;
> +       case PIN_CONFIG_DRIVE_STRENGTH_UA: {
> +               int drive = k210_pc_get_drive(argument);
> +
> +               if (IS_ERR_VALUE(drive))
> +                       return drive;
> +               val &= ~K210_PC_DRIVE_MASK;
> +               val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive);
> +               break;
> +       }
> +       case PIN_CONFIG_INPUT_ENABLE:
> +               if (argument)
> +                       val |= K210_PC_IE;
> +               else
> +                       val &= ~K210_PC_IE;
> +               break;
> +       case PIN_CONFIG_INPUT_SCHMITT:
> +               argument = 1;
> +       case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
> +               if (argument)
> +                       val |= K210_PC_ST;
> +               else
> +                       val &= ~K210_PC_ST;
> +               break;
> +       case PIN_CONFIG_OUTPUT:
> +               k210_pc_pinmux_set(dev, pin_selector, K210_PCF_CONSTANT);
> +               val = readl(&priv->fpioa->pins[pin_selector]);
> +               val |= K210_PC_MODE_OUT;
> +
> +               if (!argument)
> +                       val |= K210_PC_DO_INV;
> +               break;
> +       case PIN_CONFIG_OUTPUT_ENABLE:
> +               if (argument)
> +                       val |= K210_PC_OE;
> +               else
> +                       val &= ~K210_PC_OE;
> +               break;
> +       case PIN_CONFIG_SLEW_RATE:
> +               if (argument)
> +                       val |= K210_PC_SL;
> +               else
> +                       val &= ~K210_PC_SL;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       writel(val, &priv->fpioa->pins[pin_selector]);
> +       return 0;
> +}
> +
> +static int k210_pc_pinconf_group_set(struct udevice *dev,
> +                                    unsigned group_selector, unsigned param,
> +                                    unsigned argument)
> +{
> +       struct k210_pc_priv *priv = dev_get_priv(dev);
> +
> +       if (param == PIN_CONFIG_POWER_SOURCE) {
> +               u32 bit = BIT(group_selector);
> +
> +               regmap_update_bits(priv->sysctl, priv->power_offset, bit,
> +                                  argument ? bit : 0);
> +       } else {
> +               int i, ret;
> +
> +               for (i = 0; i < 6; i++) {
> +                       ret = k210_pc_pinconf_set(dev, group_selector * 6 + i,
> +                                                 param, argument);
> +                       if (ret)
> +                               return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int k210_pc_get_pin_muxing(struct udevice *dev, unsigned int selector,
> +                                 char *buf, int size)
> +{
> +       struct k210_pc_priv *priv = dev_get_priv(dev);
> +       u32 val = readl(&priv->fpioa->pins[selector]);
> +       const struct k210_pcf_info *info = &k210_pcf_infos[val & K210_PCF_MASK];
> +
> +       strncpy(buf, info->name, min((size_t)size, sizeof(info->name)));
> +       return 0;
> +}
> +
> +static const struct pinconf_param k210_pc_pinconf_params[] = {
> +       { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
> +       { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
> +       { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
> +       { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, U32_MAX },
> +       { "drive-strength-ua", PIN_CONFIG_DRIVE_STRENGTH_UA, U32_MAX },
> +       { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
> +       { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
> +       { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
> +       { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
> +       { "power-source", PIN_CONFIG_POWER_SOURCE, K210_PC_POWER_1V8 },
> +       { "output-low", PIN_CONFIG_OUTPUT, 0 },
> +       { "output-high", PIN_CONFIG_OUTPUT, 1 },
> +       { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
> +       { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
> +       { "slew-rate", PIN_CONFIG_SLEW_RATE, 1 },
> +};
> +
> +static const struct pinctrl_ops k210_pc_pinctrl_ops = {
> +       .get_pins_count = k210_pc_get_pins_count,
> +       .get_pin_name = k210_pc_get_pin_name,
> +       .get_groups_count = k210_pc_get_groups_count,
> +       .get_group_name = k210_pc_get_group_name,
> +       .get_functions_count = k210_pc_get_functions_count,
> +       .get_function_name = k210_pc_get_function_name,
> +       .pinmux_set = k210_pc_pinmux_set,
> +       .pinconf_num_params = ARRAY_SIZE(k210_pc_pinconf_params),
> +       .pinconf_params = k210_pc_pinconf_params,
> +       .pinconf_set = k210_pc_pinconf_set,
> +       .pinconf_group_set = k210_pc_pinconf_group_set,
> +       .set_state = pinctrl_generic_set_state,
> +       .get_pin_muxing = k210_pc_get_pin_muxing,
> +};
> +
> +static int k210_pc_probe(struct udevice *dev)
> +{
> +       int ret, i, j;
> +       struct k210_pc_priv *priv = dev_get_priv(dev);
> +
> +       priv->fpioa = dev_read_addr_ptr(dev);
> +       if (!priv->fpioa)
> +               return -EINVAL;
> +
> +       ret = clk_get_by_index(dev, 0, &priv->clk);
> +       if (ret)
> +               return ret;
> +
> +       ret = clk_enable(&priv->clk);
> +       if (ret && ret != -ENOSYS && ret != -ENOTSUPP)
> +               goto err;
> +
> +       priv->sysctl = syscon_regmap_lookup_by_phandle(dev, "kendryte,sysctl");
> +       if (IS_ERR(priv->sysctl)) {
> +               ret = -ENODEV;
> +               goto err;
> +       }
> +
> +       ret = dev_read_u32(dev, "kendryte,power-offset", &priv->power_offset);
> +       if (ret)
> +               goto err;
> +
> +       log_debug("fpioa = %p sysctl = %p power offset = %x\n", priv->fpioa,
> +                 (void *)priv->sysctl->ranges[0].start, priv->power_offset);
> +
> +       /* Disable all pins */
> +       for (i = 6; i < ARRAY_SIZE(k210_pc_pin_names); i++)
> +               k210_pc_pinmux_set(dev, i, K210_PCF_CONSTANT);
> +
> +       /* Init input ties */
> +       for (i = 0; i < ARRAY_SIZE(priv->fpioa->tie_en); i++) {
> +               u32 val = 0;
> +
> +               for (j = 0; j < 32; j++)
> +                       if (k210_pcf_infos[i * 32 + j].mode_id ==
> +                           K210_PC_DEFAULT_IN_TIE)
> +                               val |= BIT(j);
> +               writel(val, &priv->fpioa->tie_en[i]);
> +               writel(val, &priv->fpioa->tie_val[i]);
> +       }
> +
> +       return 0;
> +
> +err:
> +       clk_free(&priv->clk);
> +       return ret;
> +}
> +
> +static const struct udevice_id k210_pc_ids[] = {
> +       { .compatible = "kendryte,k210-fpioa" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(pinctrl_k210) = {
> +       .name = "pinctrl_k210",
> +       .id = UCLASS_PINCTRL,
> +       .of_match = k210_pc_ids,
> +       .probe = k210_pc_probe,
> +       .priv_auto_alloc_size = sizeof(struct k210_pc_priv),
> +       .ops = &k210_pc_pinctrl_ops,
> +};
> diff --git a/drivers/pinctrl/kendryte/pinctrl.h b/drivers/pinctrl/kendryte/pinctrl.h
> new file mode 100644
> index 0000000000..de434700c3
> --- /dev/null
> +++ b/drivers/pinctrl/kendryte/pinctrl.h
> @@ -0,0 +1,325 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef K210_PINCTRL_H
> +#define K210_PINCTRL_H
> +
> +#include <linux/bitfield.h>
> +
> +/*
> + * Full list of PinCtrl Functions from
> + * kendryte-standalone-sdk/lib/drivers/include/fpioa.h
> + */
> +#define K210_PCF_SHIFT 0
> +#define K210_PCF_MASK GENMASK(7, 0)
> +#define K210_PCF_JTAG_TCLK      0   /* JTAG Test Clock */
> +#define K210_PCF_JTAG_TDI       1   /* JTAG Test Data In */
> +#define K210_PCF_JTAG_TMS       2   /* JTAG Test Mode Select */
> +#define K210_PCF_JTAG_TDO       3   /* JTAG Test Data Out */
> +#define K210_PCF_SPI0_D0        4   /* SPI0 Data 0 */
> +#define K210_PCF_SPI0_D1        5   /* SPI0 Data 1 */
> +#define K210_PCF_SPI0_D2        6   /* SPI0 Data 2 */
> +#define K210_PCF_SPI0_D3        7   /* SPI0 Data 3 */
> +#define K210_PCF_SPI0_D4        8   /* SPI0 Data 4 */
> +#define K210_PCF_SPI0_D5        9   /* SPI0 Data 5 */
> +#define K210_PCF_SPI0_D6        10  /* SPI0 Data 6 */
> +#define K210_PCF_SPI0_D7        11  /* SPI0 Data 7 */
> +#define K210_PCF_SPI0_SS0       12  /* SPI0 Chip Select 0 */
> +#define K210_PCF_SPI0_SS1       13  /* SPI0 Chip Select 1 */
> +#define K210_PCF_SPI0_SS2       14  /* SPI0 Chip Select 2 */
> +#define K210_PCF_SPI0_SS3       15  /* SPI0 Chip Select 3 */
> +#define K210_PCF_SPI0_ARB       16  /* SPI0 Arbitration */
> +#define K210_PCF_SPI0_SCLK      17  /* SPI0 Serial Clock */
> +#define K210_PCF_UARTHS_RX      18  /* UART High speed Receiver */
> +#define K210_PCF_UARTHS_TX      19  /* UART High speed Transmitter */
> +#define K210_PCF_RESV6          20  /* Reserved function */
> +#define K210_PCF_RESV7          21  /* Reserved function */
> +#define K210_PCF_CLK_SPI1       22  /* Clock SPI1 */
> +#define K210_PCF_CLK_I2C1       23  /* Clock I2C1 */
> +#define K210_PCF_GPIOHS0        24  /* GPIO High speed 0 */
> +#define K210_PCF_GPIOHS1        25  /* GPIO High speed 1 */
> +#define K210_PCF_GPIOHS2        26  /* GPIO High speed 2 */
> +#define K210_PCF_GPIOHS3        27  /* GPIO High speed 3 */
> +#define K210_PCF_GPIOHS4        28  /* GPIO High speed 4 */
> +#define K210_PCF_GPIOHS5        29  /* GPIO High speed 5 */
> +#define K210_PCF_GPIOHS6        30  /* GPIO High speed 6 */
> +#define K210_PCF_GPIOHS7        31  /* GPIO High speed 7 */
> +#define K210_PCF_GPIOHS8        32  /* GPIO High speed 8 */
> +#define K210_PCF_GPIOHS9        33  /* GPIO High speed 9 */
> +#define K210_PCF_GPIOHS10       34  /* GPIO High speed 10 */
> +#define K210_PCF_GPIOHS11       35  /* GPIO High speed 11 */
> +#define K210_PCF_GPIOHS12       36  /* GPIO High speed 12 */
> +#define K210_PCF_GPIOHS13       37  /* GPIO High speed 13 */
> +#define K210_PCF_GPIOHS14       38  /* GPIO High speed 14 */
> +#define K210_PCF_GPIOHS15       39  /* GPIO High speed 15 */
> +#define K210_PCF_GPIOHS16       40  /* GPIO High speed 16 */
> +#define K210_PCF_GPIOHS17       41  /* GPIO High speed 17 */
> +#define K210_PCF_GPIOHS18       42  /* GPIO High speed 18 */
> +#define K210_PCF_GPIOHS19       43  /* GPIO High speed 19 */
> +#define K210_PCF_GPIOHS20       44  /* GPIO High speed 20 */
> +#define K210_PCF_GPIOHS21       45  /* GPIO High speed 21 */
> +#define K210_PCF_GPIOHS22       46  /* GPIO High speed 22 */
> +#define K210_PCF_GPIOHS23       47  /* GPIO High speed 23 */
> +#define K210_PCF_GPIOHS24       48  /* GPIO High speed 24 */
> +#define K210_PCF_GPIOHS25       49  /* GPIO High speed 25 */
> +#define K210_PCF_GPIOHS26       50  /* GPIO High speed 26 */
> +#define K210_PCF_GPIOHS27       51  /* GPIO High speed 27 */
> +#define K210_PCF_GPIOHS28       52  /* GPIO High speed 28 */
> +#define K210_PCF_GPIOHS29       53  /* GPIO High speed 29 */
> +#define K210_PCF_GPIOHS30       54  /* GPIO High speed 30 */
> +#define K210_PCF_GPIOHS31       55  /* GPIO High speed 31 */
> +#define K210_PCF_GPIO0          56  /* GPIO pin 0 */
> +#define K210_PCF_GPIO1          57  /* GPIO pin 1 */
> +#define K210_PCF_GPIO2          58  /* GPIO pin 2 */
> +#define K210_PCF_GPIO3          59  /* GPIO pin 3 */
> +#define K210_PCF_GPIO4          60  /* GPIO pin 4 */
> +#define K210_PCF_GPIO5          61  /* GPIO pin 5 */
> +#define K210_PCF_GPIO6          62  /* GPIO pin 6 */
> +#define K210_PCF_GPIO7          63  /* GPIO pin 7 */
> +#define K210_PCF_UART1_RX       64  /* UART1 Receiver */
> +#define K210_PCF_UART1_TX       65  /* UART1 Transmitter */
> +#define K210_PCF_UART2_RX       66  /* UART2 Receiver */
> +#define K210_PCF_UART2_TX       67  /* UART2 Transmitter */
> +#define K210_PCF_UART3_RX       68  /* UART3 Receiver */
> +#define K210_PCF_UART3_TX       69  /* UART3 Transmitter */
> +#define K210_PCF_SPI1_D0        70  /* SPI1 Data 0 */
> +#define K210_PCF_SPI1_D1        71  /* SPI1 Data 1 */
> +#define K210_PCF_SPI1_D2        72  /* SPI1 Data 2 */
> +#define K210_PCF_SPI1_D3        73  /* SPI1 Data 3 */
> +#define K210_PCF_SPI1_D4        74  /* SPI1 Data 4 */
> +#define K210_PCF_SPI1_D5        75  /* SPI1 Data 5 */
> +#define K210_PCF_SPI1_D6        76  /* SPI1 Data 6 */
> +#define K210_PCF_SPI1_D7        77  /* SPI1 Data 7 */
> +#define K210_PCF_SPI1_SS0       78  /* SPI1 Chip Select 0 */
> +#define K210_PCF_SPI1_SS1       79  /* SPI1 Chip Select 1 */
> +#define K210_PCF_SPI1_SS2       80  /* SPI1 Chip Select 2 */
> +#define K210_PCF_SPI1_SS3       81  /* SPI1 Chip Select 3 */
> +#define K210_PCF_SPI1_ARB       82  /* SPI1 Arbitration */
> +#define K210_PCF_SPI1_SCLK      83  /* SPI1 Serial Clock */
> +#define K210_PCF_SPI2_D0        84  /* SPI2 Data 0 */
> +#define K210_PCF_SPI2_SS        85  /* SPI2 Select */
> +#define K210_PCF_SPI2_SCLK      86  /* SPI2 Serial Clock */
> +#define K210_PCF_I2S0_MCLK      87  /* I2S0 Master Clock */
> +#define K210_PCF_I2S0_SCLK      88  /* I2S0 Serial Clock(BCLK) */
> +#define K210_PCF_I2S0_WS        89  /* I2S0 Word Select(LRCLK) */
> +#define K210_PCF_I2S0_IN_D0     90  /* I2S0 Serial Data Input 0 */
> +#define K210_PCF_I2S0_IN_D1     91  /* I2S0 Serial Data Input 1 */
> +#define K210_PCF_I2S0_IN_D2     92  /* I2S0 Serial Data Input 2 */
> +#define K210_PCF_I2S0_IN_D3     93  /* I2S0 Serial Data Input 3 */
> +#define K210_PCF_I2S0_OUT_D0    94  /* I2S0 Serial Data Output 0 */
> +#define K210_PCF_I2S0_OUT_D1    95  /* I2S0 Serial Data Output 1 */
> +#define K210_PCF_I2S0_OUT_D2    96  /* I2S0 Serial Data Output 2 */
> +#define K210_PCF_I2S0_OUT_D3    97  /* I2S0 Serial Data Output 3 */
> +#define K210_PCF_I2S1_MCLK      98  /* I2S1 Master Clock */
> +#define K210_PCF_I2S1_SCLK      99  /* I2S1 Serial Clock(BCLK) */
> +#define K210_PCF_I2S1_WS        100 /* I2S1 Word Select(LRCLK) */
> +#define K210_PCF_I2S1_IN_D0     101 /* I2S1 Serial Data Input 0 */
> +#define K210_PCF_I2S1_IN_D1     102 /* I2S1 Serial Data Input 1 */
> +#define K210_PCF_I2S1_IN_D2     103 /* I2S1 Serial Data Input 2 */
> +#define K210_PCF_I2S1_IN_D3     104 /* I2S1 Serial Data Input 3 */
> +#define K210_PCF_I2S1_OUT_D0    105 /* I2S1 Serial Data Output 0 */
> +#define K210_PCF_I2S1_OUT_D1    106 /* I2S1 Serial Data Output 1 */
> +#define K210_PCF_I2S1_OUT_D2    107 /* I2S1 Serial Data Output 2 */
> +#define K210_PCF_I2S1_OUT_D3    108 /* I2S1 Serial Data Output 3 */
> +#define K210_PCF_I2S2_MCLK      109 /* I2S2 Master Clock */
> +#define K210_PCF_I2S2_SCLK      110 /* I2S2 Serial Clock(BCLK) */
> +#define K210_PCF_I2S2_WS        111 /* I2S2 Word Select(LRCLK) */
> +#define K210_PCF_I2S2_IN_D0     112 /* I2S2 Serial Data Input 0 */
> +#define K210_PCF_I2S2_IN_D1     113 /* I2S2 Serial Data Input 1 */
> +#define K210_PCF_I2S2_IN_D2     114 /* I2S2 Serial Data Input 2 */
> +#define K210_PCF_I2S2_IN_D3     115 /* I2S2 Serial Data Input 3 */
> +#define K210_PCF_I2S2_OUT_D0    116 /* I2S2 Serial Data Output 0 */
> +#define K210_PCF_I2S2_OUT_D1    117 /* I2S2 Serial Data Output 1 */
> +#define K210_PCF_I2S2_OUT_D2    118 /* I2S2 Serial Data Output 2 */
> +#define K210_PCF_I2S2_OUT_D3    119 /* I2S2 Serial Data Output 3 */
> +#define K210_PCF_RESV0          120 /* Reserved function */
> +#define K210_PCF_RESV1          121 /* Reserved function */
> +#define K210_PCF_RESV2          122 /* Reserved function */
> +#define K210_PCF_RESV3          123 /* Reserved function */
> +#define K210_PCF_RESV4          124 /* Reserved function */
> +#define K210_PCF_RESV5          125 /* Reserved function */
> +#define K210_PCF_I2C0_SCLK      126 /* I2C0 Serial Clock */
> +#define K210_PCF_I2C0_SDA       127 /* I2C0 Serial Data */
> +#define K210_PCF_I2C1_SCLK      128 /* I2C1 Serial Clock */
> +#define K210_PCF_I2C1_SDA       129 /* I2C1 Serial Data */
> +#define K210_PCF_I2C2_SCLK      130 /* I2C2 Serial Clock */
> +#define K210_PCF_I2C2_SDA       131 /* I2C2 Serial Data */
> +#define K210_PCF_DVP_XCLK       132 /* DVP System Clock */
> +#define K210_PCF_DVP_RST        133 /* DVP System Reset */
> +#define K210_PCF_DVP_PWDN       134 /* DVP Power Down Mode */
> +#define K210_PCF_DVP_VSYNC      135 /* DVP Vertical Sync */
> +#define K210_PCF_DVP_HREF       136 /* DVP Horizontal Reference output */
> +#define K210_PCF_DVP_PCLK       137 /* Pixel Clock */
> +#define K210_PCF_DVP_D0         138 /* Data Bit 0 */
> +#define K210_PCF_DVP_D1         139 /* Data Bit 1 */
> +#define K210_PCF_DVP_D2         140 /* Data Bit 2 */
> +#define K210_PCF_DVP_D3         141 /* Data Bit 3 */
> +#define K210_PCF_DVP_D4         142 /* Data Bit 4 */
> +#define K210_PCF_DVP_D5         143 /* Data Bit 5 */
> +#define K210_PCF_DVP_D6         144 /* Data Bit 6 */
> +#define K210_PCF_DVP_D7         145 /* Data Bit 7 */
> +#define K210_PCF_SCCB_SCLK      146 /* Serial Camera Control Bus Clock */
> +#define K210_PCF_SCCB_SDA       147 /* Serial Camera Control Bus Data */
> +#define K210_PCF_UART1_CTS      148 /* UART1 Clear To Send */
> +#define K210_PCF_UART1_DSR      149 /* UART1 Data Set Ready */
> +#define K210_PCF_UART1_DCD      150 /* UART1 Data Carrier Detect */
> +#define K210_PCF_UART1_RI       151 /* UART1 Ring Indicator */
> +#define K210_PCF_UART1_SIR_IN   152 /* UART1 Serial Infrared Input */
> +#define K210_PCF_UART1_DTR      153 /* UART1 Data Terminal Ready */
> +#define K210_PCF_UART1_RTS      154 /* UART1 Request To Send */
> +#define K210_PCF_UART1_OUT2     155 /* UART1 User-designated Output 2 */
> +#define K210_PCF_UART1_OUT1     156 /* UART1 User-designated Output 1 */
> +#define K210_PCF_UART1_SIR_OUT  157 /* UART1 Serial Infrared Output */
> +#define K210_PCF_UART1_BAUD     158 /* UART1 Transmit Clock Output */
> +#define K210_PCF_UART1_RE       159 /* UART1 Receiver Output Enable */
> +#define K210_PCF_UART1_DE       160 /* UART1 Driver Output Enable */
> +#define K210_PCF_UART1_RS485_EN 161 /* UART1 RS485 Enable */
> +#define K210_PCF_UART2_CTS      162 /* UART2 Clear To Send */
> +#define K210_PCF_UART2_DSR      163 /* UART2 Data Set Ready */
> +#define K210_PCF_UART2_DCD      164 /* UART2 Data Carrier Detect */
> +#define K210_PCF_UART2_RI       165 /* UART2 Ring Indicator */
> +#define K210_PCF_UART2_SIR_IN   166 /* UART2 Serial Infrared Input */
> +#define K210_PCF_UART2_DTR      167 /* UART2 Data Terminal Ready */
> +#define K210_PCF_UART2_RTS      168 /* UART2 Request To Send */
> +#define K210_PCF_UART2_OUT2     169 /* UART2 User-designated Output 2 */
> +#define K210_PCF_UART2_OUT1     170 /* UART2 User-designated Output 1 */
> +#define K210_PCF_UART2_SIR_OUT  171 /* UART2 Serial Infrared Output */
> +#define K210_PCF_UART2_BAUD     172 /* UART2 Transmit Clock Output */
> +#define K210_PCF_UART2_RE       173 /* UART2 Receiver Output Enable */
> +#define K210_PCF_UART2_DE       174 /* UART2 Driver Output Enable */
> +#define K210_PCF_UART2_RS485_EN 175 /* UART2 RS485 Enable */
> +#define K210_PCF_UART3_CTS      176 /* UART3 Clear To Send */
> +#define K210_PCF_UART3_DSR      177 /* UART3 Data Set Ready */
> +#define K210_PCF_UART3_DCD      178 /* UART3 Data Carrier Detect */
> +#define K210_PCF_UART3_RI       179 /* UART3 Ring Indicator */
> +#define K210_PCF_UART3_SIR_IN   180 /* UART3 Serial Infrared Input */
> +#define K210_PCF_UART3_DTR      181 /* UART3 Data Terminal Ready */
> +#define K210_PCF_UART3_RTS      182 /* UART3 Request To Send */
> +#define K210_PCF_UART3_OUT2     183 /* UART3 User-designated Output 2 */
> +#define K210_PCF_UART3_OUT1     184 /* UART3 User-designated Output 1 */
> +#define K210_PCF_UART3_SIR_OUT  185 /* UART3 Serial Infrared Output */
> +#define K210_PCF_UART3_BAUD     186 /* UART3 Transmit Clock Output */
> +#define K210_PCF_UART3_RE       187 /* UART3 Receiver Output Enable */
> +#define K210_PCF_UART3_DE       188 /* UART3 Driver Output Enable */
> +#define K210_PCF_UART3_RS485_EN 189 /* UART3 RS485 Enable */
> +#define K210_PCF_TIMER0_TOGGLE1 190 /* TIMER0 Toggle Output 1 */
> +#define K210_PCF_TIMER0_TOGGLE2 191 /* TIMER0 Toggle Output 2 */
> +#define K210_PCF_TIMER0_TOGGLE3 192 /* TIMER0 Toggle Output 3 */
> +#define K210_PCF_TIMER0_TOGGLE4 193 /* TIMER0 Toggle Output 4 */
> +#define K210_PCF_TIMER1_TOGGLE1 194 /* TIMER1 Toggle Output 1 */
> +#define K210_PCF_TIMER1_TOGGLE2 195 /* TIMER1 Toggle Output 2 */
> +#define K210_PCF_TIMER1_TOGGLE3 196 /* TIMER1 Toggle Output 3 */
> +#define K210_PCF_TIMER1_TOGGLE4 197 /* TIMER1 Toggle Output 4 */
> +#define K210_PCF_TIMER2_TOGGLE1 198 /* TIMER2 Toggle Output 1 */
> +#define K210_PCF_TIMER2_TOGGLE2 199 /* TIMER2 Toggle Output 2 */
> +#define K210_PCF_TIMER2_TOGGLE3 200 /* TIMER2 Toggle Output 3 */
> +#define K210_PCF_TIMER2_TOGGLE4 201 /* TIMER2 Toggle Output 4 */
> +#define K210_PCF_CLK_SPI2       202 /* Clock SPI2 */
> +#define K210_PCF_CLK_I2C2       203 /* Clock I2C2 */
> +#define K210_PCF_INTERNAL0      204 /* Internal function signal 0 */
> +#define K210_PCF_INTERNAL1      205 /* Internal function signal 1 */
> +#define K210_PCF_INTERNAL2      206 /* Internal function signal 2 */
> +#define K210_PCF_INTERNAL3      207 /* Internal function signal 3 */
> +#define K210_PCF_INTERNAL4      208 /* Internal function signal 4 */
> +#define K210_PCF_INTERNAL5      209 /* Internal function signal 5 */
> +#define K210_PCF_INTERNAL6      210 /* Internal function signal 6 */
> +#define K210_PCF_INTERNAL7      211 /* Internal function signal 7 */
> +#define K210_PCF_INTERNAL8      212 /* Internal function signal 8 */
> +#define K210_PCF_INTERNAL9      213 /* Internal function signal 9 */
> +#define K210_PCF_INTERNAL10     214 /* Internal function signal 10 */
> +#define K210_PCF_INTERNAL11     215 /* Internal function signal 11 */
> +#define K210_PCF_INTERNAL12     216 /* Internal function signal 12 */
> +#define K210_PCF_INTERNAL13     217 /* Internal function signal 13 */
> +#define K210_PCF_INTERNAL14     218 /* Internal function signal 14 */
> +#define K210_PCF_INTERNAL15     219 /* Internal function signal 15 */
> +#define K210_PCF_INTERNAL16     220 /* Internal function signal 16 */
> +#define K210_PCF_INTERNAL17     221 /* Internal function signal 17 */
> +#define K210_PCF_CONSTANT       222 /* Constant function */
> +#define K210_PCF_INTERNAL18     223 /* Internal function signal 18 */
> +#define K210_PCF_DEBUG0         224 /* Debug function 0 */
> +#define K210_PCF_DEBUG1         225 /* Debug function 1 */
> +#define K210_PCF_DEBUG2         226 /* Debug function 2 */
> +#define K210_PCF_DEBUG3         227 /* Debug function 3 */
> +#define K210_PCF_DEBUG4         228 /* Debug function 4 */
> +#define K210_PCF_DEBUG5         229 /* Debug function 5 */
> +#define K210_PCF_DEBUG6         230 /* Debug function 6 */
> +#define K210_PCF_DEBUG7         231 /* Debug function 7 */
> +#define K210_PCF_DEBUG8         232 /* Debug function 8 */
> +#define K210_PCF_DEBUG9         233 /* Debug function 9 */
> +#define K210_PCF_DEBUG10        234 /* Debug function 10 */
> +#define K210_PCF_DEBUG11        235 /* Debug function 11 */
> +#define K210_PCF_DEBUG12        236 /* Debug function 12 */
> +#define K210_PCF_DEBUG13        237 /* Debug function 13 */
> +#define K210_PCF_DEBUG14        238 /* Debug function 14 */
> +#define K210_PCF_DEBUG15        239 /* Debug function 15 */
> +#define K210_PCF_DEBUG16        240 /* Debug function 16 */
> +#define K210_PCF_DEBUG17        241 /* Debug function 17 */
> +#define K210_PCF_DEBUG18        242 /* Debug function 18 */
> +#define K210_PCF_DEBUG19        243 /* Debug function 19 */
> +#define K210_PCF_DEBUG20        244 /* Debug function 20 */
> +#define K210_PCF_DEBUG21        245 /* Debug function 21 */
> +#define K210_PCF_DEBUG22        246 /* Debug function 22 */
> +#define K210_PCF_DEBUG23        247 /* Debug function 23 */
> +#define K210_PCF_DEBUG24        248 /* Debug function 24 */
> +#define K210_PCF_DEBUG25        249 /* Debug function 25 */
> +#define K210_PCF_DEBUG26        250 /* Debug function 26 */
> +#define K210_PCF_DEBUG27        251 /* Debug function 27 */
> +#define K210_PCF_DEBUG28        252 /* Debug function 28 */
> +#define K210_PCF_DEBUG29        253 /* Debug function 29 */
> +#define K210_PCF_DEBUG30        254 /* Debug function 30 */
> +#define K210_PCF_DEBUG31        255 /* Debug function 31 */
> +
> +/*
> + * The K210 only implements 8 drive levels, even though there is register space
> + * for 16
> + */
> +#define K210_PC_DRIVE_SHIFT 8
> +#define K210_PC_DRIVE_MASK GENMASK(11, 8)
> +#define K210_PC_DRIVE_0 (0 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_2 (2 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_3 (3 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_4 (4 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_5 (5 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_6 (6 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_7 (7 << K210_PC_DRIVE_SHIFT)
> +#define K210_PC_DRIVE_MAX 7
> +
> +#define K210_PC_MODE_MASK GENMASK(23, 12)
> +/*
> + * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE) where FUNCTION_OE is a
> + * physical signal from the function
> + */
> +#define K210_PC_OE       BIT(12) /* Output Enable */
> +#define K210_PC_OE_INV   BIT(13) /* INVert function-controlled Output Enable */
> +#define K210_PC_DO_OE    BIT(14) /* set Data Out to the Output Enable signal */
> +#define K210_PC_DO_INV   BIT(15) /* INVert final Data Output */
> +#define K210_PC_PU       BIT(16) /* Pull Up */
> +#define K210_PC_PD       BIT(17) /* Pull Down */
> +/* Strong pull up not implemented on K210 */
> +#define K210_PC_SL       BIT(19) /* reduce SLew rate to prevent overshoot */
> +/* Same semantics as OE above */
> +#define K210_PC_IE       BIT(20) /* Input Enable */
> +#define K210_PC_IE_INV   BIT(21) /* INVert function-controlled Input Enable */
> +#define K210_PC_DI_INV   BIT(22) /* INVert Data Input */
> +#define K210_PC_ST       BIT(23) /* Schmitt Trigger */
> +#define K210_PC_DI       BIT(31) /* raw Data Input */
> +#define K210_PC_BIAS_MASK (K210_PC_PU & K210_PC_PD)
> +
> +#define K210_PC_MODE_IN   (K210_PC_IE | K210_PC_ST)
> +#define K210_PC_MODE_OUT  (K210_PC_DRIVE_7 | K210_PC_OE)
> +#define K210_PC_MODE_I2C  (K210_PC_MODE_IN | K210_PC_IE_INV | K210_PC_SL | \
> +                          K210_PC_OE | K210_PC_OE_INV | K210_PC_PU)
> +#define K210_PC_MODE_SPI  (K210_PC_MODE_IN | K210_PC_IE_INV | \
> +                          K210_PC_MODE_OUT | K210_PC_OE_INV)
> +#define K210_PC_MODE_GPIO (K210_PC_MODE_IN | K210_PC_MODE_OUT)
> +
> +struct k210_fpioa {
> +       u32 pins[48];
> +       u32 tie_en[8];
> +       u32 tie_val[8];
> +};
> +
> +#endif /* K210_PINCTRL_H */
> diff --git a/include/dt-bindings/pinctrl/k210-pinctrl.h b/include/dt-bindings/pinctrl/k210-pinctrl.h
> new file mode 100644
> index 0000000000..6d44ea765b
> --- /dev/null
> +++ b/include/dt-bindings/pinctrl/k210-pinctrl.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef DT_K210_PINCTRL_H
> +#define DT_K210_PINCTRL_H
> +
> +#define K210_PC_POWER_3V3 0
> +#define K210_PC_POWER_1V8 1
> +
> +#endif /* DT_K210_PINCTRL_H */
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks
  2020-02-28 21:05 ` [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks Sean Anderson
@ 2020-03-04  6:50   ` Rick Chen
  0 siblings, 0 replies; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:50 UTC (permalink / raw)
  To: u-boot

Hi Sean

> For clocks not in the CCF, their parents will not have UCLASS_CLK, so we
> just enable them as normal. The enable count is local to the struct clk,
> but this will never result in the actual en-/dis-able op being called
> (unless the same struct clk is enabled twice).
>
> For clocks in the CCF, we always traverse up the tree when enabling.
> Previously, CCF clocks without id set would be skipped, stopping the
> traversal too early.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> Acked-by: Lukasz Majewski <lukma@denx.de>
> ---
>
> Changes in v5:
> - Clear enable_count on request
>
> Changes in v4:
> - Lint
>
> Changes in v3:
> - New
>
>  drivers/clk/clk-uclass.c | 60 ++++++++++++++++++----------------------
>  1 file changed, 27 insertions(+), 33 deletions(-)
>

Conflict with master, please rebase.

Thanks,
Rick

> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index b42e76ebfa..ffd8f874dc 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -410,6 +410,7 @@ int clk_request(struct udevice *dev, struct clk *clk)
>         ops = clk_dev_ops(dev);
>
>         clk->dev = dev;
> +       clk->enable_count = 0;
>
>         if (!ops->request)
>                 return 0;
> @@ -522,7 +523,6 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
>  int clk_enable(struct clk *clk)
>  {
>         const struct clk_ops *ops;
> -       struct clk *clkp = NULL;
>         int ret;
>
>         debug("%s(clk=%p \"%s\")\n", __func__, clk, clk->dev->name);
> @@ -531,32 +531,29 @@ int clk_enable(struct clk *clk)
>         ops = clk_dev_ops(clk->dev);
>
>         if (CONFIG_IS_ENABLED(CLK_CCF)) {
> -               /* Take id 0 as a non-valid clk, such as dummy */
> -               if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
> -                       if (clkp->enable_count) {
> -                               clkp->enable_count++;
> -                               return 0;
> -                       }
> -                       if (clkp->dev->parent &&
> -                           device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
> -                               ret = clk_enable(dev_get_clk_ptr(clkp->dev->parent));
> -                               if (ret) {
> -                                       printf("Enable %s failed\n",
> -                                              clkp->dev->parent->name);
> -                                       return ret;
> -                               }
> +               if (clk->enable_count) {
> +                       clk->enable_count++;
> +                       return 0;
> +               }
> +               if (clk->dev->parent &&
> +                   device_get_uclass_id(clk->dev->parent) == UCLASS_CLK) {
> +                       ret = clk_enable(dev_get_clk_ptr(clk->dev->parent));
> +                       if (ret) {
> +                               printf("Enable %s failed\n",
> +                                      clk->dev->parent->name);
> +                               return ret;
>                         }
>                 }
>
>                 if (ops->enable) {
>                         ret = ops->enable(clk);
>                         if (ret) {
> -                               printf("Enable %s failed\n", clk->dev->name);
> +                               printf("Enable %s failed (error %d)\n",
> +                                      clk->dev->name, ret);
>                                 return ret;
>                         }
>                 }
> -               if (clkp)
> -                       clkp->enable_count++;
> +               clk->enable_count++;
>         } else {
>                 if (!ops->enable)
>                         return -ENOSYS;
> @@ -582,7 +579,6 @@ int clk_enable_bulk(struct clk_bulk *bulk)
>  int clk_disable(struct clk *clk)
>  {
>         const struct clk_ops *ops;
> -       struct clk *clkp = NULL;
>         int ret;
>
>         debug("%s(clk=%p)\n", __func__, clk);
> @@ -591,29 +587,27 @@ int clk_disable(struct clk *clk)
>         ops = clk_dev_ops(clk->dev);
>
>         if (CONFIG_IS_ENABLED(CLK_CCF)) {
> -               if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
> -                       if (clkp->enable_count == 0) {
> -                               printf("clk %s already disabled\n",
> -                                      clkp->dev->name);
> -                               return 0;
> -                       }
> -
> -                       if (--clkp->enable_count > 0)
> -                               return 0;
> +               if (clk->enable_count == 0) {
> +                       printf("clk %s already disabled\n",
> +                              clk->dev->name);
> +                       return 0;
>                 }
>
> +               if (--clk->enable_count > 0)
> +                       return 0;
> +
>                 if (ops->disable) {
>                         ret = ops->disable(clk);
>                         if (ret)
>                                 return ret;
>                 }
>
> -               if (clkp && clkp->dev->parent &&
> -                   device_get_uclass_id(clkp->dev) == UCLASS_CLK) {
> -                       ret = clk_disable(dev_get_clk_ptr(clkp->dev->parent));
> +               if (clk->dev->parent &&
> +                   device_get_uclass_id(clk->dev) == UCLASS_CLK) {
> +                       ret = clk_disable(dev_get_clk_ptr(clk->dev->parent));
>                         if (ret) {
> -                               printf("Disable %s failed\n",
> -                                      clkp->dev->parent->name);
> +                               printf("Disable %s failed (error %d)\n",
> +                                      clk->dev->parent->name, ret);
>                                 return ret;
>                         }
>                 }
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 07/33] clk: Add K210 clock support
  2020-02-28 21:05 ` [PATCH v5 07/33] clk: Add K210 clock support Sean Anderson
@ 2020-03-04  6:58   ` Rick Chen
  2020-03-04 14:54     ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-04  6:58 UTC (permalink / raw)
  To: u-boot

Hi Sean

> Due to the large number of clocks, I decided to use the CCF. The overall
> structure is modeled after the imx code. Clocks are stored in several
> arrays.  There are some translation macros (FOOIFY()) which allow for more
> dense packing.  A possible improvement could be to only store the
> parameters we need, instead of the whole CCF struct.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---

Please checkpatch and fix
total: 4 errors, 4 warnings, 18 checks, 662 lines checked

Thanks
Rick

>
> Changes in v5:
> - Don't unmap priv->reg
> - Remove comment on APB clocks since it has been clarified by Kendryte
> - Add i2s mclks
> - Reorder clock ids to be continuous
> - Rewrite to statically allocate all clocks. This has helped find several bugs
>   (since it is easy to see when a clock has the wrong register).
> - Fix ACLK sometimes having the wrong parent
> - Fix SPI3 having the wrong divider
> - Prevent being probed multiple times on failure
>
> Changes in v4:
> - Reparent aclk before configuring pll0
> - Update copyright
> - Lint
>
> Changes in v3:
> - Removed sysctl struct, replacing it with defines. This is to have the same
>   interface to sysctl from C as from the device tree.
> - Fixed clocks having the same id
> - Fixed clocks not using the correct register/bits
> - Aligned the defines in headers
>
> Changes in v2:
> - Add clk.o to obj-y
> - Don't probe before relocation
>
>  MAINTAINERS                                   |   7 +
>  .../mfd/kendryte,k210-sysctl.txt              |  33 ++
>  drivers/clk/kendryte/Kconfig                  |   2 +-
>  drivers/clk/kendryte/Makefile                 |   2 +-
>  drivers/clk/kendryte/clk.c                    | 478 ++++++++++++++++++
>  include/dt-bindings/clock/k210-sysctl.h       |  56 ++
>  include/dt-bindings/mfd/k210-sysctl.h         |  38 ++
>  include/kendryte/clk.h                        |  35 ++
>  8 files changed, 649 insertions(+), 2 deletions(-)
>  create mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
>  create mode 100644 drivers/clk/kendryte/clk.c
>  create mode 100644 include/dt-bindings/clock/k210-sysctl.h
>  create mode 100644 include/dt-bindings/mfd/k210-sysctl.h
>  create mode 100644 include/kendryte/clk.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 82e4159bec..8e9e0569ba 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -805,6 +805,13 @@ F: arch/riscv/
>  F:     cmd/riscv/
>  F:     tools/prelink-riscv.c
>
> +RISC-V KENDRYTE
> +M:     Sean Anderson <seanga2@gmail.com>
> +S:     Maintained
> +F:     doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
> +F:     drivers/clk/kendryte/
> +F:     include/kendryte/
> +
>  RNG
>  M:     Sughosh Ganu <sughosh.ganu@linaro.org>
>  R:     Heinrich Schuchardt <xypron.glpk@gmx.de>
> diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
> new file mode 100644
> index 0000000000..5b24abcb62
> --- /dev/null
> +++ b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt
> @@ -0,0 +1,33 @@
> +Kendryte K210 Sysctl
> +
> +This binding describes the K210 sysctl device, which contains many miscellaneous
> +registers controlling system functionality. This node is a register map and can
> +be reference by other bindings which need a phandle to the K210 sysctl regmap.
> +
> +Required properties:
> +- compatible: should be
> +       "kendryte,k210-sysctl", "syscon", "simple-mfd"
> +- reg: address and length of the sysctl registers
> +- reg-io-width: must be <4>
> +
> +Clock sub-node
> +
> +This node is a binding for the clock tree driver
> +
> +Required properties:
> +- compatible: should be "kendryte,k210-clk"
> +- clocks: phandle to the "in0" external oscillator
> +- #clock-cells: must be <1>
> +
> +Example:
> +sysctl: syscon at 50440000 {
> +       compatible = "kendryte,k210-sysctl", "syscon", "simple-mfd";
> +       reg = <0x50440000 0x100>;
> +       reg-io-width = <4>;
> +
> +       sysclk: clock-controller {
> +               compatible = "kendryte,k210-clk";
> +               clocks = <&in0>;
> +               #clock-cells = <1>;
> +       };
> +};
> diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig
> index 7b69c8afaf..073fca0781 100644
> --- a/drivers/clk/kendryte/Kconfig
> +++ b/drivers/clk/kendryte/Kconfig
> @@ -1,6 +1,6 @@
>  config CLK_K210
>         bool "Clock support for Kendryte K210"
> -       depends on CLK && CLK_CCF
> +       depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF
>         help
>           This enables support clock driver for Kendryte K210 platforms.
>
> diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile
> index 47f682fce3..6fb68253ae 100644
> --- a/drivers/clk/kendryte/Makefile
> +++ b/drivers/clk/kendryte/Makefile
> @@ -1 +1 @@
> -obj-y += bypass.o pll.o
> +obj-y += bypass.o clk.o pll.o
> diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c
> new file mode 100644
> index 0000000000..b01d246682
> --- /dev/null
> +++ b/drivers/clk/kendryte/clk.c
> @@ -0,0 +1,478 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> + */
> +#include <kendryte/clk.h>
> +
> +#include <asm/io.h>
> +#include <dt-bindings/clock/k210-sysctl.h>
> +#include <dt-bindings/mfd/k210-sysctl.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <mapmem.h>
> +
> +#include <kendryte/bypass.h>
> +#include <kendryte/pll.h>
> +
> +static ulong k210_clk_get_rate(struct clk *clk)
> +{
> +       struct clk *c;
> +       int err = clk_get_by_id(clk->id, &c);
> +
> +       if (err)
> +               return err;
> +       return clk_get_rate(c);
> +}
> +
> +static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate)
> +{
> +       struct clk *c;
> +       int err = clk_get_by_id(clk->id, &c);
> +
> +       if (err)
> +               return err;
> +       return clk_set_rate(c, rate);
> +}
> +
> +static int k210_clk_set_parent(struct clk *clk, struct clk *parent)
> +{
> +       struct clk *c, *p;
> +       int err = clk_get_by_id(clk->id, &c);
> +
> +       if (err)
> +               return err;
> +
> +       err = clk_get_by_id(parent->id, &p);
> +       if (err)
> +               return err;
> +
> +       return clk_set_parent(c, p);
> +}
> +
> +static int k210_clk_endisable(struct clk *clk, bool enable)
> +{
> +       struct clk *c;
> +       int err = clk_get_by_id(clk->id, &c);
> +
> +       if (err)
> +               return err;
> +       return enable ? clk_enable(c) : clk_disable(c);
> +}
> +
> +static int k210_clk_enable(struct clk *clk)
> +{
> +       return k210_clk_endisable(clk, true);
> +}
> +
> +static int k210_clk_disable(struct clk *clk)
> +{
> +       return k210_clk_endisable(clk, false);
> +}
> +
> +static const struct clk_ops k210_clk_ops = {
> +       .set_rate = k210_clk_set_rate,
> +       .get_rate = k210_clk_get_rate,
> +       .set_parent = k210_clk_set_parent,
> +       .enable = k210_clk_enable,
> +       .disable = k210_clk_disable,
> +};
> +
> +static const char * const generic_sels[] = { "in0_half", "pll0_half" };
> +/* The first clock is in0, which is filled in by k210_clk_probe */
> +static const char *aclk_sels[] = { NULL, "pll0_half" };
> +static const char *pll2_sels[] = { NULL, "pll0", "pll1" };
> +
> +#define DIV(id, reg, shift, width) DIV_FLAGS(id, reg, shift, width, 0)
> +#define DIV_LIST \
> +       DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \
> +                 CLK_DIVIDER_POWER_OF_TWO) \
> +       DIV(K210_CLK_APB0,   K210_SYSCTL_SEL0,  3,  3) \
> +       DIV(K210_CLK_APB1,   K210_SYSCTL_SEL0,  6,  3) \
> +       DIV(K210_CLK_APB2,   K210_SYSCTL_SEL0,  9,  3) \
> +       DIV(K210_CLK_SRAM0,  K210_SYSCTL_THR0,  0,  4) \
> +       DIV(K210_CLK_SRAM1,  K210_SYSCTL_THR0,  4,  4) \
> +       DIV(K210_CLK_AI,     K210_SYSCTL_THR0,  8,  4) \
> +       DIV(K210_CLK_DVP,    K210_SYSCTL_THR0, 12,  4) \
> +       DIV(K210_CLK_ROM,    K210_SYSCTL_THR0, 16,  4) \
> +       DIV(K210_CLK_SPI0,   K210_SYSCTL_THR1,  0,  8) \
> +       DIV(K210_CLK_SPI1,   K210_SYSCTL_THR1,  8,  8) \
> +       DIV(K210_CLK_SPI2,   K210_SYSCTL_THR1, 16,  8) \
> +       DIV(K210_CLK_SPI3,   K210_SYSCTL_THR1, 24,  8) \
> +       DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2,  0,  8) \
> +       DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2,  8,  8) \
> +       DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16,  8) \
> +       DIV(K210_CLK_I2S0,   K210_SYSCTL_THR3,  0, 16) \
> +       DIV(K210_CLK_I2S1,   K210_SYSCTL_THR3, 16, 16) \
> +       DIV(K210_CLK_I2S2,   K210_SYSCTL_THR4,  0, 16) \
> +       DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16,  8) \
> +       DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24,  8) \
> +       DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4,  0,  8) \
> +       DIV(K210_CLK_I2C0,   K210_SYSCTL_THR5,  8,  8) \
> +       DIV(K210_CLK_I2C1,   K210_SYSCTL_THR5, 16,  8) \
> +       DIV(K210_CLK_I2C2,   K210_SYSCTL_THR5, 24,  8) \
> +       DIV(K210_CLK_WDT0,   K210_SYSCTL_THR6,  0,  8) \
> +       DIV(K210_CLK_WDT1,   K210_SYSCTL_THR6,  8,  8)
> +
> +#define _DIVIFY(id) K210_CLK_DIV_##id
> +#define DIVIFY(id) _DIVIFY(id)
> +#define DIV_FLAGS(id, ...) DIVIFY(id),
> +enum k210_clk_div_ids {
> +       DIV_LIST
> +};
> +#undef DIV_FLAGS
> +
> +#define DIV_FLAGS(id, _reg, _shift, _width, _flags) [DIVIFY(id)] = { \
> +       .reg = (void *)(_reg), \
> +       .shift = (_shift), \
> +       .width = (_width), \
> +       .flags = (_flags), \
> +},
> +static struct clk_divider k210_clk_dividers[]  = {
> +       DIV_LIST
> +};
> +#undef DIV_FLAGS
> +#undef DIV
> +#undef DIV_LIST
> +
> +#define GATE_LIST \
> +       GATE(K210_CLK_CPU,    K210_SYSCTL_EN_CENT,  0) \
> +       GATE(K210_CLK_SRAM0,  K210_SYSCTL_EN_CENT,  1) \
> +       GATE(K210_CLK_SRAM1,  K210_SYSCTL_EN_CENT,  2) \
> +       GATE(K210_CLK_APB0,   K210_SYSCTL_EN_CENT,  3) \
> +       GATE(K210_CLK_APB1,   K210_SYSCTL_EN_CENT,  4) \
> +       GATE(K210_CLK_APB2,   K210_SYSCTL_EN_CENT,  5) \
> +       GATE(K210_CLK_ROM,    K210_SYSCTL_EN_PERI,  0) \
> +       GATE(K210_CLK_DMA,    K210_SYSCTL_EN_PERI,  1) \
> +       GATE(K210_CLK_AI,     K210_SYSCTL_EN_PERI,  2) \
> +       GATE(K210_CLK_DVP,    K210_SYSCTL_EN_PERI,  3) \
> +       GATE(K210_CLK_FFT,    K210_SYSCTL_EN_PERI,  4) \
> +       GATE(K210_CLK_GPIO,   K210_SYSCTL_EN_PERI,  5) \
> +       GATE(K210_CLK_SPI0,   K210_SYSCTL_EN_PERI,  6) \
> +       GATE(K210_CLK_SPI1,   K210_SYSCTL_EN_PERI,  7) \
> +       GATE(K210_CLK_SPI2,   K210_SYSCTL_EN_PERI,  8) \
> +       GATE(K210_CLK_SPI3,   K210_SYSCTL_EN_PERI,  9) \
> +       GATE(K210_CLK_I2S0,   K210_SYSCTL_EN_PERI, 10) \
> +       GATE(K210_CLK_I2S1,   K210_SYSCTL_EN_PERI, 11) \
> +       GATE(K210_CLK_I2S2,   K210_SYSCTL_EN_PERI, 12) \
> +       GATE(K210_CLK_I2C0,   K210_SYSCTL_EN_PERI, 13) \
> +       GATE(K210_CLK_I2C1,   K210_SYSCTL_EN_PERI, 14) \
> +       GATE(K210_CLK_I2C2,   K210_SYSCTL_EN_PERI, 15) \
> +       GATE(K210_CLK_UART1,  K210_SYSCTL_EN_PERI, 16) \
> +       GATE(K210_CLK_UART2,  K210_SYSCTL_EN_PERI, 17) \
> +       GATE(K210_CLK_UART3,  K210_SYSCTL_EN_PERI, 18) \
> +       GATE(K210_CLK_AES,    K210_SYSCTL_EN_PERI, 19) \
> +       GATE(K210_CLK_FPIOA,  K210_SYSCTL_EN_PERI, 20) \
> +       GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \
> +       GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \
> +       GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \
> +       GATE(K210_CLK_WDT0,   K210_SYSCTL_EN_PERI, 24) \
> +       GATE(K210_CLK_WDT1,   K210_SYSCTL_EN_PERI, 25) \
> +       GATE(K210_CLK_SHA,    K210_SYSCTL_EN_PERI, 26) \
> +       GATE(K210_CLK_OTP,    K210_SYSCTL_EN_PERI, 27) \
> +       GATE(K210_CLK_RTC,    K210_SYSCTL_EN_PERI, 29)
> +
> +#define _GATEIFY(id) K210_CLK_GATE_##id
> +#define GATEIFY(id) _GATEIFY(id)
> +#define GATE(id, ...) GATEIFY(id),
> +enum k210_clk_gate_ids {
> +       GATE_LIST
> +};
> +#undef GATE
> +
> +#define GATE(id, _reg, _idx) [GATEIFY(id)] = { \
> +       .reg = (void *)(_reg), \
> +       .bit_idx = (_idx), \
> +},
> +static struct clk_gate k210_clk_gates[] = {
> +       GATE_LIST
> +};
> +#undef GATE
> +#undef GATE_LIST
> +
> +#define MUX(id, reg, shift, width) \
> +       MUX_PARENTS(id, generic_sels, reg, shift, width)
> +#define MUX_LIST \
> +       MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \
> +       MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0,  0, 1) \
> +       MUX(K210_CLK_SPI3,   K210_SYSCTL_SEL0, 12, 1) \
> +       MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \
> +       MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \
> +       MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1)
> +
> +#define _MUXIFY(id) K210_CLK_MUX_##id
> +#define MUXIFY(id) _MUXIFY(id)
> +#define MUX_PARENTS(id, ...) MUXIFY(id),
> +enum k210_clk_mux_ids {
> +       MUX_LIST
> +};
> +#undef MUX_PARENTS
> +
> +#define MUX_PARENTS(id, parents, _reg, _shift, _width) [MUXIFY(id)] = { \
> +       .parent_names = (const char * const *)(parents), \
> +       .num_parents = ARRAY_SIZE(parents), \
> +       .reg = (void *)(_reg), \
> +       .shift = (_shift), \
> +       .mask = BIT(_width) - 1, \
> +},
> +static struct clk_mux k210_clk_muxes[] = {
> +       MUX_LIST
> +};
> +#undef MUX_PARENTS
> +#undef MUX
> +#undef MUX_LIST
> +
> +#define PLL(_reg, _shift, _width) { \
> +       .reg = (void *)(_reg), \
> +       .lock = (void *)K210_SYSCTL_PLL_LOCK, \
> +       .shift = (_shift), \
> +       .width = (_width), \
> +}
> +static struct k210_pll k210_clk_plls[] = {
> +       [0] = PLL(K210_SYSCTL_PLL0,  0, 2),
> +       [1] = PLL(K210_SYSCTL_PLL1,  8, 1),
> +       [2] = PLL(K210_SYSCTL_PLL2, 16, 1),
> +};
> +#undef PLL
> +
> +#define COMP(id, mux, div, gate) \
> +       COMP_FULL(id, &(mux)->clk, &clk_mux_ops, \
> +                 &(div)->clk, &clk_divider_ops, \
> +                 &(gate)->clk, &clk_gate_ops)
> +#define COMP_ID(id) \
> +       COMP(id, &k210_clk_muxes[MUXIFY(id)], \
> +            &k210_clk_dividers[DIVIFY(id)], \
> +            &k210_clk_gates[GATEIFY(id)])
> +#define COMP_NOMUX(id, div, gate) \
> +       COMP_FULL(id, NULL, NULL, \
> +                 &(div)->clk, &clk_divider_ops, \
> +                 &(gate)->clk, &clk_gate_ops)
> +#define COMP_NOMUX_ID(id) \
> +       COMP_NOMUX(id, &k210_clk_dividers[DIVIFY(id)], \
> +                  &k210_clk_gates[GATEIFY(id)])
> +#define COMP_LIST \
> +       COMP_FULL(K210_CLK_PLL2, \
> +                 &k210_clk_muxes[MUXIFY(K210_CLK_PLL2)].clk, &clk_mux_ops, \
> +                 &k210_clk_plls[2].clk, &k210_pll_ops, \
> +                 &k210_clk_plls[2].clk, &k210_pll_ops) \
> +       COMP_FULL(K210_CLK_ACLK, \
> +                 &k210_clk_muxes[MUXIFY(K210_CLK_ACLK)].clk, &clk_mux_ops, \
> +                 &k210_clk_dividers[DIVIFY(K210_CLK_ACLK)].clk, \
> +                 &clk_divider_ops, \
> +                 NULL, NULL) \
> +       COMP_ID(K210_CLK_SPI3) \
> +       COMP_ID(K210_CLK_TIMER0) \
> +       COMP_ID(K210_CLK_TIMER1) \
> +       COMP_ID(K210_CLK_TIMER2) \
> +       COMP_NOMUX_ID(K210_CLK_SRAM0) \
> +       COMP_NOMUX_ID(K210_CLK_SRAM1) \
> +       COMP_NOMUX_ID(K210_CLK_ROM) \
> +       COMP_NOMUX_ID(K210_CLK_DVP) \
> +       COMP_NOMUX_ID(K210_CLK_APB0) \
> +       COMP_NOMUX_ID(K210_CLK_APB1) \
> +       COMP_NOMUX_ID(K210_CLK_APB2) \
> +       COMP_NOMUX_ID(K210_CLK_AI) \
> +       COMP_NOMUX_ID(K210_CLK_I2S0) \
> +       COMP_NOMUX_ID(K210_CLK_I2S1) \
> +       COMP_NOMUX_ID(K210_CLK_I2S2) \
> +       COMP_NOMUX_ID(K210_CLK_WDT0) \
> +       COMP_NOMUX_ID(K210_CLK_WDT1) \
> +       COMP_NOMUX_ID(K210_CLK_SPI0) \
> +       COMP_NOMUX_ID(K210_CLK_SPI1) \
> +       COMP_NOMUX_ID(K210_CLK_SPI2) \
> +       COMP_NOMUX_ID(K210_CLK_I2C0) \
> +       COMP_NOMUX_ID(K210_CLK_I2C1) \
> +       COMP_NOMUX_ID(K210_CLK_I2C2)
> +
> +#define _COMPIFY(id) K210_CLK_COMP_##id
> +#define COMPIFY(id) _COMPIFY(id)
> +#define COMP_FULL(id, ...) COMPIFY(id),
> +enum k210_clk_comp_ids {
> +       COMP_LIST
> +};
> +#undef COMP_FULL
> +
> +#define COMP_FULL(id, _mux, _mux_ops, _div, _div_ops, _gate, _gate_ops) \
> +[COMPIFY(id)] = { \
> +       .mux = (_mux), \
> +       .mux_ops = (_mux_ops), \
> +       .rate = (_div), \
> +       .rate_ops = (_div_ops), \
> +       .gate = (_gate), \
> +       .gate_ops = (_gate_ops), \
> +},
> +static struct clk_composite k210_clk_comps[] = {
> +       COMP_LIST
> +};
> +#undef COMP_FULL
> +#undef COMP
> +#undef COMP_ID
> +#undef COMP_NOMUX
> +#undef COMP_NOMUX_ID
> +#undef COMP_LIST
> +
> +static struct clk *k210_clk_bypass_children = {
> +       &k210_clk_comps[COMPIFY(K210_CLK_ACLK)].clk,
> +};
> +
> +static struct clk *k210_clk_bypass_saved_parents = {
> +       NULL,
> +};
> +
> +static struct k210_bypass k210_clk_bypass = {
> +       .bypassee = &k210_clk_plls[0].clk,
> +       .bypassee_ops = &k210_pll_ops,
> +       .children = &k210_clk_bypass_children,
> +       .child_count = 1,
> +       .saved_parents = &k210_clk_bypass_saved_parents,
> +};
> +
> +static bool probed = false;
> +
> +static int k210_clk_probe(struct udevice *dev)
> +{
> +       int ret, i;
> +       const char *in0;
> +       struct clk *in0_clk;
> +       void *base;
> +
> +       /* Only one instance of this driver allowed */
> +       if (READ_ONCE(probed))
> +               return -ENOTSUPP;
> +
> +       base = dev_read_addr_ptr(dev_get_parent(dev));
> +       if (!base)
> +               return -EINVAL;
> +
> +       in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL);
> +       if (!in0_clk)
> +               return -ENOMEM;
> +
> +       ret = clk_get_by_index(dev, 0, in0_clk);
> +       if (ret)
> +               return ret;
> +       in0 = in0_clk->dev->name;
> +
> +       WRITE_ONCE(probed, true);
> +
> +       aclk_sels[0] = in0;
> +       pll2_sels[0] = in0;
> +
> +       /* Fixup registers to be absolute, rather than relative */
> +#define FIXUP_REGS(clocks) \
> +       for (i = 0; i < ARRAY_SIZE(clocks); i++) \
> +               clocks[i].reg += (ulong)base
> +       FIXUP_REGS(k210_clk_dividers);
> +       FIXUP_REGS(k210_clk_gates);
> +       FIXUP_REGS(k210_clk_muxes);
> +#undef FIXUP_REGS
> +       for (i = 0; i < ARRAY_SIZE(k210_clk_plls); i++) {
> +               k210_clk_plls[i].reg += (ulong)base;
> +               k210_clk_plls[i].lock += (ulong)base;
> +       }
> +
> +       /*
> +        * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we
> +        * need to manually reparent it whenever we configure pll0
> +        */
> +       k210_clk_bypass.alt = in0_clk;
> +       clk_dm(K210_CLK_PLL0,
> +              k210_register_bypass_struct("pll0", in0, &k210_clk_bypass));
> +       clk_dm(K210_CLK_PLL1,
> +              k210_register_pll_struct("pll1", in0, &k210_clk_plls[1]));
> +       /* PLL2 is muxed, so set up a composite clock */
> +       clk_dm(K210_CLK_PLL2,
> +              clk_register_composite_struct("pll2", pll2_sels,
> +                                            ARRAY_SIZE(pll2_sels),
> +                                            &k210_clk_comps[COMPIFY(K210_CLK_PLL2)]));
> +
> +       /* Half-frequency clocks for "even" dividers */
> +       k210_clk_half("in0_half", in0);
> +       k210_clk_half("pll0_half", "pll0");
> +       k210_clk_half("pll2_half", "pll2");
> +
> +       /* ACLK has no gate */
> +       clk_dm(K210_CLK_ACLK,
> +              clk_register_composite_struct("aclk", aclk_sels,
> +                                            ARRAY_SIZE(aclk_sels),
> +                                            &k210_clk_comps[COMPIFY(K210_CLK_ACLK)]));
> +
> +#define REGISTER_COMP(id, name) \
> +       clk_dm(id, clk_register_composite_struct(name, generic_sels, \
> +                                                ARRAY_SIZE(generic_sels), \
> +                                                &k210_clk_comps[COMPIFY(id)]))
> +       REGISTER_COMP(K210_CLK_SPI3,   "spi3");
> +       REGISTER_COMP(K210_CLK_TIMER0, "timer0");
> +       REGISTER_COMP(K210_CLK_TIMER1, "timer1");
> +       REGISTER_COMP(K210_CLK_TIMER2, "timer2");
> +#undef COMP
> +
> +       /* Dividing clocks, no mux */
> +#define REGISTER_COMP_NOMUX(id, name, _parent) do { \
> +       const char *parent = _parent; \
> +       clk_dm(id, \
> +              clk_register_composite_struct(name, &parent, 1, \
> +                                            &k210_clk_comps[COMPIFY(id)])); \
> +} while (false)
> +       REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0",  "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1",  "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_ROM,   "rom",    "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_DVP,   "dvp",    "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_APB0,  "apb0",   "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_APB1,  "apb1",   "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_APB2,  "apb2",   "aclk");
> +       REGISTER_COMP_NOMUX(K210_CLK_AI,    "ai",     "pll1");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2S0,  "i2s0",   "pll2_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2S1,  "i2s1",   "pll2_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2S2,  "i2s2",   "pll2_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_WDT0,  "wdt0",   "in0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_WDT1,  "wdt1",   "in0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_SPI0,  "spi0",   "pll0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_SPI1,  "spi1",   "pll0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_SPI2,  "spi2",   "pll0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2C0,  "i2c0",   "pll0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2C1,  "i2c1",   "pll0_half");
> +       REGISTER_COMP_NOMUX(K210_CLK_I2C2,  "i2c2",   "pll0_half");
> +#undef REGISTER_COMP_NOMUX
> +
> +       /* Dividing clocks */
> +#define REGISTER_DIV(id, name, parent) clk_dm(id, \
> +       clk_register_divider_struct(name, parent, \
> +                                   &k210_clk_dividers[DIVIFY(id)]))
> +       REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half");
> +       REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half");
> +       REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half");
> +#undef REGISTER_DIV
> +
> +       /* Gated clocks */
> +#define REGISTER_GATE(id, name, parent) \
> +       clk_dm(id, clk_register_gate_struct(name, parent, \
> +                                           &k210_clk_gates[GATEIFY(id)]))
> +       REGISTER_GATE(K210_CLK_CPU,   "cpu",    "aclk");
> +       REGISTER_GATE(K210_CLK_DMA,   "dma",    "aclk");
> +       REGISTER_GATE(K210_CLK_FFT,   "fft",    "aclk");
> +       REGISTER_GATE(K210_CLK_GPIO,  "gpio",   "apb0");
> +       REGISTER_GATE(K210_CLK_UART1, "uart1",  "apb0");
> +       REGISTER_GATE(K210_CLK_UART2, "uart2",  "apb0");
> +       REGISTER_GATE(K210_CLK_UART3, "uart3",  "apb0");
> +       REGISTER_GATE(K210_CLK_FPIOA, "fpioa",  "apb0");
> +       REGISTER_GATE(K210_CLK_SHA,   "sha",    "apb0");
> +       REGISTER_GATE(K210_CLK_AES,   "aes",    "apb1");
> +       REGISTER_GATE(K210_CLK_OTP,   "otp",    "apb1");
> +       REGISTER_GATE(K210_CLK_RTC,   "rtc",    in0);
> +#undef REGISTER_GATE
> +
> +       return 0;
> +}
> +
> +static const struct udevice_id k210_clk_ids[] = {
> +       { .compatible = "kendryte,k210-clk" },
> +       { },
> +};
> +
> +U_BOOT_DRIVER(k210_clk) = {
> +       .name = "k210_clk",
> +       .id = UCLASS_CLK,
> +       .of_match = k210_clk_ids,
> +       .ops = &k210_clk_ops,
> +       .probe = k210_clk_probe,
> +};
> diff --git a/include/dt-bindings/clock/k210-sysctl.h b/include/dt-bindings/clock/k210-sysctl.h
> new file mode 100644
> index 0000000000..16d67b282f
> --- /dev/null
> +++ b/include/dt-bindings/clock/k210-sysctl.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef CLOCK_K210_SYSCTL_H
> +#define CLOCK_K210_SYSCTL_H
> +
> +/*
> + * Arbitrary identifiers for clocks.
> + */
> +#define K210_CLK_NONE   0
> +#define K210_CLK_PLL0   1
> +#define K210_CLK_PLL1   2
> +#define K210_CLK_PLL2   3
> +#define K210_CLK_CPU    4
> +#define K210_CLK_SRAM0  5
> +#define K210_CLK_SRAM1  6
> +#define K210_CLK_APB0   7
> +#define K210_CLK_APB1   8
> +#define K210_CLK_APB2   9
> +#define K210_CLK_ROM    10
> +#define K210_CLK_DMA    11
> +#define K210_CLK_AI     12
> +#define K210_CLK_DVP    13
> +#define K210_CLK_FFT    14
> +#define K210_CLK_GPIO   15
> +#define K210_CLK_SPI0   16
> +#define K210_CLK_SPI1   17
> +#define K210_CLK_SPI2   18
> +#define K210_CLK_SPI3   19
> +#define K210_CLK_I2S0   20
> +#define K210_CLK_I2S1   21
> +#define K210_CLK_I2S2   22
> +#define K210_CLK_I2S0_M 23
> +#define K210_CLK_I2S1_M 24
> +#define K210_CLK_I2S2_M 25
> +#define K210_CLK_I2C0   26
> +#define K210_CLK_I2C1   27
> +#define K210_CLK_I2C2   28
> +#define K210_CLK_UART1  29
> +#define K210_CLK_UART2  30
> +#define K210_CLK_UART3  31
> +#define K210_CLK_AES    32
> +#define K210_CLK_FPIOA  33
> +#define K210_CLK_TIMER0 34
> +#define K210_CLK_TIMER1 35
> +#define K210_CLK_TIMER2 36
> +#define K210_CLK_WDT0   37
> +#define K210_CLK_WDT1   38
> +#define K210_CLK_SHA    39
> +#define K210_CLK_OTP    40
> +#define K210_CLK_RTC    41
> +#define K210_CLK_ACLK   42
> +
> +#endif /* CLOCK_K210_SYSCTL_H */
> diff --git a/include/dt-bindings/mfd/k210-sysctl.h b/include/dt-bindings/mfd/k210-sysctl.h
> new file mode 100644
> index 0000000000..e16d7302cd
> --- /dev/null
> +++ b/include/dt-bindings/mfd/k210-sysctl.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef K210_SYSCTL_H
> +#define K210_SYSCTL_H
> +
> +/* Taken from kendryte-standalone-sdk/lib/drivers/include/sysctl.h */
> +#define K210_SYSCTL_GIT_ID     0x00 /* Git short commit id */
> +#define K210_SYSCTL_CLK_FREQ   0x04 /* System clock base frequency */
> +#define K210_SYSCTL_PLL0       0x08 /* PLL0 controller */
> +#define K210_SYSCTL_PLL1       0x0C /* PLL1 controller */
> +#define K210_SYSCTL_PLL2       0x10 /* PLL2 controller */
> +#define K210_SYSCTL_PLL_LOCK   0x18 /* PLL lock tester */
> +#define K210_SYSCTL_ROM_ERROR  0x1C /* AXI ROM detector */
> +#define K210_SYSCTL_SEL0       0x20 /* Clock select controller0 */
> +#define K210_SYSCTL_SEL1       0x24 /* Clock select controller1 */
> +#define K210_SYSCTL_EN_CENT    0x28 /* Central clock enable */
> +#define K210_SYSCTL_EN_PERI    0x2C /* Peripheral clock enable */
> +#define K210_SYSCTL_SOFT_RESET 0x30 /* Soft reset ctrl */
> +#define K210_SYSCTL_PERI_RESET 0x34 /* Peripheral reset controller */
> +#define K210_SYSCTL_THR0       0x38 /* Clock threshold controller 0 */
> +#define K210_SYSCTL_THR1       0x3C /* Clock threshold controller 1 */
> +#define K210_SYSCTL_THR2       0x40 /* Clock threshold controller 2 */
> +#define K210_SYSCTL_THR3       0x44 /* Clock threshold controller 3 */
> +#define K210_SYSCTL_THR4       0x48 /* Clock threshold controller 4 */
> +#define K210_SYSCTL_THR5       0x4C /* Clock threshold controller 5 */
> +#define K210_SYSCTL_THR6       0x50 /* Clock threshold controller 6 */
> +#define K210_SYSCTL_MISC       0x54 /* Miscellaneous controller */
> +#define K210_SYSCTL_PERI       0x58 /* Peripheral controller */
> +#define K210_SYSCTL_SPI_SLEEP  0x5C /* SPI sleep controller */
> +#define K210_SYSCTL_RESET_STAT 0x60 /* Reset source status */
> +#define K210_SYSCTL_DMA_SEL0   0x64 /* DMA handshake selector */
> +#define K210_SYSCTL_DMA_SEL1   0x68 /* DMA handshake selector */
> +#define K210_SYSCTL_POWER_SEL  0x6C /* IO Power Mode Select controller */
> +
> +#endif /* K210_SYSCTL_H */
> diff --git a/include/kendryte/clk.h b/include/kendryte/clk.h
> new file mode 100644
> index 0000000000..9c6245d468
> --- /dev/null
> +++ b/include/kendryte/clk.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> + */
> +
> +#ifndef K210_CLK_H
> +#define K210_CLK_H
> +
> +#define LOG_CATEGORY UCLASS_CLK
> +#include <linux/types.h>
> +#include <linux/clk-provider.h>
> +
> +static inline struct clk *k210_clk_gate(const char *name,
> +                                       const char *parent_name,
> +                                       void __iomem *reg, u8 bit_idx)
> +{
> +       return clk_register_gate(NULL, name, parent_name, 0, reg, bit_idx, 0,
> +                                NULL);
> +}
> +
> +static inline struct clk *k210_clk_half(const char *name,
> +                                       const char *parent_name)
> +{
> +       return clk_register_fixed_factor(NULL, name, parent_name, 0, 1, 2);
> +}
> +
> +static inline struct clk *k210_clk_div(const char *name,
> +                                      const char *parent_name,
> +                                      void __iomem *reg, u8 shift, u8 width)
> +{
> +       return clk_register_divider(NULL, name, parent_name, 0, reg, shift,
> +                                   width, 0);
> +}
> +
> +#endif /* K210_CLK_H */
> --
> 2.25.0
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 33/33] riscv: Add Sipeed Maix support
  2020-03-04  6:04   ` Rick Chen
@ 2020-03-04  7:47     ` Rick Chen
  2020-03-04 15:11       ` Sean Anderson
  0 siblings, 1 reply; 69+ messages in thread
From: Rick Chen @ 2020-03-04  7:47 UTC (permalink / raw)
  To: u-boot

Hi Sean

This patch series become larger and larger from v1 with 11 patches to
v5 with 33 patches.
You shall just fix the suggestions from the previous version in the
next version.
Additional extra features and subsystem drivers that you want to
support, you shall send them individually instead of mixing them
together.

If you can separate them into different series(spi, gpio,led ,
pinctrl, watchdog, those drivers were not present in v1) and they will
not block the reviewing schedule each other.
Narrow down the dependency, it can help to speed up the patch work.
And I am happy to help pulling the RISC-V relative patchs via riscv
tree ASAP.

Thanks,
Rick



>
> > The Sipeed Maix series is a collection of boards built around the RISC-V
> > Kendryte K210 processor. This processor contains several peripherals to
> > accelerate neural network processing and other "ai" tasks. This includes a
> > "KPU" neural network processor, an audio processor supporting beamforming
> > reception, and a digital video port supporting capture and output at VGA
> > resolution. Other peripherals include 8M of sram (accessible with and
> > without caching); remappable pins, including 40 GPIOs; AES, FFT, and SHA256
> > accelerators; a DMA controller; and I2C, I2S, and SPI controllers. Maix
> > peripherals vary, but include spi flash; on-board usb-serial bridges; ports
> > for cameras, displays, and sd cards; and ESP32 chips. Currently, only the
> > Sipeed Maix Bit V2.0 (bitm) is supported, but the boards are fairly
> > similar.
> >
> > Documentation for Maix boards is located at
> > <http://dl.sipeed.com/MAIX/HDK/>.  Documentation for the Kendryte K210 is
> > located at <https://kendryte.com/downloads/>. However, hardware details are
> > rather lacking, so most technical reference has been taken from the
> > standalone sdk located at
> > <https://github.com/kendryte/kendryte-standalone-sdk>.
> >
> > Signed-off-by: Sean Anderson <seanga2@gmail.com>
> > ---
>
> Please check patch, it can not apply
>
>
> Applying: riscv: Add Sipeed Maix support
> .git/rebase-apply/patch:344: trailing whitespace.
>
> .git/rebase-apply/patch:346: trailing whitespace.
>
> .git/rebase-apply/patch:368: trailing whitespace.
>
> .git/rebase-apply/patch:370: trailing whitespace.
>
> .git/rebase-apply/patch:377: trailing whitespace.
>     Bytes Sent:2478208   BPS:72937
> error: patch failed: doc/board/index.rst:16
> error: doc/board/index.rst: patch does not apply
> Patch failed at 0001 riscv: Add Sipeed Maix support
>
> Thanks,
> Rick
>
> >
> > Changes in v5:
> > - Configure relocation location with CONFIG_SYS_SDRAM_*
> > - Enable ram clocks
> > - Add pinmux/gpio/led support
> > - Remove (broken) MMC support
> > - Store the environment in flash
> > - Add partitions
> > - Add bootcmd
> > - Add docs for pinctrl and booting
> >
> > Changes in v4:
> > - Rework documentation to be organized by board mfg not cpu mfg
> > - Update docs to reflect working SPI support
> > - Add proper spi support
> > - Don't define unneecessary macros in config.h
> > - Lower the default stack so it isn't clobbered on relocation
> > - Update MAINTAINERS
> > - Update copyright
> >
> > Changes in v3:
> > - Reorder to be last in the patch series
> > - Add documentation for the board
> > - Generate defconfig with "make savedefconfig"
> > - Update Kconfig to imply most features we need
> > - Update MAINTAINERS
> >
> > Changes in v2:
> > - Select CONFIG_SYS_RISCV_NOCOUNTER
> > - Imply CONFIG_CLK_K210
> > - Remove spurious references to CONFIG_ARCH_K210
> > - Remove many configs from defconfig where the defaults were fine
> > - Add a few "not set" lines to suppress unneeded defaults
> > - Reduce pre-reloc malloc space, now that clocks initialization happens
> >   later
> >
> >  arch/riscv/Kconfig                 |   4 +
> >  board/sipeed/maix/Kconfig          |  72 ++++++++++
> >  board/sipeed/maix/MAINTAINERS      |  11 ++
> >  board/sipeed/maix/Makefile         |   5 +
> >  board/sipeed/maix/maix.c           |  54 +++++++
> >  configs/sipeed_maix_bitm_defconfig |  16 +++
> >  doc/board/index.rst                |   1 +
> >  doc/board/sipeed/index.rst         |   9 ++
> >  doc/board/sipeed/maix.rst          | 223 +++++++++++++++++++++++++++++
> >  include/configs/sipeed-maix.h      |  24 ++++
> >  10 files changed, 419 insertions(+)
> >  create mode 100644 board/sipeed/maix/Kconfig
> >  create mode 100644 board/sipeed/maix/MAINTAINERS
> >  create mode 100644 board/sipeed/maix/Makefile
> >  create mode 100644 board/sipeed/maix/maix.c
> >  create mode 100644 configs/sipeed_maix_bitm_defconfig
> >  create mode 100644 doc/board/sipeed/index.rst
> >  create mode 100644 doc/board/sipeed/maix.rst
> >  create mode 100644 include/configs/sipeed-maix.h
> >
> > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> > index b7a5757584..d016dd75d7 100644
> > --- a/arch/riscv/Kconfig
> > +++ b/arch/riscv/Kconfig
> > @@ -20,6 +20,9 @@ config TARGET_QEMU_VIRT
> >  config TARGET_SIFIVE_FU540
> >         bool "Support SiFive FU540 Board"
> >
> > +config TARGET_SIPEED_MAIX
> > +       bool "Support Sipeed Maix Board"
> > +
> >  endchoice
> >
> >  config SYS_ICACHE_OFF
> > @@ -53,6 +56,7 @@ source "board/AndesTech/ax25-ae350/Kconfig"
> >  source "board/emulation/qemu-riscv/Kconfig"
> >  source "board/microchip/mpfs_icicle/Kconfig"
> >  source "board/sifive/fu540/Kconfig"
> > +source "board/sipeed/maix/Kconfig"
> >
> >  # platform-specific options below
> >  source "arch/riscv/cpu/ax25/Kconfig"
> > diff --git a/board/sipeed/maix/Kconfig b/board/sipeed/maix/Kconfig
> > new file mode 100644
> > index 0000000000..939eb4829a
> > --- /dev/null
> > +++ b/board/sipeed/maix/Kconfig
> > @@ -0,0 +1,72 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +# Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> > +
> > +if TARGET_SIPEED_MAIX
> > +
> > +config SYS_BOARD
> > +       default "maix"
> > +
> > +config SYS_VENDOR
> > +       default "sipeed"
> > +
> > +config SYS_CPU
> > +       default "generic"
> > +
> > +config SYS_CONFIG_NAME
> > +       default "sipeed-maix"
> > +
> > +config SYS_TEXT_BASE
> > +       default 0x80000000
> > +
> > +config DEFAULT_DEVICE_TREE
> > +       default "k210-maix-bit"
> > +
> > +config NR_CPUS
> > +       default 2
> > +
> > +config NR_DRAM_BANKS
> > +       default 3
> > +
> > +config SF_DEFAULT_BUS
> > +       default 3
> > +
> > +config BOARD_SPECIFIC_OPTIONS
> > +       def_bool y
> > +       select GENERIC_RISCV
> > +       select RISCV_PRIV_1_9
> > +       imply SMP
> > +       imply OF_BOARD_SETUP
> > +       imply DM_SERIAL
> > +       imply SIFIVE_SERIAL
> > +       imply SIFIVE_CLINT
> > +       imply POWER_DOMAIN
> > +       imply SIMPLE_PM_BUS
> > +       imply CLK_CCF
> > +       imply CLK_COMPOSITE_CCF
> > +       imply CLK_K210
> > +       imply DM_RESET
> > +       imply RESET_SYSCON
> > +       imply SYSRESET
> > +       imply SYSRESET_SYSCON
> > +       imply PINCTRL
> > +       imply PINCONF
> > +       imply PINCTRL_K210
> > +       imply DM_GPIO
> > +       imply DWAPB_GPIO
> > +       imply SIFIVE_GPIO
> > +       imply CMD_GPIO
> > +       imply LED
> > +       imply LED_GPIO
> > +       imply SPI
> > +       imply DESIGNWARE_SPI
> > +       imply SPI_FLASH_WINBOND
> > +       imply DM_MTD
> > +       imply SPI_FLASH_MTD
> > +       imply CMD_MTD
> > +       imply ENV_IS_IN_SPI_FLASH
> > +       imply MMC
> > +       imply MMC_BROKEN_CD
> > +       imply MMC_SPI
> > +       imply WDT
> > +       imply DESIGNWARE_WATCHDOG
> > +endif
> > diff --git a/board/sipeed/maix/MAINTAINERS b/board/sipeed/maix/MAINTAINERS
> > new file mode 100644
> > index 0000000000..1f33882e1e
> > --- /dev/null
> > +++ b/board/sipeed/maix/MAINTAINERS
> > @@ -0,0 +1,11 @@
> > +Sipeed Maix BOARD
> > +M:     Sean Anderson <seanga2@gmail.com>
> > +S:     Maintained
> > +F:     arch/riscv/dts/k210.dtsi
> > +F:     arch/riscv/dts/k210-maix-bit.dts
> > +F:     board/sipeed/maix/
> > +F:     configs/sipeed_maix_defconfig
> > +F:     doc/board/sipeed/
> > +F:     include/configs/sipeed-maix.h
> > +F:     include/dt-bindings/*/k210-sysctl.h
> > +F:     test/dm/k210_pll.c
> > diff --git a/board/sipeed/maix/Makefile b/board/sipeed/maix/Makefile
> > new file mode 100644
> > index 0000000000..4acff5b31e
> > --- /dev/null
> > +++ b/board/sipeed/maix/Makefile
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +#
> > +# Copyright (c) 2019 Western Digital Corporation or its affiliates.
> > +
> > +obj-y += maix.o
> > diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c
> > new file mode 100644
> > index 0000000000..c126cb5d67
> > --- /dev/null
> > +++ b/board/sipeed/maix/maix.c
> > @@ -0,0 +1,54 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
> > + */
> > +
> > +#include <common.h>
> > +#include <clk.h>
> > +#include <dm.h>
> > +#include <fdt_support.h>
> > +
> > +phys_size_t get_effective_memsize(void)
> > +{
> > +       return CONFIG_SYS_SDRAM_SIZE;
> > +}
> > +
> > +int board_init(void)
> > +{
> > +       int ret;
> > +       ofnode bank = ofnode_null();
> > +
> > +       /* Enable RAM clocks */
> > +       while (true) {
> > +               struct clk clk;
> > +
> > +               bank = ofnode_by_prop_value(bank, "device_type", "memory",
> > +                                           sizeof("memory"));
> > +               if (ofnode_equal(bank, ofnode_null()))
> > +                       break;
> > +
> > +               ret = clk_get_by_index_nodev(bank, 0, &clk);
> > +               if (ret)
> > +                       continue;
> > +
> > +               ret = clk_enable(&clk);
> > +               clk_free(&clk);
> > +               if (ret)
> > +                       return ret;
> > +       }
> > +       return 0;
> > +}
> > +
> > +int ft_board_setup(void *blob, bd_t *bd)
> > +{
> > +       int i;
> > +       u64 base[CONFIG_NR_DRAM_BANKS];
> > +       u64 size[CONFIG_NR_DRAM_BANKS];
> > +
> > +       for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> > +               base[i] = bd->bi_dram[i].start;
> > +               size[i] = bd->bi_dram[i].size;
> > +       }
> > +
> > +       return fdt_fixup_memory_banks(blob, base, size, CONFIG_NR_DRAM_BANKS);
> > +}
> > diff --git a/configs/sipeed_maix_bitm_defconfig b/configs/sipeed_maix_bitm_defconfig
> > new file mode 100644
> > index 0000000000..7f644e7a37
> > --- /dev/null
> > +++ b/configs/sipeed_maix_bitm_defconfig
> > @@ -0,0 +1,16 @@
> > +CONFIG_RISCV=y
> > +CONFIG_ENV_SIZE=0x2000
> > +CONFIG_ENV_SECT_SIZE=0x1000
> > +CONFIG_ENV_OFFSET=0x7C000
> > +CONFIG_ENV_OFFSET_REDUND=0x7E000
> > +CONFIG_TARGET_SIPEED_MAIX=y
> > +CONFIG_ARCH_RV64I=y
> > +CONFIG_USE_BOOTCOMMAND=y
> > +CONFIG_BOOTCOMMAND="sf probe;mtd read kernel 80000000;go 80000000"
> > +CONFIG_MTDIDS_DEFAULT="nor0=spi3.0"
> > +CONFIG_MTDPARTS_DEFAULT="spi3.0:496k(u-boot),16k(env),5632k(kernel),10240k(data)"
> > +CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
> > +# CONFIG_NET is not set
> > +# CONFIG_INPUT is not set
> > +# CONFIG_DM_ETH is not set
> > +# CONFIG_EFI_LOADER is not set
> > diff --git a/doc/board/index.rst b/doc/board/index.rst
> > index b8b956d730..54cbc5c874 100644
> > --- a/doc/board/index.rst
> > +++ b/doc/board/index.rst
> > @@ -16,4 +16,5 @@ Board-specific doc
> >     renesas/index
> >     rockchip/index
> >     sifive/index
> > +   sipeed/index
> >     xilinx/index
> > diff --git a/doc/board/sipeed/index.rst b/doc/board/sipeed/index.rst
> > new file mode 100644
> > index 0000000000..92da47fddd
> > --- /dev/null
> > +++ b/doc/board/sipeed/index.rst
> > @@ -0,0 +1,9 @@
> > +.. SPDX-License-Identifier: GPL-2.0+
> > +
> > +Kendryte
> > +========
> > +
> > +.. toctree::
> > +   :maxdepth: 2
> > +
> > +   maix
> > diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst
> > new file mode 100644
> > index 0000000000..c5c2e24ea5
> > --- /dev/null
> > +++ b/doc/board/sipeed/maix.rst
> > @@ -0,0 +1,223 @@
> > +.. SPDX-License-Identifier: GPL-2.0+
> > +.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
> > +
> > +Maix Bit
> > +========
> > +
> > +Several of the Sipeed Maix series of boards cotain the Kendryte K210 processor,
> > +a 64-bit RISC-V CPU. This processor contains several peripherals to accelerate
> > +neural network processing and other "ai" tasks. This includes a "KPU" neural
> > +network processor, an audio processor supporting beamforming reception, and a
> > +digital video port supporting capture and output at VGA resolution. Other
> > +peripherals include 8M of SRAM (accessible with and without caching); remappable
> > +pins, including 40 GPIOs; AES, FFT, and SHA256 accelerators; a DMA controller;
> > +and I2C, I2S, and SPI controllers. Maix peripherals vary, but include spi flash;
> > +on-board usb-serial bridges; ports for cameras, displays, and sd cards; and
> > +ESP32 chips. Currently, only the Sipeed Maix Bit V2.0 (bitm) is supported, but
> > +the boards are fairly similar.
> > +
> > +Documentation for Maix boards is available from
> > +`Sipeed's website <http://dl.sipeed.com/MAIX/HDK/>`_.
> > +Documentation for the Kendryte K210 is available from
> > +`Kendryte's website <https://kendryte.com/downloads/>`_. However, hardware
> > +details are rather lacking, so most technical reference has been taken from the
> > +`standalone sdk <https://github.com/kendryte/kendryte-standalone-sdk>`_.
> > +
> > +Build and boot steps
> > +--------------------
> > +
> > +To build u-boot, run
> > +
> > +.. code-block:: none
> > +
> > +    make sipeed_maix_bitm_defconfig
> > +    make CROSS_COMPILE=<your cross compile prefix>
> > +
> > +To flash u-boot to a maix bit, run
> > +
> > +.. code-block:: none
> > +
> > +    kflash -tp /dev/<your tty here> -B bit_mic u-boot-dtb.bin
> > +
> > +Boot output should look like the following:
> > +
> > +.. code-block:: none
> > +
> > +
> > +    U-Boot 2020.04-rc2-00087-g2221cc09c1-dirty (Feb 28 2020 - 13:53:09 -0500)
> > +
> > +    DRAM:  8 MiB
> > +    WDT:   Started with servicing (60s timeout)
> > +    MMC:   spi at 53000000:slot at 0: 0
> > +    In:    serial at 38000000
> > +    Out:   serial at 38000000
> > +    Err:   serial at 38000000
> > +    Hit any key to stop autoboot:  0
> > +    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
> > +    Reading 5242880 byte(s) at offset 0x00000000
> > +    ## Starting application at 0x80000000 ...
> > +
> > +Flashing Images
> > +---------------
> > +
> > +To flash a kernel, transfer it over serial, then write it to the kernel
> > +partition.
> > +
> > +.. code-block:: none
> > +
> > +    => loady 80000000 1500000
> > +    ## Switch baudrate to 1500000 bps and press ENTER ...
> > +
> > +    *** baud: 1500000
> > +
> > +    *** baud: 1500000 ***
> > +    ## Ready for binary (ymodem) download to 0x80000000 at 1500000 bps...
> > +    C
> > +    *** file: loader.bin
> > +    $ sz -vv loader.bin
> > +    Sending: loader.bin
> > +    Bytes Sent:2478208   BPS:72937
> > +    Sending:
> > +    Ymodem sectors/kbytes sent:   0/ 0k
> > +    Transfer complete
> > +
> > +    *** exit status: 0 ***
> > +    ## Total Size      = 0x0025d052 = 2478162 Bytes
> > +    ## Switch baudrate to 115200 bps and press ESC ...
> > +
> > +    *** baud: 115200
> > +
> > +    *** baud: 115200 ***
> > +    => sf probe
> > +    SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
> > +    => mtd write kernel 80000000 0 25d052
> > +    Writing 2478162 byte(s) at offset 0x00000000
> > +
> > +**NB:** kflash adds a 5-byte header to payloads (and a 32-byte trailer) to all
> > +payloads it flashes. If you use kflash to flash your payload, you will need to
> > +account for this header when specifying what offset in spi flash to load from.
> > +
> > +Partition Scheme
> > +^^^^^^^^^^^^^^^^
> > +
> > +There is no partition scheme specified by the manufacturer. The only requirement
> > +imposed by the firmware is that offset 0 will be loaded and ran. The default
> > +partition scheme is
> > +
> > +========= ======== ======
> > +Partition Offset   Size
> > +========= ======== ======
> > +u-boot    0x000000 496k
> > +env       0x07C000 16k
> > +kernel    0x080000 5M
> > +data      0x580000 10.5M
> > +========= ======== ======
> > +
> > +Pin Assignment
> > +--------------
> > +
> > +The K210 contains a Fully Programmable I/O Array (FPIOA), which can remap any of
> > +its 256 input functions to any any of 48 output pins. The following table has
> > +the default pin assignments for the BitM.
> > +
> > +===== ========== =======
> > +Pin   Function   Comment
> > +===== ========== =======
> > +IO_0  JTAG_TCLK
> > +IO_1  JTAG_TDI
> > +IO_2  JTAG_TMS
> > +IO_3  JTAG_TDO
> > +IO_4  UARTHS_RX
> > +IO_5  UARTHS_TX
> > +IO_6  GPIOHS_1
> > +IO_7  GPIOHS_2
> > +IO_8  GPIO_0
> > +IO_9  GPIO_1
> > +IO_10 GPIO_2
> > +IO_11 GPIO_3
> > +IO_12 GPIO_4     Green LED
> > +IO_13 GPIO_5     Red LED
> > +IO_14 GPIO_6     Blue LED
> > +IO_15 GPIO_7
> > +IO_16 GPIOHS_0   ISP
> > +IO_17 GPIOHS_3
> > +IO_18 I2S0_SCLK  MIC CLK
> > +IO_19 I2S0_WS    MIC WS
> > +IO_20 I2S0_IN_D0 MIC SD
> > +IO_21 GPIOHS_4
> > +IO_22 GPIOHS_5
> > +IO_23 GPIOHS_6
> > +IO_24 GPIOHS_7
> > +IO_25 GPIOHS_8
> > +IO_26 SPI1_D1    MMC MISO
> > +IO_27 SPI1_SCLK  MMC CLK
> > +IO_28 SPI1_D0    MMC MOSI
> > +IO_29 GPIOHS_31  MMC CS
> > +IO_30 GPIOHS_9
> > +IO_31 GPIOHS_10
> > +IO_32 GPIOHS_11
> > +IO_33 GPIOHS_12
> > +IO_34 GPIOHS_13
> > +IO_35 GPIOHS_14
> > +IO_36 GPIOHS_28  Panel CS
> > +IO_37 GPIOHS_29  Panel RST
> > +IO_38 GPIOHS_30  Panel DC
> > +IO_39 SPI0_SCK   Panel WR
> > +IO_40 SCCP_SDA
> > +IO_41 SCCP_SCLK
> > +IO_42 DVP_RST
> > +IO_43 DVP_VSYNC
> > +IO_44 DVP_PWDN
> > +IO_45 DVP_HSYNC
> > +IO_46 DVP_XCLK
> > +IO_47 DVP_PCLK
> > +===== ========== =======
> > +
> > +Over- and Under-clocking
> > +------------------------
> > +
> > +To change the clock speed of the K210, you will need to enable
> > +``CONFIG_CLK_K210_SET_RATE`` and edit the board's device tree. To do this, add a
> > +section to ``arch/riscv/arch/riscv/dts/k210-maix-bit.dts`` like the following:
> > +
> > +.. code-block:: dts
> > +
> > +    &sysclk {
> > +       assigned-clocks = <&sysclk K210_CLK_PLL0>;
> > +       assigned-clock-rates = <800000000>;
> > +    };
> > +
> > +There are three PLLs on the K210: PLL0 is the parent of most of the components,
> > +including the CPU and RAM. PLL1 is the parent of the neural network coprocessor.
> > +PLL2 is the parent of the sound processing devices. Note that child clocks of
> > +PLL0 and PLL2 run at *half* the speed of the PLLs. For example, if PLL0 is
> > +running at 800 MHz, then the CPU will run at 400 MHz. This is the example given
> > +above. The CPU can be overclocked to around 600 MHz, and underclocked to 26 MHz.
> > +
> > +It is possible to set PLL2's parent to PLL0. The plls are more accurate when
> > +converting between similar frequencies. This makes it easier to get an accurate
> > +frequency for I2S. As an example, consider sampling an I2S device at 44.1 kHz.
> > +On this device, the I2S serial clock runs at 64 times the sample rate.
> > +Therefore, we would like to run PLL2 at an even multiple of 2.8224 MHz. If
> > +PLL2's parent is IN0, we could use a frequency of 390 MHz (the same as the CPU's
> > +default speed).  Dividing by 138 yields a serial clock of about 2.8261 MHz. This
> > +results in a sample rate of 44.158 kHz---around 50 Hz or .1% too fast. If,
> > +instead, we set PLL2's parent to PLL1 running at 390 MHz, and request a rate of
> > +2.8224 * 136 = 383.8464 MHz, the achieved rate is 383.90625 MHz. Dividing by 136
> > +yields a serial clock of about 2.8228 MHz. This results in a sample rate of
> > +44.107 kHz---just 7 Hz or .02% too fast. This configuration is shown in the
> > +following example:
> > +
> > +.. code-block:: dts
> > +
> > +    &sysclk {
> > +       assigned-clocks = <&sysclk K210_CLK_PLL1>, <&sysclk K210_CLK_PLL2>;
> > +       assigned-clock-parents = <0>, <&sysclk K210_CLK_PLL1>;
> > +       assigned-clock-rates = <390000000>, <383846400>;
> > +    };
> > +
> > +There are a couple of quirks to the PLLs. First, there are more frequency ratios
> > +just above and below 1.0, but there is a small gap around 1.0. To be explicit,
> > +if the input frequency is 100 MHz, it would be impossible to have an output of
> > +99 or 101 MHz. In addition, there is a maximum frequency for the internal VCO,
> > +so higher input/output frequencies will be less accurate than lower ones.
> > diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h
> > new file mode 100644
> > index 0000000000..a46473fc78
> > --- /dev/null
> > +++ b/include/configs/sipeed-maix.h
> > @@ -0,0 +1,24 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
> > + */
> > +
> > +#ifndef CONFIGS_SIPEED_MAIX_H
> > +#define CONFIGS_SIPEED_MAIX_H
> > +
> > +#include <linux/sizes.h>
> > +
> > +#define CONFIG_SYS_LOAD_ADDR 0x80000000
> > +/* Start just below the second bank so we don't clobber it during reloc */
> > +#define CONFIG_SYS_INIT_SP_ADDR 0x803FFFFF
> > +#define CONFIG_SYS_MALLOC_LEN SZ_128K
> > +#define CONFIG_SYS_CACHELINE_SIZE 64
> > +
> > +#define CONFIG_SYS_SDRAM_BASE 0x80000000
> > +/* Don't relocate into AI ram since it isn't set up yet */
> > +#define CONFIG_SYS_SDRAM_SIZE (SZ_4M + SZ_2M)
> > +
> > +/* For early init */
> > +#define K210_SYSCTL_BASE 0x50440000
> > +
> > +#endif /* CONFIGS_SIPEED_MAIX_H */
> > --
> > 2.25.0
> >

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 07/33] clk: Add K210 clock support
  2020-03-04  6:58   ` Rick Chen
@ 2020-03-04 14:54     ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-03-04 14:54 UTC (permalink / raw)
  To: u-boot

On 3/4/20 1:58 AM, Rick Chen wrote:
> Hi Sean
> 
>> Due to the large number of clocks, I decided to use the CCF. The overall
>> structure is modeled after the imx code. Clocks are stored in several
>> arrays.  There are some translation macros (FOOIFY()) which allow for more
>> dense packing.  A possible improvement could be to only store the
>> parameters we need, instead of the whole CCF struct.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> ---
> 
> Please checkpatch and fix
> total: 4 errors, 4 warnings, 18 checks, 662 lines checked
> 
> Thanks
> Rick
> 

Here is the output of checkpatch

> drivers/clk/kendryte/clk.c:82: warning: static const char * array should probably be static const char * const
> drivers/clk/kendryte/clk.c:83: warning: static const char * array should probably be static const char * const

These arrays can't have both consts because it needs to have the actual
name of the in0 clock added.

> drivers/clk/kendryte/clk.c:122: check: Please use a blank line after function/struct/union/enum declarations

This is due to using macros in the style

#define FOO_LIST \
	FOO(bar) \
	FOO(baz)

#define FOO(x) FOO_##x,
enum foo_ids {
	FOO_LIST
};
#undef FOO

I think sticking the undefinition of FOO immediately after the closing
enum bracket makes it clearer that FOO is only used with that definition
during the declaration of that enum. It is closing the scope, so to
speak. If you'd like I can add a newline after enums declared this way.

> drivers/clk/kendryte/clk.c:124: error: space prohibited before open square bracket '['

This is due to macros declared like

#define FOO(x) [FOO_##x] = { \
	.y = (x), \
}

Where there is clearly a space before the [, but it is necessary for the
macro. I could declare it like

#define FOO(X) \
	[FOO_##x] = { \
		.y = (x), \
	}

but I think that the former style is more elegant. However, this can
also be changed if needed.

> drivers/clk/kendryte/clk.c:133: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:180: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:182: error: space prohibited before open square bracket '['
> drivers/clk/kendryte/clk.c:189: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:208: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:210: error: space prohibited before open square bracket '['
> drivers/clk/kendryte/clk.c:210: check: Macro argument reuse 'parents' - possible side-effects?

No possible side-effects here, since this macro argument doesn't make
sense unless it is a compile-time constant.

> drivers/clk/kendryte/clk.c:220: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:230: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:235: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:241: check: Macro argument reuse 'id' - possible side-effects?
> drivers/clk/kendryte/clk.c:249: check: Macro argument reuse 'id' - possible side-effects?
> drivers/clk/kendryte/clk.c:292: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:306: check: Please use a blank line after function/struct/union/enum declarations
> drivers/clk/kendryte/clk.c:329: error: do not initialise statics to false
> drivers/clk/kendryte/clk.c:361: check: Macro argument reuse 'clocks' - possible side-effects?
> drivers/clk/kendryte/clk.c:386: warning: line over 80 characters
> drivers/clk/kendryte/clk.c:397: warning: line over 80 characters

Unfortunately, I don't see any way to keep these two lines under 80
characters without seriously sacrificing readability. For reference, the
lines look like

	clk_dm(K210_CLK_PLL2,
	       clk_register_composite_struct("pll2", pll2_sels,
					     ARRAY_SIZE(pll2_sels),
					     &k210_clk_comps[COMPIFY(K210_CLK_PLL2)]));

The only way to further reduce the length would be to split the array
access over two lines, which I think harms readability too much.

> drivers/clk/kendryte/clk.c:399: check: Macro argument reuse 'id' - possible side-effects?
> drivers/clk/kendryte/clk.c:410: check: Macro argument reuse 'id' - possible side-effects?
> drivers/clk/kendryte/clk.c:438: check: Macro argument reuse 'id' - possible side-effects?
> drivers/clk/kendryte/clk.c:447: check: Macro argument reuse 'id' - possible side-effects?
> <unknown>:0: warning: DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.txt
> <unknown>:0: warning: DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.txt

AFAIK U-Boot has no such policy.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA
  2020-03-04  6:47   ` Rick Chen
@ 2020-03-04 15:00     ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-03-04 15:00 UTC (permalink / raw)
  To: u-boot

On 3/4/20 1:47 AM, Rick Chen wrote:
> Hi Sean
> 
>> The Fully-Programmable Input/Output Array (FPIOA) device controls pin
>> multiplexing on the K210. The FPIOA can remap any supported function to any
>> multifunctional IO pin. It can also perform basic GPIO functions, such as
>> reading the current value of a pin.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> ---
>>
>> Changes in v5:
>> - New
>>
>>  MAINTAINERS                                   |   2 +
>>  .../pinctrl/kendryte,k210-fpioa.txt           | 116 +++
>>  drivers/pinctrl/Kconfig                       |   1 +
>>  drivers/pinctrl/Makefile                      |   1 +
>>  drivers/pinctrl/kendryte/Kconfig              |   7 +
>>  drivers/pinctrl/kendryte/Makefile             |   1 +
>>  drivers/pinctrl/kendryte/pinctrl.c            | 663 ++++++++++++++++++
>>  drivers/pinctrl/kendryte/pinctrl.h            | 325 +++++++++
>>  include/dt-bindings/pinctrl/k210-pinctrl.h    |  12 +
>>  9 files changed, 1128 insertions(+)
>>  create mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt
>>  create mode 100644 drivers/pinctrl/kendryte/Kconfig
>>  create mode 100644 drivers/pinctrl/kendryte/Makefile
>>  create mode 100644 drivers/pinctrl/kendryte/pinctrl.c
>>  create mode 100644 drivers/pinctrl/kendryte/pinctrl.h
>>  create mode 100644 include/dt-bindings/pinctrl/k210-pinctrl.h
>>
> 
> Please checkpatch and fix
> 
> total: 3 errors, 13 warnings, 5 checks, 1147 lines checked
> 
> Thanks
> Rick

Here is the output of checkpatch.

drivers/pinctrl/kendryte/pinctrl.c:25: error: space prohibited before open square bracket '['

This is from using macros in the style

#define FOO(x) [FOO_##x] = {\
	...
}

I think this is more elegant than putting the [] on a separate line,
but it can of course be changed.

drivers/pinctrl/kendryte/pinctrl.c:76: check: Please use a blank line after function/struct/union/enum declarations

This is from using "local" macros, e.g.

#define FOO(x) FOO_##x
enum foo {
	FOO(bar),
	FOO(baz)
};
#undef FOO

I think keeping the undefinition of FOO close to where it is last used
aids readability. This can of course be changed (perhaps by moving the
(un)definition inside the enum).

drivers/pinctrl/kendryte/pinctrl.c:83: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:106: warning: Prefer 'unsigned int' to bare use of 'unsigned'

All these warnings are due to following the function definitions as
defined in pinctrl_ops.

drivers/pinctrl/kendryte/pinctrl.c:122: error: space prohibited before open square bracket '['
drivers/pinctrl/kendryte/pinctrl.c:133: check: Please use a blank line after function/struct/union/enum declarations
drivers/pinctrl/kendryte/pinctrl.c:141: error: space prohibited before open square bracket '['
drivers/pinctrl/kendryte/pinctrl.c:145: check: Please use a blank line after function/struct/union/enum declarations
drivers/pinctrl/kendryte/pinctrl.c:403: check: Please use a blank line after function/struct/union/enum declarations
drivers/pinctrl/kendryte/pinctrl.c:411: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:416: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:417: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:442: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:453: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:454: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:454: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:529: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:529: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:530: warning: Prefer 'unsigned int' to bare use of 'unsigned'
drivers/pinctrl/kendryte/pinctrl.c:612: warning: ENOSYS means 'invalid syscall nr' and nothing else
drivers/pinctrl/kendryte/pinctrl.h:281: check: Prefer using the BIT macro

This is from the line

#define K210_PC_DRIVE_1 (1 << K210_PC_DRIVE_SHIFT)

Where there are several succeeding and preceding lines which have
different values instead of 1.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1
  2020-03-04  6:15   ` Rick Chen
@ 2020-03-04 15:01     ` Sean Anderson
  0 siblings, 0 replies; 69+ messages in thread
From: Sean Anderson @ 2020-03-04 15:01 UTC (permalink / raw)
  To: u-boot

On 3/4/20 1:15 AM, Rick Chen wrote:
> Hi Sean
> 
>> Some devices have different layouts for the fields in CTRL1 (e.g. the
> 
> Still not fix this typo in commit message
> CTRL1 -> CTRL0
> 
> Thanks,
> Rick

Ah, whoops. I think I fixed it, the forgot I had done so, and changed it
again. Hopefully I can get this straight.

--Sean

> 
>> Kendryte K210). Allow this layout to be configurable from the device tree.
>> The documentation has been taken from Linux.
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>> Changes in v4:
>> - New
>>
>>  .../spi/snps,dw-apb-ssi.txt                   | 43 +++++++++++++++++++
>>  drivers/spi/designware_spi.c                  | 40 ++++++++++-------
>>  2 files changed, 68 insertions(+), 15 deletions(-)
>>  create mode 100644 doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
>>
>> diff --git a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
>> new file mode 100644
>> index 0000000000..4b6152f6b7
>> --- /dev/null
>> +++ b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt
>> @@ -0,0 +1,43 @@
>> +Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
>> +
>> +Required properties:
>> +- compatible : "snps,dw-apb-ssi"
>> +- reg : The register base for the controller. For "mscc,<soc>-spi", a second
>> +  register set is required (named ICPU_CFG:SPI_MST)
>> +- #address-cells : <1>, as required by generic SPI binding.
>> +- #size-cells : <0>, also as required by generic SPI binding.
>> +- clocks : phandles for the clocks, see the description of clock-names below.
>> +   The phandle for the "ssi_clk" is required. The phandle for the "pclk" clock
>> +   is optional. If a single clock is specified but no clock-name, it is the
>> +   "ssi_clk" clock. If both clocks are listed, the "ssi_clk" must be first.
>> +
>> +Optional properties:
>> +- clock-names : Contains the names of the clocks:
>> +    "ssi_clk", for the core clock used to generate the external SPI clock.
>> +    "pclk", the interface clock, required for register access.
>> +- cs-gpios : Specifies the gpio pins to be used for chipselects.
>> +- num-cs : The number of chipselects. If omitted, this will default to 4.
>> +- reg-io-width : The I/O register width (in bytes) implemented by this
>> +  device.  Supported values are 2 or 4 (the default).
>> +- snps,dfs-offset The offset in bits of the DFS field in CTRL0, defaulting to 0
>> +- snps,frf-offset The offset in bits of the FRF field in CTRL0, defaulting to 4
>> +- snps,tmod-offset The offset in bits of the tmode field in CTRL0, defaulting
>> +  to 6
>> +- snps,mode-offset The offset in bits of the work mode field in CTRL0,
>> +  defaulting to 8
>> +
>> +Child nodes as per the generic SPI binding.
>> +
>> +Example:
>> +
>> +       spi at fff00000 {
>> +               compatible = "snps,dw-apb-ssi";
>> +               reg = <0xfff00000 0x1000>;
>> +               interrupts = <0 154 4>;
>> +               #address-cells = <1>;
>> +               #size-cells = <0>;
>> +               clocks = <&spi_m_clk>;
>> +               num-cs = <2>;
>> +               cs-gpios = <&gpio0 13 0>,
>> +                          <&gpio0 14 0>;
>> +       };
>> diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
>> index 2dc16736a3..6e1c289297 100644
>> --- a/drivers/spi/designware_spi.c
>> +++ b/drivers/spi/designware_spi.c
>> @@ -3,6 +3,7 @@
>>   * Designware master SPI core controller driver
>>   *
>>   * Copyright (C) 2014 Stefan Roese <sr@denx.de>
>> + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
>>   *
>>   * Very loosely based on the Linux driver:
>>   * drivers/spi/spi-dw.c, which is:
>> @@ -51,20 +52,14 @@
>>  #define DW_SPI_DR                      0x60
>>
>>  /* Bit fields in CTRLR0 */
>> -#define SPI_DFS_OFFSET                 0
>> -
>> -#define SPI_FRF_OFFSET                 4
>>  #define SPI_FRF_SPI                    0x0
>>  #define SPI_FRF_SSP                    0x1
>>  #define SPI_FRF_MICROWIRE              0x2
>>  #define SPI_FRF_RESV                   0x3
>>
>> -#define SPI_MODE_OFFSET                        6
>> -#define SPI_SCPH_OFFSET                        6
>> -#define SPI_SCOL_OFFSET                        7
>> +#define SPI_MODE_SCPH                  0x1
>> +#define SPI_MODE_SCOL                  0x2
>>
>> -#define SPI_TMOD_OFFSET                        8
>> -#define SPI_TMOD_MASK                  (0x3 << SPI_TMOD_OFFSET)
>>  #define        SPI_TMOD_TR                     0x0             /* xmit & recv */
>>  #define SPI_TMOD_TO                    0x1             /* xmit only */
>>  #define SPI_TMOD_RO                    0x2             /* recv only */
>> @@ -89,6 +84,12 @@
>>  struct dw_spi_platdata {
>>         s32 frequency;          /* Default clock frequency, -1 for none */
>>         void __iomem *regs;
>> +
>> +       /* Offsets in CTRL0 */
>> +       u8 dfs_off;
>> +       u8 frf_off;
>> +       u8 tmod_off;
>> +       u8 mode_off;
>>  };
>>
>>  struct dw_spi_priv {
>> @@ -115,6 +116,15 @@ struct dw_spi_priv {
>>         struct reset_ctl_bulk   resets;
>>  };
>>
>> +static inline u32 GEN_CTRL0(struct dw_spi_priv *priv,
>> +                           struct dw_spi_platdata *plat)
>> +{
>> +       return ((priv->bits_per_word - 1) << plat->dfs_off |
>> +             (priv->type << plat->frf_off) |
>> +             (priv->mode << plat->mode_off) |
>> +             (priv->tmode << plat->tmod_off));
>> +}
>> +
>>  static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset)
>>  {
>>         return __raw_readl(priv->regs + offset);
>> @@ -160,6 +170,10 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus)
>>         /* Use 500KHz as a suitable default */
>>         plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
>>                                                500000);
>> +       plat->dfs_off = dev_read_u32_default(bus, "snps,dfs-offset", 0);
>> +       plat->frf_off = dev_read_u32_default(bus, "snps,frf-offset", 4);
>> +       plat->mode_off = dev_read_u32_default(bus, "snps,mode-offset", 6);
>> +       plat->tmod_off = dev_read_u32_default(bus, "snps,tmod-offset", 8);
>>         debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs,
>>               plat->frequency);
>>
>> @@ -388,6 +402,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>                        const void *dout, void *din, unsigned long flags)
>>  {
>>         struct udevice *bus = dev->parent;
>> +       struct dw_spi_platdata *plat = dev_get_platdata(bus);
>>         struct dw_spi_priv *priv = dev_get_priv(bus);
>>         const u8 *tx = dout;
>>         u8 *rx = din;
>> @@ -406,10 +421,6 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>         if (flags & SPI_XFER_BEGIN)
>>                 external_cs_manage(dev, false);
>>
>> -       cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) |
>> -               (priv->mode << SPI_MODE_OFFSET) |
>> -               (priv->tmode << SPI_TMOD_OFFSET);
>> -
>>         if (rx && tx)
>>                 priv->tmode = SPI_TMOD_TR;
>>         else if (rx)
>> @@ -421,8 +432,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>                  */
>>                 priv->tmode = SPI_TMOD_TR;
>>
>> -       cr0 &= ~SPI_TMOD_MASK;
>> -       cr0 |= (priv->tmode << SPI_TMOD_OFFSET);
>> +       cr0 = GEN_CTRL0(priv, plat);
>>
>>         priv->len = bitlen >> 3;
>>         debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len);
>> @@ -476,7 +486,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
>>
>>  static int dw_spi_set_speed(struct udevice *bus, uint speed)
>>  {
>> -       struct dw_spi_platdata *plat = bus->platdata;
>> +       struct dw_spi_platdata *plat = dev_get_platdata(bus);
>>         struct dw_spi_priv *priv = dev_get_priv(bus);
>>         u16 clk_div;
>>
>> --
>> 2.25.0
>>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 33/33] riscv: Add Sipeed Maix support
  2020-03-04  7:47     ` Rick Chen
@ 2020-03-04 15:11       ` Sean Anderson
  2020-03-05  3:40         ` Bin Meng
  0 siblings, 1 reply; 69+ messages in thread
From: Sean Anderson @ 2020-03-04 15:11 UTC (permalink / raw)
  To: u-boot

On 3/4/20 2:47 AM, Rick Chen wrote:
> Hi Sean
> 
> This patch series become larger and larger from v1 with 11 patches to
> v5 with 33 patches.

I didn't intend for it to balloon so large. My original goal for this
revision was just to get the SPI and MMC slots working. However, I
discovered that I would need to implement a pinmux slot and GPIOs for
the MMC slot to work (since I think that is the primary motivating
factor for using U-Boot on this board, as opposed to the native
bootloader).

> You shall just fix the suggestions from the previous version in the
> next version.

I will try to do that :)

> Additional extra features and subsystem drivers that you want to
> support, you shall send them individually instead of mixing them
> together.

In the past, I have been told to keep this sort of thing in one series
(e.g. by Bin Meng). I'm really not sure what things I should split off
and which I should keep together.

> 
> If you can separate them into different series(spi, gpio,led ,
> pinctrl, watchdog, those drivers were not present in v1) and they will
> not block the reviewing schedule each other.

Hm, ok. Perhaps split those off into series which depend on this one?

> Narrow down the dependency, it can help to speed up the patch work.
> And I am happy to help pulling the RISC-V relative patchs via riscv
> tree ASAP.

Ok, I will try and get another version out which fixes the feedback I
have gotten on those patches.

> 
> Thanks,
> Rick
> 
> 
> 

Thanks for the feedback.

--Sean

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-03 21:57         ` Sean Anderson
@ 2020-03-04 15:25           ` Lukas Auer
  0 siblings, 0 replies; 69+ messages in thread
From: Lukas Auer @ 2020-03-04 15:25 UTC (permalink / raw)
  To: u-boot

On Tue, 2020-03-03 at 16:57 -0500, Sean Anderson wrote:
> On 3/3/20 4:53 PM, Lukas Auer wrote:
> > On Mon, 2020-03-02 at 18:43 -0500, Sean Anderson wrote:
> > > On 3/2/20 6:17 PM, Lukas Auer wrote:
> > > > Don't move this. It is intended to be run before the IPI is cleared.
> > > 
> > > Hm, ok. I think I moved it to after because of the 'if (!smp_function)'
> > > check, but those two don't really need to be done together.
> > > 
> > 
> > Thanks! We had problems with code corruption in some situations,
> > because some secondary harts entered OpenSBI after the main hart while
> > OpenSBI expected all harts to be running OpenSBI by that time. Moving
> > this code block was part of the fix for this situation, see [1].
> > 
> > [1]: 
> > https://gitlab.denx.de/u-boot/u-boot/commit/90ae28143700bae4edd23930a7772899ad259058
> 
> Ah, this makes a lot more sense why it was located where it was.
> 
> > > > >  	/*
> > > > >  	 * Clear the IPI to acknowledge the request before jumping to the
> > > > >  	 * requested function.
> > > > >  	 */
> > > > >  	ret = riscv_clear_ipi(hart);
> > > > >  	if (ret) {
> > > > > -		pr_err("Cannot clear IPI of hart %ld\n", hart);
> > > > > +		pr_err("Cannot clear IPI of hart %ld (error %d)\n", hart, ret);
> > > > >  		return;
> > > > >  	}
> > > > >  
> > > > > +	__smp_mb();
> > > > > +
> > > > > +	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
> > > > > +	/*
> > > > > +	 * There may be an IPI raised before u-boot begins execution, so check
> > > > > +	 * to ensure we actually have a function to call.
> > > > > +	 */
> > > > > +	if (!smp_function)
> > > > > +		return;
> > > > > +	log_debug("hart = %lu func = %p\n", hart, smp_function);
> > > > 
> > > > The log messages might be corrupted if multiple harts are calling the
> > > > log function here. I have not looked into the details so this might not
> > > > be an issue. In that case it is fine to keep, otherwise please remove
> > > > it.
> > > 
> > > I ran into this problem a lot when debugging. I ended up implementing a
> > > spinlock around puts/putc. I agree it's probably better to remove this,
> > > but I worry that concurrency bugs will become much harder to track down
> > > without some kind of feedback. (This same criticism applies to the log
> > > message above as well).
> > > 
> > 
> > Especially with your changes, I hope we already have or will soon reach
> > a code robustness level where we won't have too many concurrency bugs
> > in the future. :)
> > Let's remove it for now until the logging backend can handle this
> > cleanly.
> 
> Ok. Should the error message above ("Cannot clear IPI of hart...") also
> be removed? I found it tended to corrupt the log output if it was ever
> triggered.
> 

Even though it's not ideal, we should keep it for now. Otherwise we
don't have a way to get notified about the error.

Thanks,
Lukas

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI
  2020-03-03  8:27         ` Rick Chen
@ 2020-03-05  2:18           ` Rick Chen
  0 siblings, 0 replies; 69+ messages in thread
From: Rick Chen @ 2020-03-05  2:18 UTC (permalink / raw)
  To: u-boot

Hi Sean

> Hi Sean
>
> > On Mon, 2020-03-02 at 10:43 -0500, Sean Anderson wrote:
> >
> > > On 3/2/20 4:08 AM, Rick Chen wrote:
> > > > Hi Sean
> > > >
> > > > > The IPI code could have race conditions in several places.
> > > > > * Several harts could race on the value of gd->arch->clint/plic
> > > > > * Non-boot harts could race with the main hart on the DM subsystem In
> > > > >   addition, if an IPI was pending when U-Boot started, it would cause the
> > > > >   IPI handler to jump to address 0.
> > > > >
> > > > > To address these problems, a new function riscv_init_ipi is introduced. It
> > > > > is called once during arch_cpu_init_dm. Before this point, no riscv_*_ipi
> > > > > functions may be called. Access is synchronized by gd->arch->ipi_ready.
> > > > >
> > > > > Signed-off-by: Sean Anderson <seanga2@gmail.com>
> > > > > ---
> > > > >
> > > > > Changes in v5:
> > > > > - New
> > > > >
> > > > >  arch/riscv/cpu/cpu.c                 |  9 ++++
> > > > >  arch/riscv/include/asm/global_data.h |  1 +
> > > > >  arch/riscv/include/asm/smp.h         | 43 ++++++++++++++++++
> > > > >  arch/riscv/lib/andes_plic.c          | 34 +++++---------
> > > > >  arch/riscv/lib/sbi_ipi.c             |  5 ++
> > > > >  arch/riscv/lib/sifive_clint.c        | 33 +++++---------
> > > > >  arch/riscv/lib/smp.c                 | 68 ++++++++--------------------
> > > > >  7 files changed, 101 insertions(+), 92 deletions(-)
> > > > >
> > > > > diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c
> > > > > index e457f6acbf..a971ec8694 100644
> > > > > --- a/arch/riscv/cpu/cpu.c
> > > > > +++ b/arch/riscv/cpu/cpu.c
> > > > > @@ -96,6 +96,15 @@ int arch_cpu_init_dm(void)
> > > > >                         csr_write(CSR_SATP, 0);
> > > > >         }
> > > > >
> > > > > +#ifdef CONFIG_SMP
> > > > > +       ret = riscv_init_ipi();
> > > > > +       if (ret)
> > > > > +               return ret;
> > > > > +
> > > > > +       /* Atomically set a flag enabling IPI handling */
> > > > > +       WRITE_ONCE(gd->arch.ipi_ready, 1);
> > > >
> > > > I think it shall not have race condition here.
> > > > Can you explain more detail why there will occur race condition ?
> > > >
> > > > Hi Lukas
> > > >
> > > > Do you have any comments ?
> > > >
> > > > Thanks
> > > > Rick
> > >
> > > On the K210, there may already be an IPI pending when U-Boot starts.
> > > (Perhaps the prior stage sends an IPI but does not clear it). As soon as
> > > interrupts are enabled, the hart then tries to call riscv_clear_ipi().
> > > Because the clint/plic has not yet been enabled, the clear_ipi function
> > > will try and bind/probe the device. This can have really nasty effects, since
> > > the boot hart is *also* trying to bind/probe devices.
> > >
> > > In addition, a hart could end up trying to probe the clint/plic because
> > > it could receive the IPI before (from its perspective) gd->arch.clint
> > > (or plic) gets initialized.
> > >
> >
> > We did not have a problem with pending IPIs on other platforms. It
> > should suffice to clear SSIP / MSIP before enabling the interrupts.
> >
>
> Can you try to clear mip/sip in startup flow before secondary_hart_loop:
> Maybe it can help to overcome the problem of calling riscv_clear_ipi()
> before riscv_init_ipi() that you added.

How about the verified result ?
Is this can help to fix your problem without any modification (the
original flow)
Avoid unexpected condition can increase the robustness indeed.
But clarify the root cause more precisely is still necessary here.

Thanks,
Rick

>
> Thanks,
> Rick
>
> > > Aside from the above, I think the macro approach is a bit confusing,
> > > since it's unclear at first glance what function will be initializing
> > > the clint/plic. Given U-Boot's otherwise completely SMP-unsafe design, I
> > > think it's better to be explicit and conservative in these areas.
> > >
> >
> > I agree, the patch makes this more clear and helps make the code more
> > robust.
> >
> > Thanks,
> > Lukas

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH v5 33/33] riscv: Add Sipeed Maix support
  2020-03-04 15:11       ` Sean Anderson
@ 2020-03-05  3:40         ` Bin Meng
  0 siblings, 0 replies; 69+ messages in thread
From: Bin Meng @ 2020-03-05  3:40 UTC (permalink / raw)
  To: u-boot

Hi Rick,

On Wed, Mar 4, 2020 at 11:11 PM Sean Anderson <seanga2@gmail.com> wrote:
>
> On 3/4/20 2:47 AM, Rick Chen wrote:
> > Hi Sean
> >
> > This patch series become larger and larger from v1 with 11 patches to
> > v5 with 33 patches.
>
> I didn't intend for it to balloon so large. My original goal for this
> revision was just to get the SPI and MMC slots working. However, I
> discovered that I would need to implement a pinmux slot and GPIOs for
> the MMC slot to work (since I think that is the primary motivating
> factor for using U-Boot on this board, as opposed to the native
> bootloader).
>
> > You shall just fix the suggestions from the previous version in the
> > next version.
>
> I will try to do that :)
>
> > Additional extra features and subsystem drivers that you want to
> > support, you shall send them individually instead of mixing them
> > together.
>
> In the past, I have been told to keep this sort of thing in one series
> (e.g. by Bin Meng). I'm really not sure what things I should split off
> and which I should keep together.
>

If separate patches are sent via different tree, we only get a
complete new RISC-V board support after all trees are merged. Also
there might be inter-dependencies between patches. My practice is to
wait for sub-domain maintainers to give Ack or RB tags, then pull all
series via one tree.

> >
> > If you can separate them into different series(spi, gpio,led ,
> > pinctrl, watchdog, those drivers were not present in v1) and they will
> > not block the reviewing schedule each other.
>
> Hm, ok. Perhaps split those off into series which depend on this one?
>
> > Narrow down the dependency, it can help to speed up the patch work.
> > And I am happy to help pulling the RISC-V relative patchs via riscv
> > tree ASAP.
>
> Ok, I will try and get another version out which fixes the feedback I
> have gotten on those patches.

Regards,
Bin

^ permalink raw reply	[flat|nested] 69+ messages in thread

end of thread, other threads:[~2020-03-05  3:40 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-28 21:05 [PATCH v5 00/33] riscv: Add Sipeed Maix support Sean Anderson
2020-02-28 21:05 ` [PATCH v5 01/33] clk: Always use the supplied struct clk Sean Anderson
2020-02-28 21:05 ` [PATCH v5 02/33] clk: Check that ops of composite clock components exist before calling Sean Anderson
2020-02-28 21:05 ` [PATCH v5 03/33] clk: Unconditionally recursively en-/dis-able clocks Sean Anderson
2020-03-04  6:50   ` Rick Chen
2020-02-28 21:05 ` [PATCH v5 04/33] clk: Add functions to register CCF clock structs Sean Anderson
2020-02-28 21:05 ` [PATCH v5 05/33] clk: Add K210 pll support Sean Anderson
2020-02-28 21:05 ` [PATCH v5 06/33] clk: Add a bypass clock for K210 Sean Anderson
2020-02-28 21:05 ` [PATCH v5 07/33] clk: Add K210 clock support Sean Anderson
2020-03-04  6:58   ` Rick Chen
2020-03-04 14:54     ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 08/33] doc: Fix typo in FIT documentation Sean Anderson
2020-03-02  2:21   ` Bin Meng
2020-02-28 21:05 ` [PATCH v5 09/33] dm: Add support for simple-pm-bus Sean Anderson
2020-02-28 21:05 ` [PATCH v5 10/33] dm: Fix error handling for dev_read_addr_ptr Sean Anderson
2020-03-02  2:22   ` Bin Meng
2020-03-02 19:46   ` Simon Glass
2020-02-28 21:05 ` [PATCH v5 11/33] reset: Add generic reset driver Sean Anderson
2020-02-28 21:05 ` [PATCH v5 12/33] lib: Always set errno in hcreate_r Sean Anderson
2020-03-02  2:24   ` Bin Meng
2020-02-28 21:05 ` [PATCH v5 13/33] pinctrl: Add support for Kendryte K210 FPIOA Sean Anderson
2020-03-04  6:47   ` Rick Chen
2020-03-04 15:00     ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 14/33] gpio: sifive: Use generic reg read function Sean Anderson
2020-03-02  2:24   ` Bin Meng
2020-02-28 21:05 ` [PATCH v5 15/33] gpio: dw: Fix warnings about casting int to pointer Sean Anderson
2020-03-02  2:27   ` Bin Meng
2020-03-02  3:50     ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 16/33] gpio: dw: Add a trailing underscore to generated name Sean Anderson
2020-02-28 21:05 ` [PATCH v5 17/33] gpio: dw: Return output value when direction is out Sean Anderson
2020-02-28 21:05 ` [PATCH v5 18/33] led: gpio: Default to using node name if label is absent Sean Anderson
2020-02-28 21:05 ` [PATCH v5 19/33] spi: dw: Add device tree properties for fields in CTRL1 Sean Anderson
2020-03-04  6:15   ` Rick Chen
2020-03-04 15:01     ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 20/33] spi: dw: Rename "cs-gpio" to "cs-gpios" Sean Anderson
2020-02-28 21:05 ` [PATCH v5 21/33] spi: dw: Use generic function to read reg address Sean Anderson
2020-02-28 21:05 ` [PATCH v5 22/33] spi: dw: Speed up transfer loops Sean Anderson
2020-02-28 21:05 ` [PATCH v5 23/33] spi: dw: Properly set rx_end when not recieving Sean Anderson
2020-02-29 17:47   ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 24/33] spi: dw: Add mem_ops Sean Anderson
2020-02-28 21:05 ` [PATCH v5 25/33] wdt: Move asm/utils.h to log2.h Sean Anderson
2020-02-28 21:46   ` Marek Vasut
2020-02-28 22:43     ` Sean Anderson
2020-02-28 23:27       ` Marek Vasut
2020-03-03  6:58   ` Rick Chen
2020-03-03 14:11     ` Sean Anderson
2020-02-28 21:05 ` [PATCH v5 26/33] riscv: Add headers for asm/global_data.h Sean Anderson
2020-02-28 21:05 ` [PATCH v5 27/33] riscv: Fix race conditions when initializing IPI Sean Anderson
2020-03-02  9:08   ` Rick Chen
2020-03-02 15:43     ` Sean Anderson
2020-03-02 23:15       ` Lukas Auer
2020-03-03  8:27         ` Rick Chen
2020-03-05  2:18           ` Rick Chen
2020-03-02 23:17   ` Lukas Auer
2020-03-02 23:43     ` Sean Anderson
2020-03-03 21:53       ` Lukas Auer
2020-03-03 21:57         ` Sean Anderson
2020-03-04 15:25           ` Lukas Auer
2020-02-28 21:05 ` [PATCH v5 28/33] riscv: Add option to support RISC-V privileged spec 1.9 Sean Anderson
2020-03-04  6:20   ` Rick Chen
2020-02-28 21:05 ` [PATCH v5 29/33] riscv: Allow use of reset drivers Sean Anderson
2020-02-28 21:05 ` [PATCH v5 30/33] riscv: Try to get cpu frequency from a "clocks" node if it exists Sean Anderson
2020-02-28 21:05 ` [PATCH v5 31/33] riscv: Enable cpu clock if it is present Sean Anderson
2020-02-28 21:05 ` [PATCH v5 32/33] riscv: Add device tree for K210 and Sipeed Maix BitM Sean Anderson
2020-02-28 21:05 ` [PATCH v5 33/33] riscv: Add Sipeed Maix support Sean Anderson
2020-03-04  6:04   ` Rick Chen
2020-03-04  7:47     ` Rick Chen
2020-03-04 15:11       ` Sean Anderson
2020-03-05  3:40         ` Bin Meng

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.