From d6213e206c441a3b2b83c0588954c851c3610d97 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 9 Mar 2022 20:46:01 +0100 Subject: Nokia RX-51: Convert to CONFIG_DM_VIDEO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanically convert video_hw_init() function to UCLASS_VIDEO probe callback and replace CONFIG_CFB_CONSOLE by CONFIG_DM_VIDEO. As framebuffer base address is setup by the bootloader which loads U-Boot, set plat->base to that fixed framebuffer address. This change was tested in qemu n900 machine and is working fine. What does not work is CONFIG_VIDEO_LOGO, seems to be buggy. Signed-off-by: Pali Rohár --- include/configs/nokia_rx51.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/configs/nokia_rx51.h b/include/configs/nokia_rx51.h index 9be64c3d3f8..e837b12b568 100644 --- a/include/configs/nokia_rx51.h +++ b/include/configs/nokia_rx51.h @@ -70,19 +70,12 @@ #define CONFIG_SYS_ONENAND_BASE ONENAND_MAP -/* - * Framebuffer - */ -/* Video console */ -#define VIDEO_FB_16BPP_PIXEL_SWAP -#define VIDEO_FB_16BPP_WORD_SWAP - /* Environment information */ #define CONFIG_EXTRA_ENV_SETTINGS \ "usbtty=cdc_acm\0" \ "stdin=usbtty,serial,keyboard\0" \ - "stdout=usbtty,serial,vga\0" \ - "stderr=usbtty,serial,vga\0" \ + "stdout=usbtty,serial,vidconsole\0" \ + "stderr=usbtty,serial,vidconsole\0" \ "slide=gpio input " __stringify(GPIO_SLIDE) "\0" \ "switchmmc=mmc dev ${mmcnum}\0" \ "kernaddr=0x82008000\0" \ -- cgit v1.3.1 From fd426b31066ba61ee1ff96a2b56c919251ffdd9e Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 1 Mar 2022 10:35:39 +0000 Subject: k210: use the board vendor name rather than the marketing name "kendryte" is the marketing name for the K210 RISC-V SoC produced by Canaan Inc. Rather than "kendryte,k210", use the usual "canaan,k210" vendor,SoC compatibility string format in the device tree files and use the SoC name for file names. With these changes, the device tree files are more in sync with the Linux kernel DTS and drivers, making uboot device tree usable by the kernel. Signed-off-by: Damien Le Moal Signed-off-by: Niklas Cassel Reviewed-by: Leo Yu-Chi Liang --- MAINTAINERS | 12 +- arch/riscv/dts/k210-maix-bit.dts | 3 +- arch/riscv/dts/k210.dtsi | 98 +- board/sipeed/maix/maix.c | 2 +- doc/board/sipeed/maix.rst | 20 +- .../mfd/canaan,k210-sysctl.txt | 33 + .../mfd/kendryte,k210-sysctl.txt | 33 - .../pinctrl/canaan,k210-fpioa.txt | 102 ++ .../pinctrl/kendryte,k210-fpioa.txt | 102 -- doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt | 4 +- drivers/clk/Makefile | 2 +- drivers/clk/clk_k210.c | 1344 ++++++++++++++++++++ drivers/clk/clk_kendryte.c | 1344 -------------------- drivers/pinctrl/Makefile | 2 +- drivers/pinctrl/pinctrl-k210.c | 740 +++++++++++ drivers/pinctrl/pinctrl-kendryte.c | 740 ----------- drivers/spi/designware_spi.c | 4 +- include/configs/sipeed-maix.h | 2 +- include/k210/pll.h | 24 + include/kendryte/pll.h | 24 - test/dm/k210_pll.c | 2 +- 21 files changed, 2319 insertions(+), 2318 deletions(-) create mode 100644 doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt delete mode 100644 doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt create mode 100644 doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt delete mode 100644 doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt create mode 100644 drivers/clk/clk_k210.c delete mode 100644 drivers/clk/clk_kendryte.c create mode 100644 drivers/pinctrl/pinctrl-k210.c delete mode 100644 drivers/pinctrl/pinctrl-kendryte.c create mode 100644 include/k210/pll.h delete mode 100644 include/kendryte/pll.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 82fc49e31d4..8defd09a645 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1115,14 +1115,14 @@ F: drivers/timer/andes_plmt_timer.c F: drivers/timer/sifive_clint_timer.c F: tools/prelink-riscv.c -RISC-V KENDRYTE +RISC-V CANAAN KENDRYTE K210 M: Sean Anderson 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/clk_kendryte.c -F: drivers/pinctrl/pinctrl-kendryte.c -F: include/kendryte/ +F: doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt +F: doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt +F: drivers/clk/clk_k210.c +F: drivers/pinctrl/pinctrl-k210.c +F: include/k210/ RNG M: Sughosh Ganu diff --git a/arch/riscv/dts/k210-maix-bit.dts b/arch/riscv/dts/k210-maix-bit.dts index 902dcfd08a9..c4bbf6b0186 100644 --- a/arch/riscv/dts/k210-maix-bit.dts +++ b/arch/riscv/dts/k210-maix-bit.dts @@ -12,7 +12,8 @@ / { model = "Sipeed Maix Bit 2.0"; - compatible = "sipeed,maix-bitm", "sipeed,maix-bit", "kendryte,k210"; + compatible = "sipeed,maix-bitm", "sipeed,maix-bit", + "canaan,kendryte-k210"; chosen { stdout-path = "serial0:115200"; diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi index 8bcd3cebdef..7dc2785a3e1 100644 --- a/arch/riscv/dts/k210.dtsi +++ b/arch/riscv/dts/k210.dtsi @@ -15,7 +15,7 @@ */ #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210"; + compatible = "canaan,kendryte-k210"; aliases { cpu0 = &cpu0; @@ -46,7 +46,7 @@ timebase-frequency = <7800000>; cpu0: cpu@0 { device_type = "cpu"; - compatible = "kendryte,k210", "sifive,rocket0", "riscv"; + compatible = "canaan,k210", "sifive,rocket0", "riscv"; reg = <0>; riscv,isa = "rv64imafdgc"; mmu-type = "sv39"; @@ -63,7 +63,7 @@ }; cpu1: cpu@1 { device_type = "cpu"; - compatible = "kendryte,k210", "sifive,rocket0", "riscv"; + compatible = "canaan,k210", "sifive,rocket0", "riscv"; reg = <1>; riscv,isa = "rv64imafdgc"; mmu-type = "sv39"; @@ -82,7 +82,7 @@ sram: memory@80000000 { device_type = "memory"; - compatible = "kendryte,k210-sram"; + compatible = "canaan,k210-sram"; reg = <0x80000000 0x400000>, <0x80400000 0x200000>, <0x80600000 0x200000>; @@ -106,12 +106,12 @@ soc { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-soc", "simple-bus"; + compatible = "canaan,k210-soc", "simple-bus"; ranges; interrupt-parent = <&plic0>; debug0: debug@0 { - compatible = "kendryte,k210-debug", "riscv,debug"; + compatible = "canaan,k210-debug", "riscv,debug"; reg = <0x0 0x1000>; }; @@ -122,7 +122,7 @@ clint0: clint@2000000 { #interrupt-cells = <1>; - compatible = "kendryte,k210-clint", "riscv,clint0"; + compatible = "canaan,k210-clint", "sifive,clint0", "riscv,clint0"; reg = <0x2000000 0xC000>; interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, <&cpu1_intc 3>, <&cpu1_intc 7>; @@ -131,7 +131,7 @@ plic0: interrupt-controller@C000000 { #interrupt-cells = <1>; - compatible = "kendryte,k210-plic", "riscv,plic0"; + compatible = "canaan,k210-plic", "sifive,plic-1.0.0", "riscv,plic0"; reg = <0xC000000 0x4000000>; interrupt-controller; interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>, @@ -141,7 +141,7 @@ }; uarths0: serial@38000000 { - compatible = "kendryte,k210-uarths", "sifive,uart0"; + compatible = "canaan,k210-uarths", "sifive,uart0"; reg = <0x38000000 0x1000>; interrupts = <33>; clocks = <&sysclk K210_CLK_CPU>; @@ -151,7 +151,7 @@ gpio0: gpio-controller@38001000 { #interrupt-cells = <2>; #gpio-cells = <2>; - compatible = "kendryte,k210-gpiohs", "sifive,gpio0"; + compatible = "canaan,k210-gpiohs", "sifive,gpio0"; reg = <0x38001000 0x1000>; interrupt-controller; interrupts = <34 35 36 37 38 39 40 41 @@ -164,7 +164,7 @@ }; kpu0: kpu@40800000 { - compatible = "kendryte,k210-kpu"; + compatible = "canaan,k210-kpu"; reg = <0x40800000 0xc00000>; interrupts = <25>; clocks = <&sysclk K210_CLK_AI>; @@ -172,7 +172,7 @@ }; fft0: fft@42000000 { - compatible = "kendryte,k210-fft"; + compatible = "canaan,k210-fft"; reg = <0x42000000 0x400000>; interrupts = <26>; clocks = <&sysclk K210_CLK_FFT>; @@ -181,7 +181,7 @@ }; dmac0: dma-controller@50000000 { - compatible = "kendryte,k210-dmac", "snps,axi-dma-1.01a"; + compatible = "canaan,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>; @@ -199,14 +199,14 @@ apb0: bus@50200000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB0>; gpio1: gpio-controller@50200000 { #address-cells = <1>; #size-cells = <0>; - compatible = "kendryte,k210-gpio", + compatible = "canaan,k210-gpio", "snps,dw-apb-gpio"; reg = <0x50200000 0x80>; clocks = <&sysclk K210_CLK_GPIO>; @@ -226,7 +226,7 @@ }; uart1: serial@50210000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50210000 0x100>; interrupts = <11>; @@ -242,7 +242,7 @@ }; uart2: serial@50220000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50220000 0x100>; interrupts = <12>; @@ -258,7 +258,7 @@ }; uart3: serial@50230000 { - compatible = "kendryte,k210-uart", + compatible = "canaan,k210-uart", "snps,dw-apb-uart"; reg = <0x50230000 0x100>; interrupts = <13>; @@ -274,7 +274,7 @@ }; spi2: spi@50240000 { - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; spi-slave; @@ -287,7 +287,7 @@ }; i2s0: i2s@50250000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50250000 0x200>; interrupts = <5>; @@ -298,13 +298,13 @@ }; apu0: sound@520250200 { - compatible = "kendryte,k210-apu"; + compatible = "canaan,k210-apu"; reg = <0x50250200 0x200>; status = "disabled"; }; i2s1: i2s@50260000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50260000 0x200>; interrupts = <6>; @@ -315,7 +315,7 @@ }; i2s2: i2s@50270000 { - compatible = "kendryte,k210-i2s", + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50270000 0x200>; interrupts = <7>; @@ -326,7 +326,7 @@ }; i2c0: i2c@50280000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x50280000 0x100>; interrupts = <8>; @@ -336,7 +336,7 @@ }; i2c1: i2c@50290000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x50290000 0x100>; interrupts = <9>; @@ -346,7 +346,7 @@ }; i2c2: i2c@502A0000 { - compatible = "kendryte,k210-i2c", + compatible = "canaan,k210-i2c", "snps,designware-i2c"; reg = <0x502A0000 0x100>; interrupts = <10>; @@ -356,12 +356,12 @@ }; fpioa: pinmux@502B0000 { - compatible = "kendryte,k210-fpioa"; + compatible = "canaan,k210-fpioa"; reg = <0x502B0000 0x100>; clocks = <&sysclk K210_CLK_FPIOA>; resets = <&sysrst K210_RST_FPIOA>; - kendryte,sysctl = <&sysctl>; - kendryte,power-offset = ; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-power-offset = ; pinctrl-0 = <&fpioa_jtag>; pinctrl-names = "default"; status = "disabled"; @@ -375,7 +375,7 @@ }; sha256: sha256@502C0000 { - compatible = "kendryte,k210-sha256"; + compatible = "canaan,k210-sha256"; reg = <0x502C0000 0x100>; clocks = <&sysclk K210_CLK_SHA>; resets = <&sysrst K210_RST_SHA>; @@ -383,7 +383,7 @@ }; timer0: timer@502D0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502D0000 0x100>; interrupts = <14 15>; @@ -394,7 +394,7 @@ }; timer1: timer@502E0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502E0000 0x100>; interrupts = <16 17>; @@ -405,7 +405,7 @@ }; timer2: timer@502F0000 { - compatible = "kendryte,k210-timer", + compatible = "canaan,k210-timer", "snps,dw-apb-timer"; reg = <0x502F0000 0x100>; interrupts = <18 19>; @@ -419,12 +419,12 @@ apb1: bus@50400000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB1>; wdt0: watchdog@50400000 { - compatible = "kendryte,k210-wdt", "snps,dw-wdt"; + compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50400000 0x100>; interrupts = <21>; clocks = <&sysclk K210_CLK_WDT0>; @@ -432,7 +432,7 @@ }; wdt1: watchdog@50410000 { - compatible = "kendryte,k210-wdt", "snps,dw-wdt"; + compatible = "canaan,k210-wdt", "snps,dw-wdt"; reg = <0x50410000 0x100>; interrupts = <22>; clocks = <&sysclk K210_CLK_WDT1>; @@ -443,7 +443,7 @@ otp0: nvmem@50420000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-otp"; + compatible = "canaan,k210-otp"; reg = <0x50420000 0x100>, <0x88000000 0x20000>; reg-names = "reg", "mem"; @@ -480,18 +480,18 @@ }; dvp0: camera@50430000 { - compatible = "kendryte,k210-dvp"; + compatible = "canaan,k210-dvp"; reg = <0x50430000 0x100>; interrupts = <24>; clocks = <&sysclk K210_CLK_DVP>; resets = <&sysrst K210_RST_DVP>; - kendryte,sysctl = <&sysctl>; - kendryte,misc-offset = ; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-misc-offset = ; status = "disabled"; }; sysctl: syscon@50440000 { - compatible = "kendryte,k210-sysctl", + compatible = "canaan,k210-sysctl", "syscon", "simple-mfd"; reg = <0x50440000 0x100>; reg-io-width = <4>; @@ -499,7 +499,7 @@ sysclk: clock-controller { #clock-cells = <1>; - compatible = "kendryte,k210-clk"; + compatible = "canaan,k210-clk"; clocks = <&in0>; assigned-clocks = <&sysclk K210_CLK_PLL1>; assigned-clock-rates = <390000000>; @@ -507,7 +507,7 @@ }; sysrst: reset-controller { - compatible = "kendryte,k210-rst", + compatible = "canaan,k210-rst", "syscon-reset"; #reset-cells = <1>; regmap = <&sysctl>; @@ -526,7 +526,7 @@ }; aes0: aes@50450000 { - compatible = "kendryte,k210-aes"; + compatible = "canaan,k210-aes"; reg = <0x50450000 0x100>; clocks = <&sysclk K210_CLK_AES>; resets = <&sysrst K210_RST_AES>; @@ -534,7 +534,7 @@ }; rtc: rtc@50460000 { - compatible = "kendryte,k210-rtc"; + compatible = "canaan,k210-rtc"; reg = <0x50460000 0x100>; clocks = <&in0>; resets = <&sysrst K210_RST_RTC>; @@ -546,14 +546,14 @@ apb2: bus@52000000 { #address-cells = <1>; #size-cells = <1>; - compatible = "kendryte,k210-apb", "simple-pm-bus"; + compatible = "canaan,k210-apb", "simple-pm-bus"; ranges; clocks = <&sysclk K210_CLK_APB2>; spi0: spi@52000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; reg = <0x52000000 0x100>; @@ -570,7 +570,7 @@ spi1: spi@53000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-spi", + compatible = "canaan,k210-spi", "snps,dw-apb-ssi-4.01", "snps,dw-apb-ssi"; reg = <0x53000000 0x100>; @@ -587,7 +587,7 @@ spi3: spi@54000000 { #address-cells = <1>; #size-cells = <0>; - compatible = "canaan,kendryte-k210-ssi", + compatible = "canaan,k210-ssi", "snps,dwc-ssi-1.01a"; reg = <0x54000000 0x200>; interrupts = <4>; diff --git a/board/sipeed/maix/maix.c b/board/sipeed/maix/maix.c index 52e4fee2f0a..a218278cb34 100644 --- a/board/sipeed/maix/maix.c +++ b/board/sipeed/maix/maix.c @@ -22,7 +22,7 @@ static int sram_init(void) struct clk clk; /* Enable RAM clocks */ - memory = ofnode_by_compatible(ofnode_null(), "kendryte,k210-sram"); + memory = ofnode_by_compatible(ofnode_null(), "canaan,k210-sram"); if (ofnode_equal(memory, ofnode_null())) return -ENOENT; diff --git a/doc/board/sipeed/maix.rst b/doc/board/sipeed/maix.rst index ef79297ef00..903f8831d70 100644 --- a/doc/board/sipeed/maix.rst +++ b/doc/board/sipeed/maix.rst @@ -4,16 +4,16 @@ MAIX ==== -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. +Several of the Sipeed Maix series of boards contain the Kendryte K210 processor, +a 64-bit RISC-V CPU produced by Canaan Inc. 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) and Sipeed MAIXDUINO are supported, but the boards are fairly similar. diff --git a/doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt b/doc/device-tree-bindings/mfd/canaan,k210-sysctl.txt new file mode 100644 index 00000000000..e48b164fc09 --- /dev/null +++ b/doc/device-tree-bindings/mfd/canaan,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 + "canaan,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 "canaan,k210-clk" +- clocks: phandle to the "in0" external oscillator +- #clock-cells: must be <1> + +Example: +sysctl: syscon@50440000 { + compatible = "canaan,k210-sysctl", "syscon", "simple-mfd"; + reg = <0x50440000 0x100>; + reg-io-width = <4>; + + sysclk: clock-controller { + compatible = "canaan,k210-clk"; + clocks = <&in0>; + #clock-cells = <1>; + }; +}; diff --git a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt b/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt deleted file mode 100644 index 5b24abcb62c..00000000000 --- a/doc/device-tree-bindings/mfd/kendryte,k210-sysctl.txt +++ /dev/null @@ -1,33 +0,0 @@ -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@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/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt new file mode 100644 index 00000000000..deca0cfab76 --- /dev/null +++ b/doc/device-tree-bindings/pinctrl/canaan,k210-fpioa.txt @@ -0,0 +1,102 @@ +Kendryte K210 FPIOA + +This binding describes the Fully-Programmable Input/Output Array (FPIOA) found +in Kendryte K210 SoCs. Any of the 256 functions can be mapped to any of the 48 +pins. + +Required properties: +- compatible: should be "canaan,k210-fpioa" +- reg: address and length of the FPIOA registers +- canaan,sysctl: phandle to the "sysctl" register map node +- canaan,k210-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 + +Required properties for pin-configuration nodes or sub-nodes are: +- groups: list of power groups to which the configuration applies. Valid groups + are: + A0, A1, A2, B3, B4, B5, C6, C7 + (either this or "pinmux" must be specified) +- pinmux: integer array representing pin multiplexing configuration. In addition + to the 256 standard functions, each pin can also output the direction + indicator (DO) of any function. This signal is high whenever the function + would normally drive the output. Helper macros to ease assembling the "pinmux" + arguments from the pin and function are provided by the FPIOA header file at: + + Integer values in the "pinmux" argument list are assembled as: + ((PIN << 16) | (DO << 8) | (FUNC)) + Valid values for PIN are numbers 0 through 47. + Valid values for DO are 0 or 1. + Valid values for FUNC are numbers 0 through 255. For a complete list of + acceptable functions, consult the FPIOA header file. + (either this or "groups" must be specified) + +Optional properties for "pinmux" nodes are: + bias-disable, bias-pull-down, bias-pull-up, drive-strength, + drive-strength-ua, input-enable, input-disable, input-schmitt-enable, + input-schmitt-disable, output-low, output-high, output-enable, + output-disable, slew-rate, output-polarity-invert, input-polarity-invert + +Optional properties for "groups" nodes are: + power-source + +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. +- input- and output-polarity-invert: Invert the polarity of either the input or + the output, respectively. +- power-source: Controls the output voltage of a bank of pins. Either + K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. +- slew-rate: Specifying this property reduces the slew rate. + +Example: +fpioa: pinmux@502B0000 { + compatible = "canaan,k210-fpioa"; + reg = <0x502B0000 0x100>; + canaan,k210-sysctl = <&sysctl>; + canaan,k210-power-offset = ; + + /* JTAG running at 3.3V and driven at 11 mA */ + fpioa_jtag: jtag { + voltage { + group = "A0"; + power-source = ; + }; + + jtag { + pinmux = , + , + , + ; + drive-strength = <11>; + } + }; + + /* I2C configured for use with a TCA9800 level shifter */ + fpioa_i2c: i2c { + i2c { + pinmux = , + ; + }; + + direction { + pinmux = ; + output-polarity-invert; + }; + }; + + /* UART with an active-high TX status LED */ + fpioa_uart1: uart1 { + uart { + pinmux = , + ; + }; + + status { + pinmux = ; + }; + }; +}; diff --git a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt b/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt deleted file mode 100644 index 73871f59305..00000000000 --- a/doc/device-tree-bindings/pinctrl/kendryte,k210-fpioa.txt +++ /dev/null @@ -1,102 +0,0 @@ -Kendryte K210 FPIOA - -This binding describes the Fully-Programmable Input/Output Array (FPIOA) found -in 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 - -Required properties for pin-configuration nodes or sub-nodes are: -- groups: list of power groups to which the configuration applies. Valid groups - are: - A0, A1, A2, B3, B4, B5, C6, C7 - (either this or "pinmux" must be specified) -- pinmux: integer array representing pin multiplexing configuration. In addition - to the 256 standard functions, each pin can also output the direction - indicator (DO) of any function. This signal is high whenever the function - would normally drive the output. Helper macros to ease assembling the "pinmux" - arguments from the pin and function are provided by the FPIOA header file at: - - Integer values in the "pinmux" argument list are assembled as: - ((PIN << 16) | (DO << 8) | (FUNC)) - Valid values for PIN are numbers 0 through 47. - Valid values for DO are 0 or 1. - Valid values for FUNC are numbers 0 through 255. For a complete list of - acceptable functions, consult the FPIOA header file. - (either this or "groups" must be specified) - -Optional properties for "pinmux" nodes are: - bias-disable, bias-pull-down, bias-pull-up, drive-strength, - drive-strength-ua, input-enable, input-disable, input-schmitt-enable, - input-schmitt-disable, output-low, output-high, output-enable, - output-disable, slew-rate, output-polarity-invert, input-polarity-invert - -Optional properties for "groups" nodes are: - power-source - -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. -- input- and output-polarity-invert: Invert the polarity of either the input or - the output, respectively. -- power-source: Controls the output voltage of a bank of pins. Either - K210_PC_POWER_1V8 or K210_PC_POWER_3V3 may be specified. -- slew-rate: Specifying this property reduces the slew rate. - -Example: -fpioa: pinmux@502B0000 { - compatible = "kendryte,k210-fpioa"; - reg = <0x502B0000 0x100>; - kendryte,sysctl = <&sysctl>; - kendryte,power-offset = ; - - /* JTAG running at 3.3V and driven at 11 mA */ - fpioa_jtag: jtag { - voltage { - group = "A0"; - power-source = ; - }; - - jtag { - pinmux = , - , - , - ; - drive-strength = <11>; - } - }; - - /* I2C configured for use with a TCA9800 level shifter */ - fpioa_i2c: i2c { - i2c { - pinmux = , - ; - }; - - direction { - pinmux = ; - output-polarity-invert; - }; - }; - - /* UART with an active-high TX status LED */ - fpioa_uart1: uart1 { - uart { - pinmux = , - ; - }; - - status { - pinmux = ; - }; - }; -}; diff --git a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt index 8d2888fbe30..7a0f11c53bf 100644 --- a/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt +++ b/doc/device-tree-bindings/spi/snps,dw-apb-ssi.txt @@ -5,8 +5,8 @@ Required properties: - compatible : One of "altr,socfpga-spi", "altr,socfpga-arria10-spi", - "canaan,kendryte-k210-spi", - "canaan,kendryte-k210-ssi", + "canaan,k210-spi", + "canaan,k210-ssi", "intel,stratix10-spi", "intel,agilex-spi", "mscc,ocelot-spi", diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f922a7c323d..bb4eee5d99b 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o -obj-$(CONFIG_CLK_K210) += clk_kendryte.o +obj-$(CONFIG_CLK_K210) += clk_k210.o obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_MPFS) += microchip/ obj-$(CONFIG_CLK_MVEBU) += mvebu/ diff --git a/drivers/clk/clk_k210.c b/drivers/clk/clk_k210.c new file mode 100644 index 00000000000..1961efaa5e7 --- /dev/null +++ b/drivers/clk/clk_k210.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-20 Sean Anderson + */ +#define LOG_CATEGORY UCLASS_CLK + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct k210_clk_priv - K210 clock driver private data + * @base: The base address of the sysctl device + * @in0: The "in0" external oscillator + */ +struct k210_clk_priv { + void __iomem *base; + struct clk in0; +}; + +/* + * All parameters for different sub-clocks are collected into parameter arrays. + * These parameters are then initialized by the clock which uses them during + * probe. To save space, ids are automatically generated for each sub-clock by + * using an enum. Instead of storing a parameter struct for each clock, even for + * those clocks which don't use a particular type of sub-clock, we can just + * store the parameters for the clocks which need them. + * + * So why do it like this? Arranging all the sub-clocks together makes it very + * easy to find bugs in the code. + */ + +/** + * enum k210_clk_div_type - The type of divider + * @K210_DIV_ONE: freq = parent / (reg + 1) + * @K210_DIV_EVEN: freq = parent / 2 / (reg + 1) + * @K210_DIV_POWER: freq = parent / (2 << reg) + * @K210_DIV_FIXED: freq = parent / factor + */ +enum k210_clk_div_type { + K210_DIV_ONE, + K210_DIV_EVEN, + K210_DIV_POWER, + K210_DIV_FIXED, +}; + +/** + * struct k210_div_params - Parameters for dividing clocks + * @type: An &enum k210_clk_div_type specifying the dividing formula + * @off: The offset of the divider from the sysctl base address + * @shift: The offset of the LSB of the divider + * @width: The number of bits in the divider + * @div: The fixed divisor for this divider + */ +struct k210_div_params { + u8 type; + union { + struct { + u8 off; + u8 shift; + u8 width; + }; + u8 div; + }; +}; + +#define DIV_LIST \ + DIV(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, K210_DIV_POWER) \ + DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE) \ + DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE) \ + DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE) \ + DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8, K210_DIV_EVEN) \ + DIV_FIXED(K210_CLK_CLINT, 50) \ + +#define _DIVIFY(id) K210_CLK_DIV_##id +#define DIVIFY(id) _DIVIFY(id) + +enum k210_div_id { +#define DIV(id, ...) DIVIFY(id), +#define DIV_FIXED DIV + DIV_LIST +#undef DIV +#undef DIV_FIXED + K210_CLK_DIV_NONE, +}; + +static const struct k210_div_params k210_divs[] = { +#define DIV(id, _off, _shift, _width, _type) \ + [DIVIFY(id)] = { \ + .type = (_type), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, +#define DIV_FIXED(id, _div) \ + [DIVIFY(id)] = { \ + .type = K210_DIV_FIXED, \ + .div = (_div) \ + }, + DIV_LIST +#undef DIV +#undef DIV_FIXED +}; + +#undef DIV +#undef DIV_LIST + +/** + * struct k210_gate_params - Parameters for gated clocks + * @off: The offset of the gate from the sysctl base address + * @bit_idx: The index of the bit within the register + */ +struct k210_gate_params { + u8 off; + u8 bit_idx; +}; + +#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) + +enum k210_gate_id { +#define GATE(id, ...) GATEIFY(id), + GATE_LIST +#undef GATE + K210_CLK_GATE_NONE, +}; + +static const struct k210_gate_params k210_gates[] = { +#define GATE(id, _off, _idx) \ + [GATEIFY(id)] = { \ + .off = (_off), \ + .bit_idx = (_idx), \ + }, + GATE_LIST +#undef GATE +}; + +#undef GATE_LIST + +/* The most parents is PLL2 */ +#define K210_CLK_MAX_PARENTS 3 + +/** + * struct k210_mux_params - Parameters for muxed clocks + * @parents: A list of parent clock ids + * @num_parents: The number of parent clocks + * @off: The offset of the mux from the base sysctl address + * @shift: The offset of the LSB of the mux selector + * @width: The number of bits in the mux selector + */ +struct k210_mux_params { + u8 parents[K210_CLK_MAX_PARENTS]; + u8 num_parents; + u8 off; + u8 shift; + u8 width; +}; + +#define MUX(id, reg, shift, width) \ + MUX_PARENTS(id, reg, shift, width, K210_CLK_IN0, K210_CLK_PLL0) +#define MUX_LIST \ + MUX_PARENTS(K210_CLK_PLL2, K210_SYSCTL_PLL2, 26, 2, \ + K210_CLK_IN0, K210_CLK_PLL0, K210_CLK_PLL1) \ + MUX(K210_CLK_ACLK, 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) + +enum k210_mux_id { +#define MUX_PARENTS(id, ...) MUXIFY(id), + MUX_LIST +#undef MUX_PARENTS + K210_CLK_MUX_NONE, +}; + +static const struct k210_mux_params k210_muxes[] = { +#define MUX_PARENTS(id, _off, _shift, _width, ...) \ + [MUXIFY(id)] = { \ + .parents = { __VA_ARGS__ }, \ + .num_parents = __count_args(__VA_ARGS__), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, + MUX_LIST +#undef MUX_PARENTS +}; + +#undef MUX +#undef MUX_LIST + +/** + * struct k210_pll_params - K210 PLL parameters + * @off: The offset of the PLL from the base sysctl address + * @shift: The offset of the LSB of the lock status + * @width: The number of bits in the lock status + */ +struct k210_pll_params { + u8 off; + u8 shift; + u8 width; +}; + +static const struct k210_pll_params k210_plls[] = { +#define PLL(_off, _shift, _width) { \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ +} + [0] = PLL(K210_SYSCTL_PLL0, 0, 2), + [1] = PLL(K210_SYSCTL_PLL1, 8, 1), + [2] = PLL(K210_SYSCTL_PLL2, 16, 1), +#undef PLL +}; + +/** + * enum k210_clk_flags - The type of a K210 clock + * @K210_CLKF_MUX: This clock has a mux and not a static parent + * @K210_CLKF_PLL: This clock is a PLL + */ +enum k210_clk_flags { + K210_CLKF_MUX = BIT(0), + K210_CLKF_PLL = BIT(1), +}; + +/** + * struct k210_clk_params - The parameters defining a K210 clock + * @name: The name of the clock + * @flags: A set of &enum k210_clk_flags defining which fields are valid + * @mux: An &enum k210_mux_id of this clock's mux + * @parent: The clock id of this clock's parent + * @pll: The id of the PLL (if this clock is a PLL) + * @div: An &enum k210_div_id of this clock's divider + * @gate: An &enum k210_gate_id of this clock's gate + */ +struct k210_clk_params { +#if CONFIG_IS_ENABLED(CMD_CLK) + const char *name; +#endif + u8 flags; + union { + u8 parent; + u8 mux; + }; + union { + u8 pll; + struct { + u8 div; + u8 gate; + }; + }; +}; + +static const struct k210_clk_params k210_clks[] = { +#if CONFIG_IS_ENABLED(CMD_CLK) +#define NAME(_name) .name = (_name), +#else +#define NAME(name) +#endif +#define CLK(id, _name, _parent, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .parent = (_parent), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_MUX(id, _name, _mux, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .flags = K210_CLKF_MUX, \ + .mux = (_mux), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_PLL(id, _pll, _parent) \ + [id] = { \ + NAME("pll" #_pll) \ + .flags = K210_CLKF_PLL, \ + .parent = (_parent), \ + .pll = (_pll), \ + } +#define CLK_FULL(id, name) \ + CLK_MUX(id, name, MUXIFY(id), DIVIFY(id), GATEIFY(id)) +#define CLK_NOMUX(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), GATEIFY(id)) +#define CLK_DIV(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), K210_CLK_GATE_NONE) +#define CLK_GATE(id, name, parent) \ + CLK(id, name, parent, K210_CLK_DIV_NONE, GATEIFY(id)) + CLK_PLL(K210_CLK_PLL0, 0, K210_CLK_IN0), + CLK_PLL(K210_CLK_PLL1, 1, K210_CLK_IN0), + [K210_CLK_PLL2] = { + NAME("pll2") + .flags = K210_CLKF_MUX | K210_CLKF_PLL, + .mux = MUXIFY(K210_CLK_PLL2), + .pll = 2, + }, + CLK_MUX(K210_CLK_ACLK, "aclk", MUXIFY(K210_CLK_ACLK), + DIVIFY(K210_CLK_ACLK), K210_CLK_GATE_NONE), + CLK_FULL(K210_CLK_SPI3, "spi3"), + CLK_FULL(K210_CLK_TIMER0, "timer0"), + CLK_FULL(K210_CLK_TIMER1, "timer1"), + CLK_FULL(K210_CLK_TIMER2, "timer2"), + CLK_NOMUX(K210_CLK_SRAM0, "sram0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_SRAM1, "sram1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_ROM, "rom", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_DVP, "dvp", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB0, "apb0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB1, "apb1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB2, "apb2", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_AI, "ai", K210_CLK_PLL1), + CLK_NOMUX(K210_CLK_I2S0, "i2s0", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S1, "i2s1", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S2, "i2s2", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_WDT0, "wdt0", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_WDT1, "wdt1", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_SPI0, "spi0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI1, "spi1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI2, "spi2", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C0, "i2c0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C1, "i2c1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C2, "i2c2", K210_CLK_PLL0), + CLK_DIV(K210_CLK_I2S0_M, "i2s0_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S1_M, "i2s1_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S2_M, "i2s2_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_CLINT, "clint", K210_CLK_ACLK), + CLK_GATE(K210_CLK_CPU, "cpu", K210_CLK_ACLK), + CLK_GATE(K210_CLK_DMA, "dma", K210_CLK_ACLK), + CLK_GATE(K210_CLK_FFT, "fft", K210_CLK_ACLK), + CLK_GATE(K210_CLK_GPIO, "gpio", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART1, "uart1", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART2, "uart2", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART3, "uart3", K210_CLK_APB0), + CLK_GATE(K210_CLK_FPIOA, "fpioa", K210_CLK_APB0), + CLK_GATE(K210_CLK_SHA, "sha", K210_CLK_APB0), + CLK_GATE(K210_CLK_AES, "aes", K210_CLK_APB1), + CLK_GATE(K210_CLK_OTP, "otp", K210_CLK_APB1), + CLK_GATE(K210_CLK_RTC, "rtc", K210_CLK_IN0), +#undef NAME +#undef CLK_PLL +#undef CLK +#undef CLK_FULL +#undef CLK_NOMUX +#undef CLK_DIV +#undef CLK_GATE +#undef CLK_LIST +}; + +#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 + +#ifdef CONFIG_CLK_K210_SET_RATE +static int k210_pll_enable(struct k210_clk_priv *priv, int id); +static int k210_pll_disable(struct k210_clk_priv *priv, int id); +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in); + +/* + * 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_config(u32 rate, u32 rate_in, + struct k210_pll_config *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; + best_error = S64_MAX; + error = best_error; + /* do-while here so we always try at least one ratio */ + do { + /* + * Whether we swapped r and od while enforcing frequency limits + */ + bool swapped = false; + /* + * Whether the intermediate frequencies are out-of-spec + */ + bool out_of_spec; + 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. + */ +again: + 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) { + u64 new_r, new_od; + + if (!swapped) { + u64 tmp = r; + + r = od; + od = tmp; + swapped = true; + goto again; + } + + /* + * Try looking ahead to see if there are additional + * factors for the same product. + */ + if (i + 1 < ARRAY_SIZE(factors)) { + 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; + goto again; + } + i--; + } + + /* + * Try looking back to see if there is a worse ratio + * that we could try anyway + */ + while (i > 0) { + i--; + new_r = UNPACK_R(factors[i]); + new_od = UNPACK_OD(factors[i]); + /* + * Don't loop over factors for the same product + * to avoid getting stuck because of the above + * clause + */ + if (r * od != new_r * new_od) { + if (new_r * new_od > last_r * last_od) { + r = new_r; + od = new_od; + swapped = false; + goto again; + } + break; + } + } + + /* We ran out of things to try */ + 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; + } + } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); + + log_debug("best error %lld\n", best_error); + if (best_error == S64_MAX) + return -EINVAL; + + return 0; +} + +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + int err; + const struct k210_pll_params *pll = &k210_plls[id]; + struct k210_pll_config config = {}; + u32 reg; + ulong calc_rate; + + err = k210_pll_calc_config(rate, rate_in, &config); + if (err) + return err; + log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); + + /* Don't bother setting the rate if we're already at that rate */ + calc_rate = DIV_ROUND_DOWN_ULL(((u64)rate_in) * config.f, + config.r * config.od); + if (calc_rate == k210_pll_get_rate(priv, id, rate)) + return calc_rate; + + k210_pll_disable(priv, id); + + reg = readl(priv->base + pll->off); + reg &= ~K210_PLL_CLKR + & ~K210_PLL_CLKF + & ~K210_PLL_CLKOD + & ~K210_PLL_BWADJ; + reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) + | FIELD_PREP(K210_PLL_CLKF, config.f - 1) + | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) + | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); + writel(reg, priv->base + pll->off); + + k210_pll_enable(priv, id); + + serial_setbrg(); + return k210_pll_get_rate(priv, id, rate); +} +#else +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + return -ENOSYS; +} +#endif /* CONFIG_CLK_K210_SET_RATE */ + +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, + ulong rate_in) +{ + u64 r, f, od; + u32 reg = readl(priv->base + k210_plls[id].off); + + if (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_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 mask = (BIT(pll->width) - 1) << pll->shift; + + while (true) { + u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK); + + if ((reg & mask) == mask) + break; + + reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); + writel(reg, priv->base + K210_SYSCTL_PLL_LOCK); + } +} + +static bool k210_pll_enabled(u32 reg) +{ + return (reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && + !(reg & K210_PLL_RESET); +} + +/* Adapted from sysctl_pll_enable */ +static int k210_pll_enable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + if (k210_pll_enabled(reg)) + return 0; + + reg |= K210_PLL_PWRD; + writel(reg, priv->base + pll->off); + + /* Ensure reset is low before asserting it */ + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + reg |= K210_PLL_RESET; + writel(reg, priv->base + pll->off); + nop(); + nop(); + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + + k210_pll_waitfor_lock(priv, id); + + reg &= ~K210_PLL_BYPASS; + reg |= K210_PLL_EN; + writel(reg, priv->base + pll->off); + + return 0; +} + +static int k210_pll_disable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + /* + * 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, priv->base + pll->off); + + reg &= ~K210_PLL_PWRD; + reg &= ~K210_PLL_EN; + writel(reg, priv->base + pll->off); + return 0; +} + +static u32 k210_clk_readl(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width) +{ + u32 reg = readl(priv->base + off); + + return (reg >> shift) & (BIT(width) - 1); +} + +static void k210_clk_writel(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width, u32 val) +{ + u32 reg = readl(priv->base + off); + u32 mask = (BIT(width) - 1) << shift; + + reg &= ~mask; + reg |= mask & (val << shift); + writel(reg, priv->base + off); +} + +static int k210_clk_get_parent(struct k210_clk_priv *priv, int id) +{ + u32 sel; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return k210_clks[id].parent; + mux = &k210_muxes[k210_clks[id].mux]; + + sel = k210_clk_readl(priv, mux->off, mux->shift, mux->width); + assert(sel < mux->num_parents); + return mux->parents[sel]; +} + +static ulong do_k210_clk_get_rate(struct k210_clk_priv *priv, int id) +{ + int parent; + u32 val; + ulong parent_rate; + const struct k210_div_params *div; + + if (id == K210_CLK_IN0) + return clk_get_rate(&priv->in0); + + parent = k210_clk_get_parent(priv, id); + parent_rate = do_k210_clk_get_rate(priv, parent); + if (IS_ERR_VALUE(parent_rate)) + return parent_rate; + + if (k210_clks[id].flags & K210_CLKF_PLL) + return k210_pll_get_rate(priv, k210_clks[id].pll, parent_rate); + + if (k210_clks[id].div == K210_CLK_DIV_NONE) + return parent_rate; + div = &k210_divs[k210_clks[id].div]; + + if (div->type == K210_DIV_FIXED) + return parent_rate / div->div; + + val = k210_clk_readl(priv, div->off, div->shift, div->width); + switch (div->type) { + case K210_DIV_ONE: + return parent_rate / (val + 1); + case K210_DIV_EVEN: + return parent_rate / 2 / (val + 1); + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return parent_rate; + return parent_rate / (2 << val); + default: + assert(false); + return -EINVAL; + }; +} + +static ulong k210_clk_get_rate(struct clk *clk) +{ + return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id); +} + +static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new) +{ + int i; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return -ENOSYS; + mux = &k210_muxes[k210_clks[id].mux]; + + for (i = 0; i < mux->num_parents; i++) { + if (mux->parents[i] == new) { + k210_clk_writel(priv, mux->off, mux->shift, mux->width, + i); + return 0; + } + } + return -EINVAL; +} + +static int k210_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return do_k210_clk_set_parent(dev_get_priv(clk->dev), clk->id, + parent->id); +} + +static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) +{ + int parent, ret, err; + ulong rate_in, val; + const struct k210_div_params *div; + struct k210_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id == K210_CLK_IN0) + return clk_set_rate(&priv->in0, rate); + + parent = k210_clk_get_parent(priv, clk->id); + rate_in = do_k210_clk_get_rate(priv, parent); + if (IS_ERR_VALUE(rate_in)) + return rate_in; + + log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in); + + if (clk->id == K210_CLK_PLL0) { + /* Bypass ACLK so the CPU keeps going */ + ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0); + if (ret) + return ret; + } else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) { + /* + * We can't bypass the AI clock like we can ACLK, and after + * relocation we are using the AI ram. + */ + return -EPERM; + } + + if (k210_clks[clk->id].flags & K210_CLKF_PLL) { + ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate, + rate_in); + if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) { + /* + * This may have the side effect of reparenting ACLK, + * but I don't really want to keep track of what the old + * parent was. + */ + err = do_k210_clk_set_parent(priv, K210_CLK_ACLK, + K210_CLK_PLL0); + if (err) + return err; + } + return ret; + } + + if (k210_clks[clk->id].div == K210_CLK_DIV_NONE) + return -ENOSYS; + div = &k210_divs[k210_clks[clk->id].div]; + + switch (div->type) { + case K210_DIV_ONE: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = val ? val - 1 : 0; + break; + case K210_DIV_EVEN: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate); + break; + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return -ENOSYS; + + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = __ffs(val); + break; + default: + assert(false); + return -EINVAL; + }; + + val = val ? val - 1 : 0; + k210_clk_writel(priv, div->off, div->shift, div->width, val); + return do_k210_clk_get_rate(priv, clk->id); +} + +static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable) +{ + int parent = k210_clk_get_parent(priv, id); + const struct k210_gate_params *gate; + + if (id == K210_CLK_IN0) { + if (enable) + return clk_enable(&priv->in0); + else + return clk_disable(&priv->in0); + } + + /* Only recursively enable clocks since we don't track refcounts */ + if (enable) { + int ret = k210_clk_endisable(priv, parent, true); + + if (ret && ret != -ENOSYS) + return ret; + } + + if (k210_clks[id].flags & K210_CLKF_PLL) { + if (enable) + return k210_pll_enable(priv, k210_clks[id].pll); + else + return k210_pll_disable(priv, k210_clks[id].pll); + } + + if (k210_clks[id].gate == K210_CLK_GATE_NONE) + return -ENOSYS; + gate = &k210_gates[k210_clks[id].gate]; + + k210_clk_writel(priv, gate->off, gate->bit_idx, 1, enable); + return 0; +} + +static int k210_clk_enable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, true); +} + +static int k210_clk_disable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, false); +} + +static int k210_clk_request(struct clk *clk) +{ + if (clk->id >= ARRAY_SIZE(k210_clks)) + return -EINVAL; + return 0; +} + +static const struct clk_ops k210_clk_ops = { + .request = k210_clk_request, + .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 int k210_clk_probe(struct udevice *dev) +{ + int ret; + struct k210_clk_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev_get_parent(dev)); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &priv->in0); + if (ret) + return ret; + + /* + * Force setting defaults, even before relocation. This is so we can + * set the clock rate for PLL1 before we relocate into aisram. + */ + if (!(gd->flags & GD_FLG_RELOC)) + clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE); + + return 0; +} + +static const struct udevice_id k210_clk_ids[] = { + { .compatible = "canaan,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, + .priv_auto = sizeof(struct k210_clk_priv), +}; + +#if CONFIG_IS_ENABLED(CMD_CLK) +static char show_enabled(struct k210_clk_priv *priv, int id) +{ + bool enabled; + + if (k210_clks[id].flags & K210_CLKF_PLL) { + const struct k210_pll_params *pll = + &k210_plls[k210_clks[id].pll]; + + enabled = k210_pll_enabled(readl(priv->base + pll->off)); + } else if (k210_clks[id].gate == K210_CLK_GATE_NONE) { + return '-'; + } else { + const struct k210_gate_params *gate = + &k210_gates[k210_clks[id].gate]; + + enabled = k210_clk_readl(priv, gate->off, gate->bit_idx, 1); + } + + return enabled ? 'y' : 'n'; +} + +static void show_clks(struct k210_clk_priv *priv, int id, int depth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k210_clks); i++) { + if (k210_clk_get_parent(priv, i) != id) + continue; + + printf(" %-9lu %-7c %*s%s\n", do_k210_clk_get_rate(priv, i), + show_enabled(priv, i), depth * 4, "", + k210_clks[i].name); + + show_clks(priv, i, depth + 1); + } +} + +int soc_clk_dump(void) +{ + int ret; + struct udevice *dev; + struct k210_clk_priv *priv; + + ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(k210_clk), + &dev); + if (ret) + return ret; + priv = dev_get_priv(dev); + + puts(" Rate Enabled Name\n"); + puts("------------------------\n"); + printf(" %-9lu %-7c %*s%s\n", clk_get_rate(&priv->in0), 'y', 0, "", + priv->in0.dev->name); + show_clks(priv, K210_CLK_IN0, 1); + return 0; +} +#endif diff --git a/drivers/clk/clk_kendryte.c b/drivers/clk/clk_kendryte.c deleted file mode 100644 index 97efda5b6f0..00000000000 --- a/drivers/clk/clk_kendryte.c +++ /dev/null @@ -1,1344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2019-20 Sean Anderson - */ -#define LOG_CATEGORY UCLASS_CLK - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -/** - * struct k210_clk_priv - K210 clock driver private data - * @base: The base address of the sysctl device - * @in0: The "in0" external oscillator - */ -struct k210_clk_priv { - void __iomem *base; - struct clk in0; -}; - -/* - * All parameters for different sub-clocks are collected into parameter arrays. - * These parameters are then initialized by the clock which uses them during - * probe. To save space, ids are automatically generated for each sub-clock by - * using an enum. Instead of storing a parameter struct for each clock, even for - * those clocks which don't use a particular type of sub-clock, we can just - * store the parameters for the clocks which need them. - * - * So why do it like this? Arranging all the sub-clocks together makes it very - * easy to find bugs in the code. - */ - -/** - * enum k210_clk_div_type - The type of divider - * @K210_DIV_ONE: freq = parent / (reg + 1) - * @K210_DIV_EVEN: freq = parent / 2 / (reg + 1) - * @K210_DIV_POWER: freq = parent / (2 << reg) - * @K210_DIV_FIXED: freq = parent / factor - */ -enum k210_clk_div_type { - K210_DIV_ONE, - K210_DIV_EVEN, - K210_DIV_POWER, - K210_DIV_FIXED, -}; - -/** - * struct k210_div_params - Parameters for dividing clocks - * @type: An &enum k210_clk_div_type specifying the dividing formula - * @off: The offset of the divider from the sysctl base address - * @shift: The offset of the LSB of the divider - * @width: The number of bits in the divider - * @div: The fixed divisor for this divider - */ -struct k210_div_params { - u8 type; - union { - struct { - u8 off; - u8 shift; - u8 width; - }; - u8 div; - }; -}; - -#define DIV_LIST \ - DIV(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, K210_DIV_POWER) \ - DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE) \ - DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE) \ - DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE) \ - DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE) \ - DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE) \ - DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE) \ - DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE) \ - DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE) \ - DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8, K210_DIV_EVEN) \ - DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8, K210_DIV_EVEN) \ - DIV_FIXED(K210_CLK_CLINT, 50) \ - -#define _DIVIFY(id) K210_CLK_DIV_##id -#define DIVIFY(id) _DIVIFY(id) - -enum k210_div_id { -#define DIV(id, ...) DIVIFY(id), -#define DIV_FIXED DIV - DIV_LIST -#undef DIV -#undef DIV_FIXED - K210_CLK_DIV_NONE, -}; - -static const struct k210_div_params k210_divs[] = { -#define DIV(id, _off, _shift, _width, _type) \ - [DIVIFY(id)] = { \ - .type = (_type), \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - }, -#define DIV_FIXED(id, _div) \ - [DIVIFY(id)] = { \ - .type = K210_DIV_FIXED, \ - .div = (_div) \ - }, - DIV_LIST -#undef DIV -#undef DIV_FIXED -}; - -#undef DIV -#undef DIV_LIST - -/** - * struct k210_gate_params - Parameters for gated clocks - * @off: The offset of the gate from the sysctl base address - * @bit_idx: The index of the bit within the register - */ -struct k210_gate_params { - u8 off; - u8 bit_idx; -}; - -#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) - -enum k210_gate_id { -#define GATE(id, ...) GATEIFY(id), - GATE_LIST -#undef GATE - K210_CLK_GATE_NONE, -}; - -static const struct k210_gate_params k210_gates[] = { -#define GATE(id, _off, _idx) \ - [GATEIFY(id)] = { \ - .off = (_off), \ - .bit_idx = (_idx), \ - }, - GATE_LIST -#undef GATE -}; - -#undef GATE_LIST - -/* The most parents is PLL2 */ -#define K210_CLK_MAX_PARENTS 3 - -/** - * struct k210_mux_params - Parameters for muxed clocks - * @parents: A list of parent clock ids - * @num_parents: The number of parent clocks - * @off: The offset of the mux from the base sysctl address - * @shift: The offset of the LSB of the mux selector - * @width: The number of bits in the mux selector - */ -struct k210_mux_params { - u8 parents[K210_CLK_MAX_PARENTS]; - u8 num_parents; - u8 off; - u8 shift; - u8 width; -}; - -#define MUX(id, reg, shift, width) \ - MUX_PARENTS(id, reg, shift, width, K210_CLK_IN0, K210_CLK_PLL0) -#define MUX_LIST \ - MUX_PARENTS(K210_CLK_PLL2, K210_SYSCTL_PLL2, 26, 2, \ - K210_CLK_IN0, K210_CLK_PLL0, K210_CLK_PLL1) \ - MUX(K210_CLK_ACLK, 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) - -enum k210_mux_id { -#define MUX_PARENTS(id, ...) MUXIFY(id), - MUX_LIST -#undef MUX_PARENTS - K210_CLK_MUX_NONE, -}; - -static const struct k210_mux_params k210_muxes[] = { -#define MUX_PARENTS(id, _off, _shift, _width, ...) \ - [MUXIFY(id)] = { \ - .parents = { __VA_ARGS__ }, \ - .num_parents = __count_args(__VA_ARGS__), \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - }, - MUX_LIST -#undef MUX_PARENTS -}; - -#undef MUX -#undef MUX_LIST - -/** - * struct k210_pll_params - K210 PLL parameters - * @off: The offset of the PLL from the base sysctl address - * @shift: The offset of the LSB of the lock status - * @width: The number of bits in the lock status - */ -struct k210_pll_params { - u8 off; - u8 shift; - u8 width; -}; - -static const struct k210_pll_params k210_plls[] = { -#define PLL(_off, _shift, _width) { \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ -} - [0] = PLL(K210_SYSCTL_PLL0, 0, 2), - [1] = PLL(K210_SYSCTL_PLL1, 8, 1), - [2] = PLL(K210_SYSCTL_PLL2, 16, 1), -#undef PLL -}; - -/** - * enum k210_clk_flags - The type of a K210 clock - * @K210_CLKF_MUX: This clock has a mux and not a static parent - * @K210_CLKF_PLL: This clock is a PLL - */ -enum k210_clk_flags { - K210_CLKF_MUX = BIT(0), - K210_CLKF_PLL = BIT(1), -}; - -/** - * struct k210_clk_params - The parameters defining a K210 clock - * @name: The name of the clock - * @flags: A set of &enum k210_clk_flags defining which fields are valid - * @mux: An &enum k210_mux_id of this clock's mux - * @parent: The clock id of this clock's parent - * @pll: The id of the PLL (if this clock is a PLL) - * @div: An &enum k210_div_id of this clock's divider - * @gate: An &enum k210_gate_id of this clock's gate - */ -struct k210_clk_params { -#if CONFIG_IS_ENABLED(CMD_CLK) - const char *name; -#endif - u8 flags; - union { - u8 parent; - u8 mux; - }; - union { - u8 pll; - struct { - u8 div; - u8 gate; - }; - }; -}; - -static const struct k210_clk_params k210_clks[] = { -#if CONFIG_IS_ENABLED(CMD_CLK) -#define NAME(_name) .name = (_name), -#else -#define NAME(name) -#endif -#define CLK(id, _name, _parent, _div, _gate) \ - [id] = { \ - NAME(_name) \ - .parent = (_parent), \ - .div = (_div), \ - .gate = (_gate), \ - } -#define CLK_MUX(id, _name, _mux, _div, _gate) \ - [id] = { \ - NAME(_name) \ - .flags = K210_CLKF_MUX, \ - .mux = (_mux), \ - .div = (_div), \ - .gate = (_gate), \ - } -#define CLK_PLL(id, _pll, _parent) \ - [id] = { \ - NAME("pll" #_pll) \ - .flags = K210_CLKF_PLL, \ - .parent = (_parent), \ - .pll = (_pll), \ - } -#define CLK_FULL(id, name) \ - CLK_MUX(id, name, MUXIFY(id), DIVIFY(id), GATEIFY(id)) -#define CLK_NOMUX(id, name, parent) \ - CLK(id, name, parent, DIVIFY(id), GATEIFY(id)) -#define CLK_DIV(id, name, parent) \ - CLK(id, name, parent, DIVIFY(id), K210_CLK_GATE_NONE) -#define CLK_GATE(id, name, parent) \ - CLK(id, name, parent, K210_CLK_DIV_NONE, GATEIFY(id)) - CLK_PLL(K210_CLK_PLL0, 0, K210_CLK_IN0), - CLK_PLL(K210_CLK_PLL1, 1, K210_CLK_IN0), - [K210_CLK_PLL2] = { - NAME("pll2") - .flags = K210_CLKF_MUX | K210_CLKF_PLL, - .mux = MUXIFY(K210_CLK_PLL2), - .pll = 2, - }, - CLK_MUX(K210_CLK_ACLK, "aclk", MUXIFY(K210_CLK_ACLK), - DIVIFY(K210_CLK_ACLK), K210_CLK_GATE_NONE), - CLK_FULL(K210_CLK_SPI3, "spi3"), - CLK_FULL(K210_CLK_TIMER0, "timer0"), - CLK_FULL(K210_CLK_TIMER1, "timer1"), - CLK_FULL(K210_CLK_TIMER2, "timer2"), - CLK_NOMUX(K210_CLK_SRAM0, "sram0", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_SRAM1, "sram1", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_ROM, "rom", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_DVP, "dvp", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB0, "apb0", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB1, "apb1", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_APB2, "apb2", K210_CLK_ACLK), - CLK_NOMUX(K210_CLK_AI, "ai", K210_CLK_PLL1), - CLK_NOMUX(K210_CLK_I2S0, "i2s0", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_I2S1, "i2s1", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_I2S2, "i2s2", K210_CLK_PLL2), - CLK_NOMUX(K210_CLK_WDT0, "wdt0", K210_CLK_IN0), - CLK_NOMUX(K210_CLK_WDT1, "wdt1", K210_CLK_IN0), - CLK_NOMUX(K210_CLK_SPI0, "spi0", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_SPI1, "spi1", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_SPI2, "spi2", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C0, "i2c0", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C1, "i2c1", K210_CLK_PLL0), - CLK_NOMUX(K210_CLK_I2C2, "i2c2", K210_CLK_PLL0), - CLK_DIV(K210_CLK_I2S0_M, "i2s0_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_I2S1_M, "i2s1_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_I2S2_M, "i2s2_m", K210_CLK_PLL2), - CLK_DIV(K210_CLK_CLINT, "clint", K210_CLK_ACLK), - CLK_GATE(K210_CLK_CPU, "cpu", K210_CLK_ACLK), - CLK_GATE(K210_CLK_DMA, "dma", K210_CLK_ACLK), - CLK_GATE(K210_CLK_FFT, "fft", K210_CLK_ACLK), - CLK_GATE(K210_CLK_GPIO, "gpio", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART1, "uart1", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART2, "uart2", K210_CLK_APB0), - CLK_GATE(K210_CLK_UART3, "uart3", K210_CLK_APB0), - CLK_GATE(K210_CLK_FPIOA, "fpioa", K210_CLK_APB0), - CLK_GATE(K210_CLK_SHA, "sha", K210_CLK_APB0), - CLK_GATE(K210_CLK_AES, "aes", K210_CLK_APB1), - CLK_GATE(K210_CLK_OTP, "otp", K210_CLK_APB1), - CLK_GATE(K210_CLK_RTC, "rtc", K210_CLK_IN0), -#undef NAME -#undef CLK_PLL -#undef CLK -#undef CLK_FULL -#undef CLK_NOMUX -#undef CLK_DIV -#undef CLK_GATE -#undef CLK_LIST -}; - -#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 - -#ifdef CONFIG_CLK_K210_SET_RATE -static int k210_pll_enable(struct k210_clk_priv *priv, int id); -static int k210_pll_disable(struct k210_clk_priv *priv, int id); -static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in); - -/* - * 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_config(u32 rate, u32 rate_in, - struct k210_pll_config *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; - best_error = S64_MAX; - error = best_error; - /* do-while here so we always try at least one ratio */ - do { - /* - * Whether we swapped r and od while enforcing frequency limits - */ - bool swapped = false; - /* - * Whether the intermediate frequencies are out-of-spec - */ - bool out_of_spec; - 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. - */ -again: - 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) { - u64 new_r, new_od; - - if (!swapped) { - u64 tmp = r; - - r = od; - od = tmp; - swapped = true; - goto again; - } - - /* - * Try looking ahead to see if there are additional - * factors for the same product. - */ - if (i + 1 < ARRAY_SIZE(factors)) { - 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; - goto again; - } - i--; - } - - /* - * Try looking back to see if there is a worse ratio - * that we could try anyway - */ - while (i > 0) { - i--; - new_r = UNPACK_R(factors[i]); - new_od = UNPACK_OD(factors[i]); - /* - * Don't loop over factors for the same product - * to avoid getting stuck because of the above - * clause - */ - if (r * od != new_r * new_od) { - if (new_r * new_od > last_r * last_od) { - r = new_r; - od = new_od; - swapped = false; - goto again; - } - break; - } - } - - /* We ran out of things to try */ - 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; - } - } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); - - log_debug("best error %lld\n", best_error); - if (best_error == S64_MAX) - return -EINVAL; - - return 0; -} - -static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, - ulong rate_in) -{ - int err; - const struct k210_pll_params *pll = &k210_plls[id]; - struct k210_pll_config config = {}; - u32 reg; - ulong calc_rate; - - err = k210_pll_calc_config(rate, rate_in, &config); - if (err) - return err; - log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); - - /* Don't bother setting the rate if we're already at that rate */ - calc_rate = DIV_ROUND_DOWN_ULL(((u64)rate_in) * config.f, - config.r * config.od); - if (calc_rate == k210_pll_get_rate(priv, id, rate)) - return calc_rate; - - k210_pll_disable(priv, id); - - reg = readl(priv->base + pll->off); - reg &= ~K210_PLL_CLKR - & ~K210_PLL_CLKF - & ~K210_PLL_CLKOD - & ~K210_PLL_BWADJ; - reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) - | FIELD_PREP(K210_PLL_CLKF, config.f - 1) - | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) - | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); - writel(reg, priv->base + pll->off); - - k210_pll_enable(priv, id); - - serial_setbrg(); - return k210_pll_get_rate(priv, id, rate); -} -#else -static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, - ulong rate_in) -{ - return -ENOSYS; -} -#endif /* CONFIG_CLK_K210_SET_RATE */ - -static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, - ulong rate_in) -{ - u64 r, f, od; - u32 reg = readl(priv->base + k210_plls[id].off); - - if (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_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 mask = (BIT(pll->width) - 1) << pll->shift; - - while (true) { - u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK); - - if ((reg & mask) == mask) - break; - - reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); - writel(reg, priv->base + K210_SYSCTL_PLL_LOCK); - } -} - -static bool k210_pll_enabled(u32 reg) -{ - return (reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && - !(reg & K210_PLL_RESET); -} - -/* Adapted from sysctl_pll_enable */ -static int k210_pll_enable(struct k210_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 reg = readl(priv->base + pll->off); - - if (k210_pll_enabled(reg)) - return 0; - - reg |= K210_PLL_PWRD; - writel(reg, priv->base + pll->off); - - /* Ensure reset is low before asserting it */ - reg &= ~K210_PLL_RESET; - writel(reg, priv->base + pll->off); - reg |= K210_PLL_RESET; - writel(reg, priv->base + pll->off); - nop(); - nop(); - reg &= ~K210_PLL_RESET; - writel(reg, priv->base + pll->off); - - k210_pll_waitfor_lock(priv, id); - - reg &= ~K210_PLL_BYPASS; - reg |= K210_PLL_EN; - writel(reg, priv->base + pll->off); - - return 0; -} - -static int k210_pll_disable(struct k210_clk_priv *priv, int id) -{ - const struct k210_pll_params *pll = &k210_plls[id]; - u32 reg = readl(priv->base + pll->off); - - /* - * 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, priv->base + pll->off); - - reg &= ~K210_PLL_PWRD; - reg &= ~K210_PLL_EN; - writel(reg, priv->base + pll->off); - return 0; -} - -static u32 k210_clk_readl(struct k210_clk_priv *priv, u8 off, u8 shift, - u8 width) -{ - u32 reg = readl(priv->base + off); - - return (reg >> shift) & (BIT(width) - 1); -} - -static void k210_clk_writel(struct k210_clk_priv *priv, u8 off, u8 shift, - u8 width, u32 val) -{ - u32 reg = readl(priv->base + off); - u32 mask = (BIT(width) - 1) << shift; - - reg &= ~mask; - reg |= mask & (val << shift); - writel(reg, priv->base + off); -} - -static int k210_clk_get_parent(struct k210_clk_priv *priv, int id) -{ - u32 sel; - const struct k210_mux_params *mux; - - if (!(k210_clks[id].flags & K210_CLKF_MUX)) - return k210_clks[id].parent; - mux = &k210_muxes[k210_clks[id].mux]; - - sel = k210_clk_readl(priv, mux->off, mux->shift, mux->width); - assert(sel < mux->num_parents); - return mux->parents[sel]; -} - -static ulong do_k210_clk_get_rate(struct k210_clk_priv *priv, int id) -{ - int parent; - u32 val; - ulong parent_rate; - const struct k210_div_params *div; - - if (id == K210_CLK_IN0) - return clk_get_rate(&priv->in0); - - parent = k210_clk_get_parent(priv, id); - parent_rate = do_k210_clk_get_rate(priv, parent); - if (IS_ERR_VALUE(parent_rate)) - return parent_rate; - - if (k210_clks[id].flags & K210_CLKF_PLL) - return k210_pll_get_rate(priv, k210_clks[id].pll, parent_rate); - - if (k210_clks[id].div == K210_CLK_DIV_NONE) - return parent_rate; - div = &k210_divs[k210_clks[id].div]; - - if (div->type == K210_DIV_FIXED) - return parent_rate / div->div; - - val = k210_clk_readl(priv, div->off, div->shift, div->width); - switch (div->type) { - case K210_DIV_ONE: - return parent_rate / (val + 1); - case K210_DIV_EVEN: - return parent_rate / 2 / (val + 1); - case K210_DIV_POWER: - /* This is ACLK, which has no divider on IN0 */ - if (parent == K210_CLK_IN0) - return parent_rate; - return parent_rate / (2 << val); - default: - assert(false); - return -EINVAL; - }; -} - -static ulong k210_clk_get_rate(struct clk *clk) -{ - return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id); -} - -static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new) -{ - int i; - const struct k210_mux_params *mux; - - if (!(k210_clks[id].flags & K210_CLKF_MUX)) - return -ENOSYS; - mux = &k210_muxes[k210_clks[id].mux]; - - for (i = 0; i < mux->num_parents; i++) { - if (mux->parents[i] == new) { - k210_clk_writel(priv, mux->off, mux->shift, mux->width, - i); - return 0; - } - } - return -EINVAL; -} - -static int k210_clk_set_parent(struct clk *clk, struct clk *parent) -{ - return do_k210_clk_set_parent(dev_get_priv(clk->dev), clk->id, - parent->id); -} - -static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) -{ - int parent, ret, err; - ulong rate_in, val; - const struct k210_div_params *div; - struct k210_clk_priv *priv = dev_get_priv(clk->dev); - - if (clk->id == K210_CLK_IN0) - return clk_set_rate(&priv->in0, rate); - - parent = k210_clk_get_parent(priv, clk->id); - rate_in = do_k210_clk_get_rate(priv, parent); - if (IS_ERR_VALUE(rate_in)) - return rate_in; - - log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in); - - if (clk->id == K210_CLK_PLL0) { - /* Bypass ACLK so the CPU keeps going */ - ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0); - if (ret) - return ret; - } else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) { - /* - * We can't bypass the AI clock like we can ACLK, and after - * relocation we are using the AI ram. - */ - return -EPERM; - } - - if (k210_clks[clk->id].flags & K210_CLKF_PLL) { - ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate, - rate_in); - if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) { - /* - * This may have the side effect of reparenting ACLK, - * but I don't really want to keep track of what the old - * parent was. - */ - err = do_k210_clk_set_parent(priv, K210_CLK_ACLK, - K210_CLK_PLL0); - if (err) - return err; - } - return ret; - } - - if (k210_clks[clk->id].div == K210_CLK_DIV_NONE) - return -ENOSYS; - div = &k210_divs[k210_clks[clk->id].div]; - - switch (div->type) { - case K210_DIV_ONE: - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); - val = val ? val - 1 : 0; - break; - case K210_DIV_EVEN: - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate); - break; - case K210_DIV_POWER: - /* This is ACLK, which has no divider on IN0 */ - if (parent == K210_CLK_IN0) - return -ENOSYS; - - val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); - val = __ffs(val); - break; - default: - assert(false); - return -EINVAL; - }; - - val = val ? val - 1 : 0; - k210_clk_writel(priv, div->off, div->shift, div->width, val); - return do_k210_clk_get_rate(priv, clk->id); -} - -static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable) -{ - int parent = k210_clk_get_parent(priv, id); - const struct k210_gate_params *gate; - - if (id == K210_CLK_IN0) { - if (enable) - return clk_enable(&priv->in0); - else - return clk_disable(&priv->in0); - } - - /* Only recursively enable clocks since we don't track refcounts */ - if (enable) { - int ret = k210_clk_endisable(priv, parent, true); - - if (ret && ret != -ENOSYS) - return ret; - } - - if (k210_clks[id].flags & K210_CLKF_PLL) { - if (enable) - return k210_pll_enable(priv, k210_clks[id].pll); - else - return k210_pll_disable(priv, k210_clks[id].pll); - } - - if (k210_clks[id].gate == K210_CLK_GATE_NONE) - return -ENOSYS; - gate = &k210_gates[k210_clks[id].gate]; - - k210_clk_writel(priv, gate->off, gate->bit_idx, 1, enable); - return 0; -} - -static int k210_clk_enable(struct clk *clk) -{ - return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, true); -} - -static int k210_clk_disable(struct clk *clk) -{ - return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, false); -} - -static int k210_clk_request(struct clk *clk) -{ - if (clk->id >= ARRAY_SIZE(k210_clks)) - return -EINVAL; - return 0; -} - -static const struct clk_ops k210_clk_ops = { - .request = k210_clk_request, - .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 int k210_clk_probe(struct udevice *dev) -{ - int ret; - struct k210_clk_priv *priv = dev_get_priv(dev); - - priv->base = dev_read_addr_ptr(dev_get_parent(dev)); - if (!priv->base) - return -EINVAL; - - ret = clk_get_by_index(dev, 0, &priv->in0); - if (ret) - return ret; - - /* - * Force setting defaults, even before relocation. This is so we can - * set the clock rate for PLL1 before we relocate into aisram. - */ - if (!(gd->flags & GD_FLG_RELOC)) - clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE); - - 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, - .priv_auto = sizeof(struct k210_clk_priv), -}; - -#if CONFIG_IS_ENABLED(CMD_CLK) -static char show_enabled(struct k210_clk_priv *priv, int id) -{ - bool enabled; - - if (k210_clks[id].flags & K210_CLKF_PLL) { - const struct k210_pll_params *pll = - &k210_plls[k210_clks[id].pll]; - - enabled = k210_pll_enabled(readl(priv->base + pll->off)); - } else if (k210_clks[id].gate == K210_CLK_GATE_NONE) { - return '-'; - } else { - const struct k210_gate_params *gate = - &k210_gates[k210_clks[id].gate]; - - enabled = k210_clk_readl(priv, gate->off, gate->bit_idx, 1); - } - - return enabled ? 'y' : 'n'; -} - -static void show_clks(struct k210_clk_priv *priv, int id, int depth) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(k210_clks); i++) { - if (k210_clk_get_parent(priv, i) != id) - continue; - - printf(" %-9lu %-7c %*s%s\n", do_k210_clk_get_rate(priv, i), - show_enabled(priv, i), depth * 4, "", - k210_clks[i].name); - - show_clks(priv, i, depth + 1); - } -} - -int soc_clk_dump(void) -{ - int ret; - struct udevice *dev; - struct k210_clk_priv *priv; - - ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(k210_clk), - &dev); - if (ret) - return ret; - priv = dev_get_priv(dev); - - puts(" Rate Enabled Name\n"); - puts("------------------------\n"); - printf(" %-9lu %-7c %*s%s\n", clk_get_rate(&priv->in0), 'y', 0, "", - priv->in0.dev->name); - show_clks(priv, K210_CLK_IN0, 1); - return 0; -} -#endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fd736a7f640..df37c32033f 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -18,7 +18,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) += pinctrl-kendryte.o +obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o obj-$(CONFIG_PINCTRL_MESON) += meson/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ obj-$(CONFIG_PINCTRL_MSCC) += mscc/ diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c new file mode 100644 index 00000000000..bb5153c6736 --- /dev/null +++ b/drivers/pinctrl/pinctrl-k210.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The K210 only implements 8 drive levels, even though there is register space + * for 16 + */ +#define K210_PC_DRIVE_MASK GENMASK(11, 8) +#define K210_PC_DRIVE_SHIFT 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_SL | K210_PC_OE | \ + K210_PC_PU) +#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | K210_PC_OE_INV | K210_PC_IE_INV) +#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) + +#define K210_PG_FUNC GENMASK(7, 0) +#define K210_PG_DO BIT(8) +#define K210_PG_PIN GENMASK(22, 16) + +#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1) +#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2) + +struct k210_fpioa { + u32 pins[48]; + u32 tie_en[8]; + u32 tie_val[8]; +}; + +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 */ +}; + +#ifdef CONFIG_CMD_PINMUX +static const char k210_pc_pin_names[][6] = { +#define PIN(i) \ + [i] = "IO_" #i + 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]; +} +#endif /* CONFIG_CMD_PINMUX */ + +/* These are just power domains */ +static const char k210_pc_group_names[][3] = { + [0] = "A0", + [1] = "A1", + [2] = "A2", + [3] = "B3", + [4] = "B4", + [5] = "B5", + [6] = "C6", + [7] = "C7", +}; + +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_SCCB, + K210_PC_DEFAULT_SPI, + K210_PC_DEFAULT_GPIO, + K210_PC_DEFAULT_INT13, +}; + +static const u32 k210_pc_mode_id_to_mode[] = { +#define DEFAULT(mode) \ + [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode + [K210_PC_DEFAULT_DISABLED] = 0, + DEFAULT(IN), + [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN, + DEFAULT(OUT), + DEFAULT(I2C), + DEFAULT(SCCB), + 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 { +#ifdef CONFIG_CMD_PINMUX + char name[15]; +#endif + u8 mode_id; +}; + +static const struct k210_pcf_info k210_pcf_infos[] = { +#ifdef CONFIG_CMD_PINMUX +#define FUNC(id, mode) \ + [K210_PCF_##id] = { \ + .name = #id, \ + .mode_id = K210_PC_DEFAULT_##mode \ + } +#else +#define FUNC(id, mode) \ + [K210_PCF_##id] = { \ + .mode_id = K210_PC_DEFAULT_##mode \ + } +#endif + 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_HSYNC, 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, SCCB), + FUNC(SCCB_SDA, SCCB), + 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 +}; + +static int k210_pc_pinmux_set(struct udevice *dev, u32 pinmux_group) +{ + unsigned pin = FIELD_GET(K210_PG_PIN, pinmux_group); + bool do_oe = FIELD_GET(K210_PG_DO, pinmux_group); + unsigned func = FIELD_GET(K210_PG_FUNC, pinmux_group); + struct k210_pc_priv *priv = dev_get_priv(dev); + const struct k210_pcf_info *info = &k210_pcf_infos[func]; + u32 mode = k210_pc_mode_id_to_mode[info->mode_id]; + u32 val = func | mode | (do_oe ? K210_PC_DO_OE : 0); + + debug("%s(%.8x): IO_%.2u = %3u | %.8x\n", __func__, pinmux_group, pin, + func, mode); + + writel(val, &priv->fpioa->pins[pin]); + return pin; +} + +/* 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, + K210_FPIOA(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; + case PIN_CONFIG_OUTPUT_INVERT: + if (argument) + val |= K210_PC_DO_INV; + else + val &= ~K210_PC_DO_INV; + break; + case PIN_CONFIG_INPUT_INVERT: + if (argument) + val |= K210_PC_DI_INV; + else + val &= ~K210_PC_DI_INV; + 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 { + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_CMD_PINMUX +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; +} +#endif + +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 }, + { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1}, + { "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1}, +}; + +static const struct pinctrl_ops k210_pc_pinctrl_ops = { +#ifdef CONFIG_CMD_PINMUX + .get_pins_count = k210_pc_get_pins_count, + .get_pin_name = k210_pc_get_pin_name, +#endif + .get_groups_count = k210_pc_get_groups_count, + .get_group_name = k210_pc_get_group_name, + .pinmux_property_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, +#ifdef CONFIG_CMD_PINMUX + .get_pin_muxing = k210_pc_get_pin_muxing, +#endif +}; + +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, "canaan,k210-sysctl"); + if (IS_ERR(priv->sysctl)) { + ret = -ENODEV; + goto err; + } + + ret = dev_read_u32(dev, "canaan,k210-power-offset", &priv->power_offset); + if (ret) + goto err; + + debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__, + priv->fpioa, (void *)priv->sysctl->ranges[0].start, + priv->power_offset); + + /* 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 = "canaan,k210-fpioa" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_k210) = { + .name = "pinctrl_k210", + .id = UCLASS_PINCTRL, + .of_match = k210_pc_ids, + .probe = k210_pc_probe, + .priv_auto = sizeof(struct k210_pc_priv), + .ops = &k210_pc_pinctrl_ops, +}; diff --git a/drivers/pinctrl/pinctrl-kendryte.c b/drivers/pinctrl/pinctrl-kendryte.c deleted file mode 100644 index 09d51ca6769..00000000000 --- a/drivers/pinctrl/pinctrl-kendryte.c +++ /dev/null @@ -1,740 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2020 Sean Anderson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The K210 only implements 8 drive levels, even though there is register space - * for 16 - */ -#define K210_PC_DRIVE_MASK GENMASK(11, 8) -#define K210_PC_DRIVE_SHIFT 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_SL | K210_PC_OE | \ - K210_PC_PU) -#define K210_PC_MODE_SCCB (K210_PC_MODE_I2C | K210_PC_OE_INV | K210_PC_IE_INV) -#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) - -#define K210_PG_FUNC GENMASK(7, 0) -#define K210_PG_DO BIT(8) -#define K210_PG_PIN GENMASK(22, 16) - -#define PIN_CONFIG_OUTPUT_INVERT (PIN_CONFIG_END + 1) -#define PIN_CONFIG_INPUT_INVERT (PIN_CONFIG_END + 2) - -struct k210_fpioa { - u32 pins[48]; - u32 tie_en[8]; - u32 tie_val[8]; -}; - -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 */ -}; - -#ifdef CONFIG_CMD_PINMUX -static const char k210_pc_pin_names[][6] = { -#define PIN(i) \ - [i] = "IO_" #i - 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]; -} -#endif /* CONFIG_CMD_PINMUX */ - -/* These are just power domains */ -static const char k210_pc_group_names[][3] = { - [0] = "A0", - [1] = "A1", - [2] = "A2", - [3] = "B3", - [4] = "B4", - [5] = "B5", - [6] = "C6", - [7] = "C7", -}; - -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_SCCB, - K210_PC_DEFAULT_SPI, - K210_PC_DEFAULT_GPIO, - K210_PC_DEFAULT_INT13, -}; - -static const u32 k210_pc_mode_id_to_mode[] = { -#define DEFAULT(mode) \ - [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode - [K210_PC_DEFAULT_DISABLED] = 0, - DEFAULT(IN), - [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN, - DEFAULT(OUT), - DEFAULT(I2C), - DEFAULT(SCCB), - 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 { -#ifdef CONFIG_CMD_PINMUX - char name[15]; -#endif - u8 mode_id; -}; - -static const struct k210_pcf_info k210_pcf_infos[] = { -#ifdef CONFIG_CMD_PINMUX -#define FUNC(id, mode) \ - [K210_PCF_##id] = { \ - .name = #id, \ - .mode_id = K210_PC_DEFAULT_##mode \ - } -#else -#define FUNC(id, mode) \ - [K210_PCF_##id] = { \ - .mode_id = K210_PC_DEFAULT_##mode \ - } -#endif - 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_HSYNC, 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, SCCB), - FUNC(SCCB_SDA, SCCB), - 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 -}; - -static int k210_pc_pinmux_set(struct udevice *dev, u32 pinmux_group) -{ - unsigned pin = FIELD_GET(K210_PG_PIN, pinmux_group); - bool do_oe = FIELD_GET(K210_PG_DO, pinmux_group); - unsigned func = FIELD_GET(K210_PG_FUNC, pinmux_group); - struct k210_pc_priv *priv = dev_get_priv(dev); - const struct k210_pcf_info *info = &k210_pcf_infos[func]; - u32 mode = k210_pc_mode_id_to_mode[info->mode_id]; - u32 val = func | mode | (do_oe ? K210_PC_DO_OE : 0); - - debug("%s(%.8x): IO_%.2u = %3u | %.8x\n", __func__, pinmux_group, pin, - func, mode); - - writel(val, &priv->fpioa->pins[pin]); - return pin; -} - -/* 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, - K210_FPIOA(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; - case PIN_CONFIG_OUTPUT_INVERT: - if (argument) - val |= K210_PC_DO_INV; - else - val &= ~K210_PC_DO_INV; - break; - case PIN_CONFIG_INPUT_INVERT: - if (argument) - val |= K210_PC_DI_INV; - else - val &= ~K210_PC_DI_INV; - 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 { - return -EINVAL; - } - - return 0; -} - -#ifdef CONFIG_CMD_PINMUX -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; -} -#endif - -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 }, - { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1}, - { "input-polarity-invert", PIN_CONFIG_INPUT_INVERT, 1}, -}; - -static const struct pinctrl_ops k210_pc_pinctrl_ops = { -#ifdef CONFIG_CMD_PINMUX - .get_pins_count = k210_pc_get_pins_count, - .get_pin_name = k210_pc_get_pin_name, -#endif - .get_groups_count = k210_pc_get_groups_count, - .get_group_name = k210_pc_get_group_name, - .pinmux_property_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, -#ifdef CONFIG_CMD_PINMUX - .get_pin_muxing = k210_pc_get_pin_muxing, -#endif -}; - -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; - - debug("%s: fpioa = %p sysctl = %p power offset = %x\n", __func__, - priv->fpioa, (void *)priv->sysctl->ranges[0].start, - priv->power_offset); - - /* 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 = sizeof(struct k210_pc_priv), - .ops = &k210_pc_pinctrl_ops, -}; diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index fc22f540fe6..9200efced96 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -758,8 +758,8 @@ static const struct udevice_id dw_spi_ids[] = { */ { .compatible = "altr,socfpga-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "altr,socfpga-arria10-spi", .data = (ulong)dw_spi_apb_init }, - { .compatible = "canaan,kendryte-k210-spi", .data = (ulong)dw_spi_apb_init }, - { .compatible = "canaan,kendryte-k210-ssi", .data = (ulong)dw_spi_dwc_init }, + { .compatible = "canaan,k210-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "canaan,k210-ssi", .data = (ulong)dw_spi_dwc_init }, { .compatible = "intel,stratix10-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "intel,agilex-spi", .data = (ulong)dw_spi_apb_init }, { .compatible = "mscc,ocelot-spi", .data = (ulong)dw_spi_apb_init }, diff --git a/include/configs/sipeed-maix.h b/include/configs/sipeed-maix.h index 1f74702ea7f..1cc2992c804 100644 --- a/include/configs/sipeed-maix.h +++ b/include/configs/sipeed-maix.h @@ -20,7 +20,7 @@ "fdt_addr_r=0x80400000\0" \ "scriptaddr=0x80020000\0" \ "kernel_addr_r=0x80060000\0" \ - "fdtfile=kendryte/" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ + "fdtfile=k210/" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "k210_bootcmd=load mmc 0:1 $loadaddr /uImage && " \ "load mmc 0:1 $fdt_addr_r /k210.dtb && " \ "bootm $loadaddr - $fdt_addr_r\0" diff --git a/include/k210/pll.h b/include/k210/pll.h new file mode 100644 index 00000000000..fd16a89cb20 --- /dev/null +++ b/include/k210/pll.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-20 Sean Anderson + */ +#ifndef K210_PLL_H +#define K210_PLL_H + +#include + +struct k210_pll_config { + u8 r; + u8 f; + u8 od; +}; + +#ifdef CONFIG_UNIT_TEST +TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, + struct k210_pll_config *best); +#ifndef nop +#define nop() +#endif + +#endif +#endif /* K210_PLL_H */ diff --git a/include/kendryte/pll.h b/include/kendryte/pll.h deleted file mode 100644 index fd16a89cb20..00000000000 --- a/include/kendryte/pll.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2019-20 Sean Anderson - */ -#ifndef K210_PLL_H -#define K210_PLL_H - -#include - -struct k210_pll_config { - u8 r; - u8 f; - u8 od; -}; - -#ifdef CONFIG_UNIT_TEST -TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, - struct k210_pll_config *best); -#ifndef nop -#define nop() -#endif - -#endif -#endif /* K210_PLL_H */ diff --git a/test/dm/k210_pll.c b/test/dm/k210_pll.c index f55379f3365..a0cc84c3961 100644 --- a/test/dm/k210_pll.c +++ b/test/dm/k210_pll.c @@ -7,7 +7,7 @@ /* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */ #include #include -#include +#include #include static int dm_test_k210_pll_calc_config(u32 rate, u32 rate_in, -- cgit v1.3.1 From 925f6900c8f51f2b9d4c5cd389be51c19121e974 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 22 Feb 2022 11:28:18 +0800 Subject: mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit f132aab40327 ("Revert "mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON to control card clock output""), it involve issue in mmc_switch_voltage(), because of the special design of usdhc. For FSL_USDHC, it do not implement VENDORSPEC_CKEN/PEREN/HCKEN/IPGEN, these are reserved bits(Though RM contain the definition of these bits, but actually internal IC logic do not implement, already confirm with IC team). Instead, use VENDORSPEC_FRC_SDCLK_ON to gate on/off the card clock output. Here is the definition of this bit in RM: [8] FRC_SDCLK_ON Force CLK output active Do not set this bit to 1 unless it is necessary. Also, make sure that this bit is cleared when uSDHC’s clock is about to be changed (frequency change, clock source change, or delay chain tuning). 0b - CLK active or inactive is fully controlled by the hardware. 1b - Force CLK active In default, the FRC_SDCLK_ON is 0. This means, when there is no command or data transfer on bus, hardware will gate off the card clock. But in some case, we need the card clock keep on. Take IO voltage 1.8v switch as example, after IO voltage change to 1.8v, spec require gate off the card clock for 5ms, and gate on the clock back, once detect the card clock on, then the card will draw the dat0 to high immediately. If there is not clock gate off/on behavior, some card will keep the dat0 to low level. This is the reason we fail in mmc_switch_voltage(). To fix this issue, and concern that this is only the fsl usdhc hardware design limitation, set the bit FRC_SDCLK_ON in the beginning of the wait_dat0() and clear it in the end. To make sure the 1.8v IO voltage switch process align with SD specification. For standard tuning process, usdhc specification also require the card clock keep on, so also add these behavior in fsl_esdhc_execute_tuning(). Reviewed-by: Marek Vasut Tested-by: Fabio Estevam Signed-off-by: Haibo Chen Reviewed-by: Peng Fan Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc_imx.c | 25 ++++++++++++++++++++++--- include/fsl_esdhc_imx.h | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 697e3c641d0..02208a5ade4 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -827,13 +827,16 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) struct mmc *mmc = &plat->mmc; u32 irqstaten = esdhc_read32(®s->irqstaten); u32 irqsigen = esdhc_read32(®s->irqsigen); - int i, ret = -ETIMEDOUT; - u32 val, mixctrl; + int i, err, ret = -ETIMEDOUT; + u32 val, mixctrl, tmp; /* clock tuning is not needed for upto 52MHz */ if (mmc->clock <= 52000000) return 0; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ if (priv->flags & ESDHC_FLAG_STD_TUNING) { val = esdhc_read32(®s->autoc12err); @@ -893,6 +896,12 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) esdhc_stop_tuning(mmc); + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } #endif @@ -1567,14 +1576,24 @@ static int __maybe_unused fsl_esdhc_set_enhanced_strobe(struct udevice *dev) static int fsl_esdhc_wait_dat0(struct udevice *dev, int state, int timeout_us) { - int ret; + int ret, err; u32 tmp; struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc *regs = priv->esdhc_regs; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + ret = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, !!(tmp & PRSSTAT_DAT0) == !!state, timeout_us); + + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } diff --git a/include/fsl_esdhc_imx.h b/include/fsl_esdhc_imx.h index 2153f29bef2..b8efd2a1664 100644 --- a/include/fsl_esdhc_imx.h +++ b/include/fsl_esdhc_imx.h @@ -37,6 +37,7 @@ #define VENDORSPEC_HCKEN 0x00001000 #define VENDORSPEC_IPGEN 0x00000800 #define VENDORSPEC_INIT 0x20007809 +#define VENDORSPEC_FRC_SDCLK_ON 0x00000100 #define IRQSTAT 0x0002e030 #define IRQSTAT_DMAE (0x10000000) @@ -94,6 +95,7 @@ #define PRSSTAT_CINS (0x00010000) #define PRSSTAT_BREN (0x00000800) #define PRSSTAT_BWEN (0x00000400) +#define PRSSTAT_SDOFF (0x00000080) #define PRSSTAT_SDSTB (0X00000008) #define PRSSTAT_DLA (0x00000004) #define PRSSTAT_CICHB (0x00000002) -- cgit v1.3.1 From 2a1d7c635fdede97a05d16067a936bba0a37f908 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:26 +0300 Subject: mmc: sdhci: Add HS400 Enhanced Strobe support Delegate setting the Enhanced Strobe configuration to individual drivers if they set a function for it. Return -ENOTSUPP if they do not, like what the MMC uclass does. Signed-off-by: Alper Nebi Yasak Reviewed-by: Jaehoon Chung Reviewed-by: Kever Yang --- drivers/mmc/sdhci.c | 18 ++++++++++++++++++ include/sdhci.h | 12 ++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 766e4a6b0c5..bf989a594f7 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -513,6 +513,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) reg |= SDHCI_CTRL_UHS_SDR104; break; case MMC_HS_400: + case MMC_HS_400_ES: reg |= SDHCI_CTRL_HS400; break; default: @@ -666,6 +667,7 @@ static int sdhci_set_ios(struct mmc *mmc) mmc->selected_mode == MMC_DDR_52 || mmc->selected_mode == MMC_HS_200 || mmc->selected_mode == MMC_HS_400 || + mmc->selected_mode == MMC_HS_400_ES || mmc->selected_mode == UHS_SDR25 || mmc->selected_mode == UHS_SDR50 || mmc->selected_mode == UHS_SDR104 || @@ -799,6 +801,19 @@ static int sdhci_wait_dat0(struct udevice *dev, int state, return -ETIMEDOUT; } +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +static int sdhci_set_enhanced_strobe(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct sdhci_host *host = mmc->priv; + + if (host->ops && host->ops->set_enhanced_strobe) + return host->ops->set_enhanced_strobe(host); + + return -ENOTSUPP; +} +#endif + const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, @@ -808,6 +823,9 @@ const struct dm_mmc_ops sdhci_ops = { .execute_tuning = sdhci_execute_tuning, #endif .wait_dat0 = sdhci_wait_dat0, +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) + .set_enhanced_strobe = sdhci_set_enhanced_strobe, +#endif }; #else static const struct mmc_ops sdhci_ops = { diff --git a/include/sdhci.h b/include/sdhci.h index c8d69f5a63f..88f1917480b 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -272,6 +272,18 @@ struct sdhci_ops { int (*platform_execute_tuning)(struct mmc *host, u8 opcode); int (*set_delay)(struct sdhci_host *host); int (*deferred_probe)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is called after setting the card speed and mode to + * HS400 ES, and should set any host-specific configuration + * necessary for it. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; #define ADMA_MAX_LEN 65532 -- cgit v1.3.1 From e4015661c39383c786e00beec586bddb0cf1541c Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Fri, 24 Dec 2021 16:43:45 +0300 Subject: rockchip: bob: Enable more configs This patch enables some configs that should be working on the Bob board, based on what is observed to work on the Kevin board. The Bob board uses an Embedded DisplayPort panel compatible with the simple panel and Rockchip eDP drivers. Its backlight is controlled by the Chromium OS Embedded Controller Pulse Width Modulator. Enable these for the board. Also set VIDEO_ROCKCHIP_MAX_{XRES,YRES} to 1280x800, the resolution of its panel. This had to be done for the Kevin board, but it's untested if this is actually necessary for Bob. The Rockchip video driver needs to assert/deassert some resets, so also enable the reset controller. RESET_ROCKCHIP defaults to y for this board when DM_RESET=y, so it's enough to set that. The Bob board has two USB 3.0 Type-C ports and one USB 2.0 Type-A port on its right side. Enable the configs relevant to USB devices so these can be used. This is despite a known issue with RK3399 boards where USB de-init causes a hang, as there is a known workaround. Some other rk3399-based devices enable support for the SoC's random number generator in commit a475bef5340c ("configs: rk3399: enable rng on firefly/rock960/rockpro64"), as it can provide a KASLR seed when booting using UEFI. Enable it for Bob as well. The default misc_init_r() for Rockchip boards sets cpuid and ethernet MAC address based on e-fuse block. A previous patch extends this on Gru boards to set registers related to SoC IO domains as is necessary on these boards. Enable this function and configs for it on Bob. The microSD card slot on this board (and others based on Gru) is connected to a GPIO controlled regulator (ppvar-sd-card-io), which must be operable by U-Boot. Enable the relevant config option to allow this. Bob boards also use the Winbond W25Q64DW SPI flash chip, enable support for Winbond SPI flash chips in the board config so U-Boot can boot with this chip. Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- configs/chromebook_bob_defconfig | 22 +++++++++++++++++++++- include/configs/gru.h | 3 +++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/configs/chromebook_bob_defconfig b/configs/chromebook_bob_defconfig index 79a26853a5f..3366e76e78c 100644 --- a/configs/chromebook_bob_defconfig +++ b/configs/chromebook_bob_defconfig @@ -21,6 +21,7 @@ CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-gru-bob.dtb" # CONFIG_DISPLAY_CPUINFO is not set CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_MISC_INIT_R=y CONFIG_BLOBLIST=y CONFIG_BLOBLIST_ADDR=0x100000 CONFIG_BLOBLIST_SIZE=0x1000 @@ -52,8 +53,9 @@ CONFIG_ROCKCHIP_GPIO=y CONFIG_I2C_CROS_EC_TUNNEL=y CONFIG_SYS_I2C_ROCKCHIP=y CONFIG_I2C_MUX=y -CONFIG_DM_KEYBOARD=y CONFIG_CROS_EC_KEYB=y +CONFIG_MISC=y +CONFIG_ROCKCHIP_EFUSE=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_PWRSEQ=y @@ -65,13 +67,21 @@ CONFIG_MMC_SDHCI_ROCKCHIP=y CONFIG_SF_DEFAULT_BUS=1 CONFIG_SF_DEFAULT_SPEED=20000000 CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_ETH_DESIGNWARE=y CONFIG_GMAC_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_TYPEC=y CONFIG_PMIC_RK8XX=y CONFIG_REGULATOR_PWM=y +CONFIG_DM_REGULATOR_GPIO=y CONFIG_REGULATOR_RK8XX=y +CONFIG_PWM_CROS_EC=y CONFIG_PWM_ROCKCHIP=y +CONFIG_DM_RESET=y +CONFIG_DM_RNG=y +CONFIG_RNG_ROCKCHIP=y CONFIG_DEBUG_UART_SHIFT=2 CONFIG_ROCKCHIP_SPI=y CONFIG_SYSRESET=y @@ -80,11 +90,21 @@ CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_GENERIC=y +CONFIG_USB_DWC3=y +CONFIG_USB_KEYBOARD=y CONFIG_USB_HOST_ETHER=y CONFIG_USB_ETHER_ASIX=y CONFIG_USB_ETHER_ASIX88179=y CONFIG_USB_ETHER_MCS7830=y CONFIG_USB_ETHER_RTL8152=y CONFIG_USB_ETHER_SMSC95XX=y +CONFIG_DM_VIDEO=y +CONFIG_DISPLAY=y +CONFIG_VIDEO_ROCKCHIP=y +CONFIG_VIDEO_ROCKCHIP_MAX_XRES=1280 +CONFIG_VIDEO_ROCKCHIP_MAX_YRES=800 +CONFIG_DISPLAY_ROCKCHIP_EDP=y CONFIG_CMD_DHRYSTONE=y CONFIG_ERRNO_STR=y diff --git a/include/configs/gru.h b/include/configs/gru.h index be2dc79968c..b1084bb21d4 100644 --- a/include/configs/gru.h +++ b/include/configs/gru.h @@ -13,4 +13,7 @@ #include +#define CONFIG_USB_OHCI_NEW +#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 2 + #endif -- cgit v1.3.1 From 6d36e92d28c9d009c786e29623c9558b7652ceda Mon Sep 17 00:00:00 2001 From: "Marty E. Plummer" Date: Fri, 24 Dec 2021 16:43:46 +0300 Subject: rockchip: rk3399: Add support for chromebook_kevin Add support for Kevin, an RK3399-based convertible chromebook that is very similar to Bob. This patch is mostly based on existing support for Bob, with only minor changes for Kevin-specific things. Unlike other Gru boards, coreboot sets Kevin's center logic to 925 mV, so adjust it here in the dts as well. The rk3399-gru-kevin devicetree has an unknown event code reference which has to be defined, set it to the Linux counterpart. The new defconfig is copied from Bob with the diffconfig: DEFAULT_DEVICE_TREE "rk3399-gru-bob" -> "rk3399-gru-kevin" DEFAULT_FDT_FILE "rockchip/rk3399-gru-bob.dtb" -> "rockchip/rk3399-gru-kevin.dtb" VIDEO_ROCKCHIP_MAX_XRES 1280 -> 2400 VIDEO_ROCKCHIP_MAX_YRES 800 -> 1600 +TARGET_CHROMEBOOK_KEVIN y With this Kevin can boot from SPI flash to a usable U-Boot prompt on the display with the keyboard working, but cannot boot into Linux for unknown reasons. eMMC starts in a working state but fails to re-init, microSD card works but at a lower-than-expected speed, USB works but causes a hang on de-init. There are known workarounds to solve eMMC and USB issues. Cc: Marty E. Plummer Cc: Simon Glass [Alper: commit message, resync config with Bob, update MAINTAINERS, add to Rockchip doc, add Kconfig help message, set regulator] Co-developed-by: Alper Nebi Yasak Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang Reviewed-by: Simon Glass Tested-by: Simon Glass --- arch/arm/dts/Makefile | 1 + arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi | 11 +++ arch/arm/mach-rockchip/rk3399/Kconfig | 11 +++ arch/arm/mach-rockchip/rk3399/rk3399.c | 3 +- arch/arm/mach-rockchip/spl.c | 3 +- board/google/gru/Kconfig | 16 ++++ board/google/gru/MAINTAINERS | 8 ++ board/google/gru/gru.c | 2 +- configs/chromebook_kevin_defconfig | 111 ++++++++++++++++++++++++++ doc/board/rockchip/rockchip.rst | 1 + include/dt-bindings/input/linux-event-codes.h | 3 +- 11 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi create mode 100644 configs/chromebook_kevin_defconfig (limited to 'include') diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 960f1a9fd4d..644ba961a22 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -137,6 +137,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3399) += \ rk3399-ficus.dtb \ rk3399-firefly.dtb \ rk3399-gru-bob.dtb \ + rk3399-gru-kevin.dtb \ rk3399-khadas-edge.dtb \ rk3399-khadas-edge-captain.dtb \ rk3399-khadas-edge-v.dtb \ diff --git a/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi b/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi new file mode 100644 index 00000000000..c03bd48e95d --- /dev/null +++ b/arch/arm/dts/rk3399-gru-kevin-u-boot.dtsi @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Jagan Teki + */ + +#include "rk3399-gru-u-boot.dtsi" +#include "rk3399-sdram-lpddr3-samsung-4GB-1866.dtsi" + +&ppvar_centerlogic_pwm { + regulator-init-microvolt = <925000>; +}; diff --git a/arch/arm/mach-rockchip/rk3399/Kconfig b/arch/arm/mach-rockchip/rk3399/Kconfig index 17628f91712..0833e083d9e 100644 --- a/arch/arm/mach-rockchip/rk3399/Kconfig +++ b/arch/arm/mach-rockchip/rk3399/Kconfig @@ -14,6 +14,17 @@ config TARGET_CHROMEBOOK_BOB display. It includes a Chrome OS EC (Cortex-M3) to provide access to the keyboard and battery functions. +config TARGET_CHROMEBOOK_KEVIN + bool "Samsung Chromebook Plus (RK3399)" + select HAS_ROM + select ROCKCHIP_SPI_IMAGE + help + Kevin is a RK3399-based convertible chromebook. It has two USB 3.0 + Type-C ports, 4GB of SDRAM, WiFi and a 12.3" 2400x1600 display. It + uses its USB ports for both power and external display. It includes + a Chromium OS EC (Cortex-M3) to provide access to the keyboard and + battery functions. + config TARGET_EVB_RK3399 bool "RK3399 evaluation board" help diff --git a/arch/arm/mach-rockchip/rk3399/rk3399.c b/arch/arm/mach-rockchip/rk3399/rk3399.c index d40969c8889..01a05599cd0 100644 --- a/arch/arm/mach-rockchip/rk3399/rk3399.c +++ b/arch/arm/mach-rockchip/rk3399/rk3399.c @@ -140,7 +140,8 @@ void board_debug_uart_init(void) struct rockchip_gpio_regs * const gpio = (void *)GPIO0_BASE; if (IS_ENABLED(CONFIG_SPL_BUILD) && - IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_BOB)) { + (IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_BOB) || + IS_ENABLED(CONFIG_TARGET_CHROMEBOOK_KEVIN))) { rk_setreg(&grf->io_vsel, 1 << 0); /* diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c index 02c40fb37ed..7a8db632b80 100644 --- a/arch/arm/mach-rockchip/spl.c +++ b/arch/arm/mach-rockchip/spl.c @@ -56,7 +56,8 @@ u32 spl_boot_device(void) defined(CONFIG_TARGET_CHROMEBIT_MICKEY) || \ defined(CONFIG_TARGET_CHROMEBOOK_MINNIE) || \ defined(CONFIG_TARGET_CHROMEBOOK_SPEEDY) || \ - defined(CONFIG_TARGET_CHROMEBOOK_BOB) + defined(CONFIG_TARGET_CHROMEBOOK_BOB) || \ + defined(CONFIG_TARGET_CHROMEBOOK_KEVIN) return BOOT_DEVICE_SPI; #endif if (CONFIG_IS_ENABLED(ROCKCHIP_BACK_TO_BROM)) diff --git a/board/google/gru/Kconfig b/board/google/gru/Kconfig index 61f7bbca989..1455e1481dc 100644 --- a/board/google/gru/Kconfig +++ b/board/google/gru/Kconfig @@ -13,3 +13,19 @@ config BOARD_SPECIFIC_OPTIONS # dummy def_bool y endif + +if TARGET_CHROMEBOOK_KEVIN + +config SYS_BOARD + default "gru" + +config SYS_VENDOR + default "google" + +config SYS_CONFIG_NAME + default "gru" + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + +endif diff --git a/board/google/gru/MAINTAINERS b/board/google/gru/MAINTAINERS index e1cda756b8c..53257c52a04 100644 --- a/board/google/gru/MAINTAINERS +++ b/board/google/gru/MAINTAINERS @@ -4,3 +4,11 @@ S: Maintained F: board/google/gru/ F: include/configs/gru.h F: configs/chromebook_bob_defconfig + +CHROMEBOOK KEVIN BOARD +M: Simon Glass +M: Alper Nebi Yasak +S: Maintained +F: board/google/gru/ +F: include/configs/gru.h +F: configs/chromebook_kevin_defconfig diff --git a/board/google/gru/gru.c b/board/google/gru/gru.c index cbf62a9427c..fbcf845e87d 100644 --- a/board/google/gru/gru.c +++ b/board/google/gru/gru.c @@ -26,7 +26,7 @@ void gru_dummy_function(int i) int board_early_init_f(void) { -# ifdef CONFIG_TARGET_CHROMEBOOK_BOB +# if defined(CONFIG_TARGET_CHROMEBOOK_BOB) || defined(CONFIG_TARGET_CHROMEBOOK_KEVIN) int sum, i; /* diff --git a/configs/chromebook_kevin_defconfig b/configs/chromebook_kevin_defconfig new file mode 100644 index 00000000000..831a6d3822b --- /dev/null +++ b/configs/chromebook_kevin_defconfig @@ -0,0 +1,111 @@ +CONFIG_ARM=y +CONFIG_SKIP_LOWLEVEL_INIT=y +CONFIG_ARCH_ROCKCHIP=y +CONFIG_SYS_TEXT_BASE=0x00200000 +CONFIG_SPL_GPIO=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_ENV_OFFSET=0x3F8000 +CONFIG_DEFAULT_DEVICE_TREE="rk3399-gru-kevin" +CONFIG_SPL_TEXT_BASE=0xff8c2000 +CONFIG_ROCKCHIP_RK3399=y +CONFIG_ROCKCHIP_BOOT_MODE_REG=0 +CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x4000 +# CONFIG_SPL_MMC is not set +CONFIG_TARGET_CHROMEBOOK_KEVIN=y +CONFIG_DEBUG_UART_BASE=0xff1a0000 +CONFIG_DEBUG_UART_CLOCK=24000000 +CONFIG_SPL_SPI_FLASH_SUPPORT=y +CONFIG_SPL_SPI=y +CONFIG_DEBUG_UART=y +CONFIG_SYS_LOAD_ADDR=0x800800 +CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-gru-kevin.dtb" +# CONFIG_DISPLAY_CPUINFO is not set +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_MISC_INIT_R=y +CONFIG_BLOBLIST=y +CONFIG_BLOBLIST_SIZE=0x1000 +CONFIG_BLOBLIST_ADDR=0x100000 +CONFIG_HANDOFF=y +# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set +CONFIG_SPL_STACK_R=y +CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x4000 +CONFIG_SPL_SPI_LOAD=y +CONFIG_SYS_SPI_U_BOOT_OFFS=0x40000 +CONFIG_CMD_BOOTZ=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_GPT=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TIME=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_LOG=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" +CONFIG_ENV_IS_IN_MMC=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_SPL_DM_SEQ_ALIAS=y +CONFIG_ROCKCHIP_GPIO=y +CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_SYS_I2C_ROCKCHIP=y +CONFIG_I2C_MUX=y +CONFIG_CROS_EC_KEYB=y +CONFIG_MISC=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_CROS_EC=y +CONFIG_CROS_EC_SPI=y +CONFIG_PWRSEQ=y +CONFIG_MMC_PWRSEQ=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ROCKCHIP=y +CONFIG_SF_DEFAULT_BUS=1 +CONFIG_SF_DEFAULT_SPEED=20000000 +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_DM_ETH=y +CONFIG_ETH_DESIGNWARE=y +CONFIG_GMAC_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PMIC_RK8XX=y +CONFIG_REGULATOR_PWM=y +CONFIG_DM_REGULATOR_GPIO=y +CONFIG_REGULATOR_RK8XX=y +CONFIG_PWM_CROS_EC=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_DM_RESET=y +CONFIG_DM_RNG=y +CONFIG_RNG_ROCKCHIP=y +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_ROCKCHIP_SPI=y +CONFIG_SYSRESET=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_GENERIC=y +CONFIG_USB_DWC3=y +CONFIG_USB_KEYBOARD=y +CONFIG_USB_HOST_ETHER=y +CONFIG_USB_ETHER_ASIX=y +CONFIG_USB_ETHER_ASIX88179=y +CONFIG_USB_ETHER_MCS7830=y +CONFIG_USB_ETHER_RTL8152=y +CONFIG_USB_ETHER_SMSC95XX=y +CONFIG_DM_VIDEO=y +CONFIG_DISPLAY=y +CONFIG_VIDEO_ROCKCHIP=y +CONFIG_VIDEO_ROCKCHIP_MAX_XRES=2400 +CONFIG_VIDEO_ROCKCHIP_MAX_YRES=1600 +CONFIG_DISPLAY_ROCKCHIP_EDP=y +CONFIG_CMD_DHRYSTONE=y +CONFIG_ERRNO_STR=y diff --git a/doc/board/rockchip/rockchip.rst b/doc/board/rockchip/rockchip.rst index 144cb98ef94..a75e60b9fa3 100644 --- a/doc/board/rockchip/rockchip.rst +++ b/doc/board/rockchip/rockchip.rst @@ -66,6 +66,7 @@ List of mainline supported Rockchip boards: - FriendlyElec NanoPi M4B (nanopi-m4b-rk3399) - FriendlyARM NanoPi NEO4 (nanopi-neo4-rk3399) - Google Bob (chromebook_bob) + - Google Kevin (chromebook_kevin) - Khadas Edge (khadas-edge-rk3399) - Khadas Edge-Captain (khadas-edge-captain-rk3399) - Khadas Edge-V (hadas-edge-v-rk3399) diff --git a/include/dt-bindings/input/linux-event-codes.h b/include/dt-bindings/input/linux-event-codes.h index 87cf351bab0..331458c0e71 100644 --- a/include/dt-bindings/input/linux-event-codes.h +++ b/include/dt-bindings/input/linux-event-codes.h @@ -749,7 +749,8 @@ #define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ #define SW_LINEIN_INSERT 0x0d /* set = inserted */ #define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_MAX 0x0f +#define SW_PEN_INSERTED 0x0f /* set = pen inserted */ +#define SW_MAX 0x10 #define SW_CNT (SW_MAX+1) /* -- cgit v1.3.1 From 8399488672e8717d3aa454f9f67408f96168074f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:10:10 +0100 Subject: efi_loader: export efi_dp_shorten() Rename function shorten_path() to efi_dp_shorten() and export it. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 3 ++- lib/efi_loader/efi_device_path.c | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/efi_loader.h b/include/efi_loader.h index 110d8ae79cc..1ffcdfc4856 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -725,7 +725,8 @@ extern void *efi_bounce_buffer; #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024) #endif - +/* shorten device path */ +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp); struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index dc787b4d3dd..ddd5f132ec5 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -122,20 +122,25 @@ int efi_dp_match(const struct efi_device_path *a, } } -/* +/** + * efi_dp_shorten() - shorten device-path + * * We can have device paths that start with a USB WWID or a USB Class node, * and a few other cases which don't encode the full device path with bus * hierarchy: * - * - MESSAGING:USB_WWID - * - MESSAGING:USB_CLASS - * - MEDIA:FILE_PATH - * - MEDIA:HARD_DRIVE - * - MESSAGING:URI + * * MESSAGING:USB_WWID + * * MESSAGING:USB_CLASS + * * MEDIA:FILE_PATH + * * MEDIA:HARD_DRIVE + * * MESSAGING:URI * * See UEFI spec (section 3.1.2, about short-form device-paths) + * + * @dp: original devie-path + * @Return: shortened device-path or NULL */ -static struct efi_device_path *shorten_path(struct efi_device_path *dp) +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) { while (dp) { /* @@ -189,7 +194,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, } } - obj_dp = shorten_path(efi_dp_next(obj_dp)); + obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); } while (short_path && obj_dp); } -- cgit v1.3.1 From c409593d0853da646194b0a3d65c8b45fe7cb6d4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 4 Mar 2022 08:20:00 +0100 Subject: efi_loader: fix efi_dp_find_obj() efi_dp_find_obj() should not return any handle with a partially matching device path but the handle with the maximum matching device path. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 4 +- lib/efi_loader/efi_device_path.c | 110 ++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/efi_loader.h b/include/efi_loader.h index 1ffcdfc4856..6271d40125e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -730,8 +730,8 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp); struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); -struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, - struct efi_device_path **rem); +efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + struct efi_device_path **rem); /* get size of the first device path instance excluding end node */ efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp); /* size of multi-instance device path excluding end node */ diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ddd5f132ec5..aeb52648200 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -159,69 +159,81 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) return dp; } -static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +/** + * find_handle() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @short_path: use short form device path for matching + * @rem: pointer to receive remaining device path + * Return: matching handle + */ +static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, + struct efi_device_path **rem) { - struct efi_object *efiobj; - efi_uintn_t dp_size = efi_dp_instance_size(dp); + efi_handle_t handle, best_handle = NULL; + efi_uintn_t len, best_len = 0; + + len = efi_dp_instance_size(dp); - list_for_each_entry(efiobj, &efi_obj_list, link) { + list_for_each_entry(handle, &efi_obj_list, link) { struct efi_handler *handler; - struct efi_device_path *obj_dp; + struct efi_device_path *dp_current; + efi_uintn_t len_current; efi_status_t ret; - ret = efi_search_protocol(efiobj, - &efi_guid_device_path, &handler); + ret = efi_search_protocol(handle, &efi_guid_device_path, + &handler); if (ret != EFI_SUCCESS) continue; - obj_dp = handler->protocol_interface; - - do { - if (efi_dp_match(dp, obj_dp) == 0) { - if (rem) { - /* - * Allow partial matches, but inform - * the caller. - */ - *rem = ((void *)dp) + - efi_dp_instance_size(obj_dp); - return efiobj; - } else { - /* Only return on exact matches */ - if (efi_dp_instance_size(obj_dp) == - dp_size) - return efiobj; - } - } - - obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); - } while (short_path && obj_dp); + dp_current = handler->protocol_interface; + if (short_path) { + dp_current = efi_dp_shorten(dp_current); + if (!dp_current) + continue; + } + len_current = efi_dp_instance_size(dp_current); + if (rem) { + if (len_current < len) + continue; + } else { + if (len_current != len) + continue; + } + if (memcmp(dp_current, dp, len)) + continue; + if (!rem) + return handle; + if (len_current > best_len) { + best_len = len_current; + best_handle = handle; + *rem = (void*)((u8 *)dp + len_current); + } } - - return NULL; + return best_handle; } -/* - * Find an efiobj from device-path, if 'rem' is not NULL, returns the - * remaining part of the device path after the matched object. +/** + * efi_dp_find_obj() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @rem: pointer to receive remaining device path + * Return: matching handle */ -struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, - struct efi_device_path **rem) +efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + struct efi_device_path **rem) { - struct efi_object *efiobj; - - /* Search for an exact match first */ - efiobj = find_obj(dp, false, NULL); - - /* Then for a fuzzy match */ - if (!efiobj) - efiobj = find_obj(dp, false, rem); + efi_handle_t handle; - /* And now for a fuzzy short match */ - if (!efiobj) - efiobj = find_obj(dp, true, rem); + handle = find_handle(dp, false, rem); + if (!handle) + /* Match short form device path */ + handle = find_handle(dp, true, rem); - return efiobj; + return handle; } /* -- cgit v1.3.1 From e46ef1db9e2c87d5aa13a04ea2329b8bae7ea9db Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 19 Mar 2022 06:35:43 +0100 Subject: efi_loader: efi_dp_find_obj() add protocol check Let function efi_dp_find_obj() additionally check if a given protocol is installed on the handle relating to the device-path. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 1 + lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_capsule.c | 2 +- lib/efi_loader/efi_device_path.c | 23 ++++++++++++++++------- lib/efi_loader/efi_disk.c | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/efi_loader.h b/include/efi_loader.h index 6271d40125e..1ae47a87137 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -731,6 +731,7 @@ struct efi_device_path *efi_dp_next(const struct efi_device_path *dp); int efi_dp_match(const struct efi_device_path *a, const struct efi_device_path *b); efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + const efi_guid_t *guid, struct efi_device_path **rem); /* get size of the first device path instance excluding end node */ efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d0f3e05e708..a7bc371f541 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1750,7 +1750,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, info->system_table = &systab; if (device_path) { - info->device_handle = efi_dp_find_obj(device_path, NULL); + info->device_handle = efi_dp_find_obj(device_path, NULL, NULL); dp = efi_dp_append(device_path, file_path); if (!dp) { diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 613b531b825..011942bbb79 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -680,7 +680,7 @@ static bool device_is_present_and_system_part(struct efi_device_path *dp) { efi_handle_t handle; - handle = efi_dp_find_obj(dp, NULL); + handle = efi_dp_find_obj(dp, NULL, NULL); if (!handle) return false; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index aeb52648200..0a8802903d7 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -160,17 +160,19 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) } /** - * find_handle() - find handle by device path + * find_handle() - find handle by device path and installed protocol * * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @short_path: use short form device path for matching * @rem: pointer to receive remaining device path * Return: matching handle */ -static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +static efi_handle_t find_handle(struct efi_device_path *dp, + const efi_guid_t *guid, bool short_path, + struct efi_device_path **rem) { efi_handle_t handle, best_handle = NULL; efi_uintn_t len, best_len = 0; @@ -183,6 +185,11 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, efi_uintn_t len_current; efi_status_t ret; + if (guid) { + ret = efi_search_protocol(handle, guid, &handler); + if (ret != EFI_SUCCESS) + continue; + } ret = efi_search_protocol(handle, &efi_guid_device_path, &handler); if (ret != EFI_SUCCESS) @@ -195,13 +202,13 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, } len_current = efi_dp_instance_size(dp_current); if (rem) { - if (len_current < len) + if (len_current > len) continue; } else { if (len_current != len) continue; } - if (memcmp(dp_current, dp, len)) + if (memcmp(dp_current, dp, len_current)) continue; if (!rem) return handle; @@ -220,18 +227,20 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @rem: pointer to receive remaining device path * Return: matching handle */ efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + const efi_guid_t *guid, struct efi_device_path **rem) { efi_handle_t handle; - handle = find_handle(dp, false, rem); + handle = find_handle(dp, guid, false, rem); if (!handle) /* Match short form device path */ - handle = find_handle(dp, true, rem); + handle = find_handle(dp, guid, true, rem); return handle; } diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 45127d17686..d36a35d94fe 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -302,7 +302,7 @@ efi_fs_from_path(struct efi_device_path *full_path) efi_free_pool(file_path); /* Get the EFI object for the partition */ - efiobj = efi_dp_find_obj(device_path, NULL); + efiobj = efi_dp_find_obj(device_path, NULL, NULL); efi_free_pool(device_path); if (!efiobj) return NULL; -- cgit v1.3.1 From 90dcd9b2d3a318391453d9d623d931a568906cb5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 19 Mar 2022 07:04:03 +0100 Subject: efi_loader: export efi_system_partition_guid The efi_system_partition_guid is needed in multiple places. Export it. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/efi_loader.h b/include/efi_loader.h index 1ae47a87137..156056fcd3d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -260,6 +260,8 @@ extern const efi_guid_t efi_block_io_guid; extern const efi_guid_t efi_global_variable_guid; extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; +/* GUID of the EFI system partition */ +extern const efi_guid_t efi_system_partition_guid; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ extern const efi_guid_t efi_guid_driver_binding_protocol; /* event group ExitBootServices() invoked */ -- cgit v1.3.1 From b78631d54f72f063e1f2a5d14a349b1b0d8fbce7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 5 Mar 2022 00:36:50 +0100 Subject: efi_loader: remove efi_disk_is_system_part() The block IO protocol may be installed on any handle. We should make no assumption about the structure the handle points to. efi_disk_is_system_part() makes an illegal widening cast from a handle to a struct efi_disk_obj. Remove the function. Fixes: Fixes: 41fd506842c2 ("efi_loader: disk: add efi_disk_is_system_part()") Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 2 -- lib/efi_loader/efi_capsule.c | 11 +++++++++-- lib/efi_loader/efi_disk.c | 29 ----------------------------- 3 files changed, 9 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/efi_loader.h b/include/efi_loader.h index 156056fcd3d..af36639ec6a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -541,8 +541,6 @@ efi_status_t tcg2_measure_pe_image(void *efi, u64 efi_size, int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *if_typename, int diskid, const char *pdevname); -/* Check if it is EFI system partition */ -bool efi_disk_is_system_part(efi_handle_t handle); /* Called by bootefi to make GOP (graphical) interface available */ efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 011942bbb79..f00440163d4 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -669,22 +669,29 @@ static efi_status_t get_dp_device(u16 *boot_var, /** * device_is_present_and_system_part - check if a device exists - * @dp Device path * * Check if a device pointed to by the device path, @dp, exists and is * located in UEFI system partition. * + * @dp device path * Return: true - yes, false - no */ static bool device_is_present_and_system_part(struct efi_device_path *dp) { efi_handle_t handle; + struct efi_device_path *rem; + /* Check device exists */ handle = efi_dp_find_obj(dp, NULL, NULL); if (!handle) return false; - return efi_disk_is_system_part(handle); + /* Check device is on system partition */ + handle = efi_dp_find_obj(dp, &efi_system_partition_guid, &rem); + if (!handle) + return false; + + return true; } /** diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index d36a35d94fe..c905c12abc2 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -587,32 +587,3 @@ efi_status_t efi_disk_register(void) return EFI_SUCCESS; } - -/** - * efi_disk_is_system_part() - check if handle refers to an EFI system partition - * - * @handle: handle of partition - * - * Return: true if handle refers to an EFI system partition - */ -bool efi_disk_is_system_part(efi_handle_t handle) -{ - struct efi_handler *handler; - struct efi_disk_obj *diskobj; - struct disk_partition info; - efi_status_t ret; - int r; - - /* check if this is a block device */ - ret = efi_search_protocol(handle, &efi_block_io_guid, &handler); - if (ret != EFI_SUCCESS) - return false; - - diskobj = container_of(handle, struct efi_disk_obj, header); - - r = part_get_info(diskobj->desc, diskobj->part, &info); - if (r) - return false; - - return !!(info.bootable & PART_EFI_SYSTEM_PARTITION); -} -- cgit v1.3.1 From ed48490f8d3f8715b23071a8a1cbe5bba8c8626e Mon Sep 17 00:00:00 2001 From: Han Xu Date: Fri, 25 Mar 2022 08:36:38 -0500 Subject: mtd: gpmi: fix the bch setting backward compatible issue Previous u-boot code changed the default bch setting behavior and caused backward compatible issue. This fix choose the legacy bch geometry back again as the default option. If the minimum ecc strength that NAND chips required need to be chosen, it can be enabled by either adding DT flag "fsl,use-minimum-ecc" or CONFIG_NAND_MXS_USE_MINIMUM_ECC in configs. The unused flag "fsl,legacy-bch-geometry" get removed. Fixes: 51cdf83eea (mtd: gpmi: provide the option to use legacy bch geometry) Fixes: 616f03daba (mtd: gpmi: change the BCH layout setting for large oob NAND) Tested-by: Tim Harvey Tested-by: Sean Nyekjaer Signed-off-by: Han Xu Reviewed-by: Miquel Raynal --- drivers/mtd/nand/raw/mxs_nand.c | 71 +++++++++++++++++++++++++++++++++----- drivers/mtd/nand/raw/mxs_nand_dt.c | 2 -- include/mxs_nand.h | 2 -- 3 files changed, 62 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index 748056a43e6..ee5d7fde9ce 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -195,6 +195,7 @@ static inline int mxs_nand_legacy_calc_ecc_layout(struct bch_geometry *geo, struct nand_chip *chip = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(chip); unsigned int block_mark_bit_offset; + int corr, ds_corr; /* The default for the length of Galois Field. */ geo->gf_len = 13; @@ -225,6 +226,17 @@ static inline int mxs_nand_legacy_calc_ecc_layout(struct bch_geometry *geo, geo->ecc_strength = min(round_down(geo->ecc_strength, 2), nand_info->max_ecc_strength_supported); + /* check ecc strength, same as nand_ecc_is_strong_enough() did*/ + if (chip->ecc_step_ds) { + corr = mtd->writesize * geo->ecc_strength / + geo->ecc_chunkn_size; + ds_corr = mtd->writesize * chip->ecc_strength_ds / + chip->ecc_step_ds; + if (corr < ds_corr || + geo->ecc_strength < chip->ecc_strength_ds) + return -EINVAL; + } + block_mark_bit_offset = mtd->writesize * 8 - (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + MXS_NAND_METADATA_SIZE * 8); @@ -1111,6 +1123,7 @@ static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *nand = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + int err; if (chip->ecc_strength_ds > nand_info->max_ecc_strength_supported) { printf("unsupported NAND chip, minimum ecc required %d\n" @@ -1118,19 +1131,57 @@ static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) return -EINVAL; } - if ((!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) && - mtd->oobsize < 1024) || nand_info->legacy_bch_geometry) { - dev_warn(mtd->dev, "use legacy bch geometry\n"); - return mxs_nand_legacy_calc_ecc_layout(geo, mtd); + /* use the legacy bch setting by default */ + if ((!nand_info->use_minimum_ecc && mtd->oobsize < 1024) || + !(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) { + dev_dbg(mtd->dev, "use legacy bch geometry\n"); + err = mxs_nand_legacy_calc_ecc_layout(geo, mtd); + if (!err) + return 0; } - if (mtd->oobsize > 1024 || chip->ecc_step_ds < mtd->oobsize) - return mxs_nand_calc_ecc_for_large_oob(geo, mtd); + /* for large oob nand */ + if (mtd->oobsize > 1024) { + dev_dbg(mtd->dev, "use large oob bch geometry\n"); + err = mxs_nand_calc_ecc_for_large_oob(geo, mtd); + if (!err) + return 0; + } - return mxs_nand_calc_ecc_layout_by_info(geo, mtd, - chip->ecc_strength_ds, chip->ecc_step_ds); + /* otherwise use the minimum ecc nand chips required */ + dev_dbg(mtd->dev, "use minimum ecc bch geometry\n"); + err = mxs_nand_calc_ecc_layout_by_info(geo, mtd, chip->ecc_strength_ds, + chip->ecc_step_ds); - return 0; + if (err) + dev_err(mtd->dev, "none of the bch geometry setting works\n"); + + return err; +} + +void mxs_nand_dump_geo(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; + + dev_dbg(mtd->dev, "BCH Geometry :\n" + "GF Length\t\t: %u\n" + "ECC Strength\t\t: %u\n" + "ECC for Meta\t\t: %u\n" + "ECC Chunk0 Size\t\t: %u\n" + "ECC Chunkn Size\t\t: %u\n" + "ECC Chunk Count\t\t: %u\n" + "Block Mark Byte Offset\t: %u\n" + "Block Mark Bit Offset\t: %u\n", + geo->gf_len, + geo->ecc_strength, + geo->ecc_for_meta, + geo->ecc_chunk0_size, + geo->ecc_chunkn_size, + geo->ecc_chunk_count, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset); } /* @@ -1159,6 +1210,8 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) if (ret) return ret; + mxs_nand_dump_geo(mtd); + /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index 878796d5552..b9833a646f0 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -92,8 +92,6 @@ static int mxs_nand_dt_probe(struct udevice *dev) info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc"); - info->legacy_bch_geometry = dev_read_bool(dev, "fsl,legacy-bch-geometry"); - if (IS_ENABLED(CONFIG_CLK) && IS_ENABLED(CONFIG_IMX8)) { /* Assigned clock already set clock */ struct clk gpmi_clk; diff --git a/include/mxs_nand.h b/include/mxs_nand.h index 66c909318d1..741dc8734ea 100644 --- a/include/mxs_nand.h +++ b/include/mxs_nand.h @@ -44,8 +44,6 @@ struct mxs_nand_info { struct udevice *dev; unsigned int max_ecc_strength_supported; bool use_minimum_ecc; - /* legacy bch geometry flag */ - bool legacy_bch_geometry; int cur_chip; uint32_t cmd_queue_len; -- cgit v1.3.1