diff --git a/.gitignore b/.gitignore index 56972adb5..8ce1f2678 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,4 @@ sphinx_*/ # Rust analyzer configuration /rust-project.json +*uImage diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 5e27292c1..939e65bfd 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -539,9 +539,20 @@ config BUILTIN_DTB Built-in DTBs are generic enough and can be used as references. +config DTB_MATCH_BY_BOARD_NAME + bool "dtb match by board_name which from command line" + depends on BUILTIN_DTB + default y + help + Base name (without suffix, relative to arch/loongarch/boot/dts/) + for the DTS file that will be used to produce the DTB linked into + the kernel. + support match dtb by board_name which from command line + so that bootloader given this value in command line + config BUILTIN_DTB_NAME string "Source file for built-in dtb" - depends on BUILTIN_DTB + depends on BUILTIN_DTB && !DTB_MATCH_BY_BOARD_NAME help Base name (without suffix, relative to arch/loongarch/boot/dts/) for the DTS file that will be used to produce the DTB linked into diff --git a/arch/loongarch/boot/dts/Makefile b/arch/loongarch/boot/dts/Makefile index 747d0c3f6..da5ba0253 100644 --- a/arch/loongarch/boot/dts/Makefile +++ b/arch/loongarch/boot/dts/Makefile @@ -1,5 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -dtb-y = loongson-2k0500-ref.dtb loongson-2k1000-ref.dtb loongson-2k2000-ref.dtb +# dtb-y = loongson-2k0500-ref.dtb loongson-2k1000-ref.dtb loongson-2k2000-ref.dtb +ifdef CONFIG_DTB_MATCH_BY_BOARD_NAME +dtb-$(CONFIG_LOONGSON_2K300) += ls2k300_mini_dp.dtb ls2k300_vanguard_pi.dtb ls2k300_tewei.dtb +obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) +else obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .dtb.o, $(CONFIG_BUILTIN_DTB_NAME)) +endif diff --git a/arch/loongarch/boot/dts/loongson2k300.dtsi b/arch/loongarch/boot/dts/loongson2k300.dtsi new file mode 100644 index 000000000..589649743 --- /dev/null +++ b/arch/loongarch/boot/dts/loongson2k300.dtsi @@ -0,0 +1,897 @@ +/dts-v1/; +#include +#include + +/ { + model = "loongson,generic"; + compatible = "loongson,loongson3"; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + serial4 = &uart4; + serial5 = &uart5; + serial6 = &uart6; + serial7 = &uart7; + serial8 = &uart8; + serial9 = &uart9; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + spi3 = &spi3; + ethernet0 = &gmac0; + ethernet1 = &gmac1; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + }; + }; + + cpu0: cpu@10000 { + device_type = "cpu"; + compatible = "loongarch"; + reg=<0>; + numa-node-id = <0>; + }; + + }; + + cpuintc: interrupt-controller { + compatible = "loongson,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + liointc0: icu0 { + compatible = "loongson,liointc-2.0"; + reg = <0 0x16001400 0 0x40>, + <0 0x16001040 0 0x8>; + reg-names = "main", "isr0"; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&cpuintc>; + interrupts = <2>; + interrupt-names = "int0"; + + loongson,parent_int_map = <0xffffffff>, /* int0 */ + <0x00000000>, /* int1 */ + <0x00000000>, /* int2 */ + <0x00000000>; /* int3 */ + }; + + liointc1: icu1 { + compatible = "loongson,liointc-2.0"; + reg = <0 0x16001440 0 0x40>, + <0 0x16001048 0 0x8>; + reg-names = "main", "isr0"; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&cpuintc>; + interrupts = <4>; + interrupt-names = "int2"; + + loongson,parent_int_map = <0x00000000>, /* int0 */ + <0x00000000>, /* int1 */ + <0xffffffff>, /* int2 */ + <0x00000000>; /* int3 */ + }; + + // icu: interrupt-controller@16001400 { + // compatible = "loongson,2k500-icu"; + // interrupt-controller; + // #interrupt-cells = <1>; + // reg = <0 0x16001400 0 0x40 + // 0 0x16001040 0 16>; + // interrupt-parent = <&cpuic>; + // interrupt-names = "cascade"; + // interrupts = <4>; + // }; + + // extioiic: interrupt-controller@16000100 { + // compatible = "loongson,extioi-interrupt-controller"; + // interrupt-controller; + // #interrupt-cells = <1>; + // interrupt-parent = <&cpuic>; + // interrupts = <3>; + // interrupt-names = "cascade"; + // vec_count=<128>; + // misc_func=<0x100>; + // eio_en_off=<27>; + // default-irq-domain; + // }; + + pinmux: pinmux@16000490 { + compatible = "pinctrl-single"; + reg = <0 0x16000490 0 0x20>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x3>; + + uart1_pins: pinmux_G42_G43_as_uart1 { + pinctrl-single,bits = <0x8 0x00f00000 0x00f00000>; + }; + + uart2_pins: pinmux_G44_G45_as_uart2 { + pinctrl-single,bits = <0x8 0x0f000000 0x0f000000>; + }; + + uart3_pins: pinmux_G46_G47_as_uart3 { + pinctrl-single,bits = <0x8 0xf0000000 0xf0000000>; + }; + + uart4_pins: pinmux_G62_G63_as_uart4 { + pinctrl-single,bits = <0xc 0xa0000000 0xf0000000>; + }; + + uart5_pins: pinmux_G64_G65_as_uart5 { + pinctrl-single,bits = <0x10 0x0000000a 0x0000000f>; + }; + + uart6_pins: pinmux_G60_G61_as_uart6 { + pinctrl-single,bits = <0xc 0x0a000000 0x0f000000>; + }; + + uart7_pins: pinmux_G68_G69_as_uart7 { + pinctrl-single,bits = <0x10 0x00000a00 0x00000f00>; + }; + + uart8_pins: pinmux_G70_G71_as_uart8 { + pinctrl-single,bits = <0x10 0x0000a000 0x0000f000>; + }; + + uart9_pins: pinmux_G66_G67_as_uart9 { + pinctrl-single,bits = <0x10 0x000000a0 0x000000f0>; + }; + + gmac1_pins: pinmux_G44_G55_as_gmac1 { + pinctrl-single,bits = <0x8 0x55000000 0xff000000 + 0xc 0x00005555 0x0000ffff>; + }; + i2c0_pins: pinmux_G48_G49_as_i2c0 { + pinctrl-single,bits = <0xc 0x0000000f 0x0000000f>; + }; + + i2c1_pins: pinmux_G50_G51_as_i2c1 { + pinctrl-single,bits = <0xc 0x000000f0 0x000000f0>; + }; + + i2c1_as_gpio: pinmux_G50_G51_as_gpio { + pinctrl-single,bits = <0xc 0x00000000 0x000000f0>; + }; + + i2c2_pins: pinmux_G52_G53_as_i2c2 { + pinctrl-single,bits = <0xc 0x00000f00 0x00000f00>; + }; + + i2c3_pins: pinmux_G54_G55_as_i2c3 { + pinctrl-single,bits = <0xc 0x0000f000 0x0000f000>; + }; + + spi1_pins: pinmux_G60_G63_as_spi1 { + pinctrl-single,bits = <0xc 0xff000000 0xff000000>; + }; + + spi2_pins: pinmux_G64_G67_as_spi2 { + pinctrl-single,bits = <0x10 0x000000ff 0x000000ff>; + }; + + spi2_cs_as_gpio: pinmux_G64_G66_as_spi2 { + pinctrl-single,bits = <0x10 0x0000003f 0x000000ff>; + }; + + can0_pins: pinmux_G68_G69_as_can0 { + pinctrl-single,bits = <0x10 0x00000f00 0x00000f00>; + }; + + can0_as_gpio_pins: pinmux_G68_G69_as_gpio { + pinctrl-single,bits = <0x10 0x00000000 0x00000f00>; + }; + + can1_pins: pinmux_G70_G71_as_can1 { + pinctrl-single,bits = <0x10 0x0000f000 0x0000f000>; + }; + + can1_as_gpio_pins: pinmux_G70_G71_as_gpio { + pinctrl-single,bits = <0x10 0x00000000 0x0000f000>; + }; + + can2_pins: pinmux_G72_G73_as_can2 { + pinctrl-single,bits = <0x10 0x000f0000 0x000f0000>; + }; + + can2_as_gpio_pins: pinmux_G72_G73_as_gpio { + pinctrl-single,bits = <0x10 0x00000000 0x000f0000>; + }; + + can3_pins: pinmux_G74_G75_as_can3 { + pinctrl-single,bits = <0x10 0x00f00000 0x00f00000>; + }; + + can3_as_gpio_pins: pinmux_G74_G75_as_gpio { + pinctrl-single,bits = <0x10 0x00000000 0x00f00000>; + }; + + i2s_pins: pinmux_G76_G80_as_i2s { + pinctrl-single,bits = <0x10 0xff000000 0xff000000 + 0x14 0x00000003 0x00000003>; + }; + + pwm0_pins: pinmux_G86_as_pwm0 { + pinctrl-single,bits = <0x14 0x00002000 0x00003000>; + }; + + pwm1_pins: pinmux_G87_as_pwm1 { + pinctrl-single,bits = <0x14 0x00008000 0x0000c000>; + }; + + pwm2_pins: pinmux_G88_as_pwm2 { + pinctrl-single,bits = <0x14 0x00020000 0x00030000>; + }; + + pwm3_pins: pinmux_G89_as_pwm3 { + pinctrl-single,bits = <0x14 0x00080000 0x000c0000>; + }; + + tim1_ch1:pinmux_G81_as_tim1_ch1 { + pinctrl-single,bits = <0x14 0x0000000c 0x0000000c>; + }; + + tim1_ch1n:pinmux_G84_as_tim1_ch1n { + pinctrl-single,bits = <0x14 0x00000300 0x000000300>; + }; + + tim1_ch2n:pinmux_G85_as_tim1_ch2n { + pinctrl-single,bits = <0x14 0x00000c00 0x000000c00>; + }; + + tim1_ch3n:pinmux_G86_as_tim1_ch3n { + pinctrl-single,bits = <0x14 0x00003000 0x000003000>; + }; + + tim2_ch1:pinmux_G87_as_tim2_ch1 { + pinctrl-single,bits = <0x14 0x0000c000 0x00000c000>; + }; + + tim2_ch2:pinmux_G88_as_tim2_ch2 { + pinctrl-single,bits = <0x14 0x00030000 0x00030000>; + }; + + sdio0_pins: pinmux_G90_G99_as_sdio { + pinctrl-single,bits = <0x14 0xfff00000 0xfff00000 + 0x18 0x000000ff 0x000000ff>; + }; + + sdio1_pins: pinmux_G100_G105_as_sdio { + pinctrl-single,bits = <0x18 0x000fff00 0x000fff00>; + }; + }; + + soc: 2k300-soc { + compatible = "ls,nbus", "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0 0x10000000 0 0x10000000 0 0x10000000 + 0 0x2000000 0 0x2000000 0 0x4000000 + 0 0x40000000 0 0x40000000 0 0x40000000 + 0xfe 0x00000000 0xfe 0x00000000 0 0x40000000>; + + osc_clk: osc-clock { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <120000000>; + clock-output-names = "ref_clk"; + }; + + clocks:clock-controller@0x16000400{ + compatible = "loongson,ls2k300-clk"; + reg = <0 0x16000400 0 0x28>; + clocks = <&osc_clk>; + clock-names = "ref_clk"; + #clock-cells = <1>; + }; + + isa@16400000 { + compatible = "isa"; + #size-cells = <1>; + #address-cells = <2>; + ranges = <1 0 0 0x16400000 0x4000>; + }; + + uart0: serial@0x16100000 { + compatible = "ns16550a"; + reg = <0 0x16100000 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + }; + + uart1: serial@0x16100400 { + compatible = "ns16550a"; + reg = <0 0x16100400 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart1_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart2: serial@0x16100800 { + compatible = "ns16550a"; + reg = <0 0x16100800 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart2_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart3: serial@0x16100c00 { + compatible = "ns16550a"; + reg = <0 0x16100c00 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart3_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart4: serial@0x16101000 { + compatible = "ns16550a"; + reg = <0 0x16101000 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&uart4_pins>; + pinctrl-names = "default"; + no-loopback-test; + status = "disabled"; + }; + + uart5: serial@0x16101400 { + compatible = "ns16550a"; + reg = <0 0x16101400 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart5_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart6: serial@0x16101800 { + compatible = "ns16550a"; + reg = <0 0x16101800 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart6_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart7: serial@0x16101c00 { + compatible = "ns16550a"; + reg = <0 0x16101c00 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart7_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart8: serial@0x16102000 { + compatible = "ns16550a"; + reg = <0 0x16102000 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart8_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart9: serial@0x16102400 { + compatible = "ns16550a"; + reg = <0 0x16102400 0 0x10>; + clock-frequency = <200000000>; + interrupt-parent = <&liointc0>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + no-loopback-test; + pinctrl-0 = <&uart9_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + + dc: dc@0x16090000 { + compatible = "loongson,ls2k0300-dc"; + reg = <0 0x16090000 0 0x00010000>; + interrupt-parent = <&liointc1>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; // 51 - 32 = 19 + dma-mask = <0x00000000 0xffffffff>; + + #address-cells = <1>; + #size-cells = <0>; + + lsdc,relax_alignment; + + status = "disabled"; + + dvo0: dvo@0 { + compatible = "lsdc,dvo"; + /* 0 for connector 0 (DVO0) */ + reg = <0>; + connector = "panel"; + }; + }; + + wdt: syscon@0x16124000 { + compatible = "loongson,ls2x-wdt", "syscon"; + reg = <0 0x16124000 0 0xC>; + clocks = <&clocks CLK_APB>; + clock-names = "wdt-clk"; + status = "okay"; + }; + + reboot { + compatible ="syscon-reboot"; + regmap = <&wdt>; + offset = <0x00>; + mask = <0x1>; + }; + + adc: adc@0x1611c000 { + compatible = "ls2k300-adc"; + reg = <0 0x1611c000 0 0x50>; + status = "disabled"; + }; + + gmac0: ethernet@0x16020000 { + compatible = "snps,dwmac-3.70a"; + reg = <0 0x16020000 0 0x7fff>; + interrupt-parent = <&liointc1>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; // 49 - 32 = 17 + interrupt-names = "macirq"; + dma-mask = <0xffffffff 0xffffffff>; + phy-mode = "rgmii-id"; + // pinctrl-0 = <&gmac0_pins>; + // pinctrl-names = "default"; + snps,pbl = <32>; + status = "disabled"; + }; + + gmac1: ethernet@0x16030000 { + compatible = "snps,dwmac-3.70a"; + reg = <0 0x16030000 0 0x7fff>; + interrupt-parent = <&liointc1>; + interrupts = <18 IRQ_TYPE_LEVEL_HIGH>; // 50 - 32 = 18 + interrupt-names = "macirq"; + dma-mask = <0xffffffff 0xffffffff>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&gmac1_pins>; + pinctrl-names = "default"; + snps,pbl = <32>; + status = "disabled"; + }; + + gpio: gpio@0x16104000 { + compatible = "loongson,ls2k0300-gpio"; + reg = <0 0x16104000 0 0x1000>; + ngpios = <106>; + gpio_base = <0>; + gpio-controller; + #gpio-cells = <2>; + loongson,vhwirq-base = <128>; + interrupt-controller; + #interrupt-cells = <2>; + // 可选传统中断与扩展中断 + #if 1 + interrupt-parent =<&liointc1>; + interrupts = + <21 IRQ_TYPE_LEVEL_HIGH>, <22 IRQ_TYPE_LEVEL_HIGH>, <23 IRQ_TYPE_LEVEL_HIGH>, <24 IRQ_TYPE_LEVEL_HIGH>, + <25 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>; + // base 32 x - 32 + loongson,irqpin-group = + <0 16>, <16 16>, <32 16>, <48 16>, + <64 16>, <80 16>, <96 11>; + #else + interrupt-parent =<&extioiic>; + interrupts = + <79>, <80>, <81>, <82>, + <83>, <84>, <85>, <86>, + <87>, <88>, <89>, <90>, + <91>, <92>, <93>, <94>, + <95>, <96>, <97>, <98>, + <99>, <100>, <101>, <102>, + <103>, <104>, <105>; + loongson,irqpin-group = + <0 4>, <4 4>, <8 4>, <12 4>, + <16 4>, <20 4>, <24 4>, <28 4>, + <32 4>, <36 5>, <40 4>, <44 4>, + <48 4>, <52 4>, <56 4>, <60 4>, + <64 4>, <68 4>, <72 4>, <76 4>, + <80 4>, <84 4>, <88 4>, <92 4>, + <96 4>, <100 4>, <104 2>; + #endif + }; + + can0: can@0x16110000 { + compatible = "loongson,ls-canfd"; + reg = <0 0x16110000 0 0x400>; + ls,clock-frequency = <200000000>; + ls,canfd-dmarx; + interrupt-parent = <&liointc0>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&can0_pins>; + pinctrl-1 = <&can0_as_gpio_pins>; + pinctrl-names = "default", "isolate"; + ls,canfd-tx-gpio = <&gpio 69 GPIO_ACTIVE_HIGH>; + dmas = <&dma 0 0xaae>; + dma-names = "rx"; + status = "disabled"; + }; + + can1: can@0x16111000 { + compatible = "loongson,ls-canfd"; + reg = <0 0x16110400 0 0x400>; + ls,clock-frequency = <200000000>; + ls,canfd-dmarx; + interrupt-parent = <&liointc0>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&can1_pins>; + pinctrl-1 = <&can1_as_gpio_pins>; + pinctrl-names = "default", "isolate"; + ls,canfd-tx-gpio = <&gpio 71 GPIO_ACTIVE_HIGH>; + dmas = <&dma 1 0xaae>; + dma-names = "rx"; + status = "disabled"; + }; + + can2: can@0x16112000 { + compatible = "loongson,ls-canfd"; + reg = <0 0x16110800 0 0x400>; + ls,clock-frequency = <200000000>; + ls,canfd-dmarx; + interrupt-parent = <&liointc0>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&can2_pins>; + pinctrl-1 = <&can2_as_gpio_pins>; + pinctrl-names = "default", "isolate"; + ls,canfd-tx-gpio = <&gpio 73 GPIO_ACTIVE_HIGH>; + dmas = <&dma 2 0xaae>; + dma-names = "rx"; + status = "disabled"; + }; + + can3: can@0x16113000 { + compatible = "loongson,ls-canfd"; + reg = <0 0x16110c00 0 0x400>; + ls,clock-frequency = <200000000>; + ls,canfd-dmarx; + interrupt-parent = <&liointc0>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&can3_pins>; + pinctrl-1 = <&can3_as_gpio_pins>; + pinctrl-names = "default", "isolate"; + ls,canfd-tx-gpio = <&gpio 75 GPIO_ACTIVE_HIGH>; + dmas = <&dma 3 0xaae>; + dma-names = "rx"; + status = "disabled"; + }; + + spi0: spi@0x16010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "loongson,ls-spi"; + reg = <0 0x16010000 0 0x10>; + interrupt-parent = <&liointc1>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; // 44 - 32 = 12 + clock-frequency = <200000000>; + status = "disabled"; + }; + + spi1: spi@0x16018000 { + compatible = "loongson,ls-spi"; + reg = <0 0x16018000 0 0x10>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&liointc1>; + interrupts = <13 IRQ_TYPE_LEVEL_HIGH>; // 45 - 32 = 13 + clock-frequency = <200000000>; + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + spi2: spi@0x1601c000 { + compatible = "loongson,ls-spi-io"; + reg = <0 0x1610c000 0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <200000000>; + pinctrl-0 = <&spi2_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + spi3: spi@0x1601e000 { + compatible = "loongson,ls-spi-io"; + reg = <0 0x1610e000 0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <200000000>; + status = "disabled"; + }; + + i2c0: i2c0@0x16108000 { + compatible = "loongson,lsfs-i2c"; + reg = <0 0x16108000 0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&liointc0>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + i2c1: i2c1@0x16109000 { + compatible = "loongson,lsfs-i2c"; + reg = <0 0x16109000 0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&liointc0>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + i2c2: i2c2@0x1610a000 { + compatible = "loongson,lsfs-i2c"; + reg = <0 0x1610a000 0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&liointc0>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + i2c3: i2c3@0x1610b000 { + compatible = "loongson,lsfs-i2c"; + reg = <0 0x1610b000 0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&liointc0>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + dma: dma@0x1612c000{ + compatible = "loongson,lsia-dma"; + reg = <0 0x1612c000 0 0x100>; + #config-nr = <2>; + interrupt-parent = <&liointc0>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>, <24 IRQ_TYPE_LEVEL_HIGH>, + <25 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>, + <27 IRQ_TYPE_LEVEL_HIGH>, <28 IRQ_TYPE_LEVEL_HIGH>, + <29 IRQ_TYPE_LEVEL_HIGH>, <30 IRQ_TYPE_LEVEL_HIGH>; + #dma-cells = <2>; + dma-channels = <8>; + }; + + emmc: sdio@0x16140000 { + #address-cells = <2>; + compatible = "loongson,ls2k_sdio_1.2"; + reg = <0 0x16140000 0 0x1000>; + interrupt-parent = <&liointc0>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + //interrupt-parent = <&icu>; + //interrupts = <53>; + interrupt-names = "ls2k_mci_irq"; + dma-mask = <0xffffffff 0xffffffff>; + clock-frequency = <0 200000000>; + bus-width = <8>; + cap-mmc-highspeed; +#if CONFIG_LOONGSON_MMC_HIGH_PERFORMANCE + mmc-hs200-1_8v; +#endif + no-sd; + no-sdio; + }; + + sdio1: sdio@0x16148000 { + #address-cells = <2>; + compatible = "loongson,ls2k_sdio_1.2"; + reg = <0 0x16148000 0 0x8000>; + interrupt-parent = <&liointc1>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; // 32 - 32 = 0 + interrupt-names = "ls2k_mci_irq"; + dma-mask = <0xffffffff 0xffffffff>; + clock-frequency = <0 200000000>; + status = "disabled"; + }; + + ohci@0x16088000 { + compatible = "generic-ohci"; + reg = <0 0x16088000 0 0x8000>; + interrupt-parent = <&liointc1>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; // 47 - 32 = 15 + dma-mask = <0x0 0xffffffff>; + }; + + ehci@0x16080000 { + compatible = "generic-ehci"; + reg = <0 0x16080000 0 0x8000>; + interrupt-parent = <&liointc1>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH>; // 46 - 32 = 14 + dma-mask = <0x0 0xffffffff>; + }; + + otg@0x16040000 { + compatible = "loongson,loongson2-dwc2"; + reg = <0 0x16040000 0 0x40000>; + interrupt-parent = <&liointc1>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; // 48 - 32 = 16 + dma-mask = <0x0 0xffffffff>; + loongson,dwc2-fix = ; + //dr_mode = "peripheral"; + /* Optional for that dr_mode = "host" or dr_mode = "peripheral" */ + }; + + pwm0: pwm@0x1611b000 { + compatible = "loongson,ls2k-pwm"; + reg = <0 0x1611b000 0 0xf>; + interrupt-parent = <&liointc0>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; + #pwm-cells = <2>; + pinctrl-0 = <&pwm0_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + pwm1: pwm@0x1611b010 { + compatible = "loongson,ls2k-pwm"; + reg = <0 0x1611b010 0 0xf>; + interrupt-parent = <&liointc0>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; + #pwm-cells = <2>; + pinctrl-0 = <&pwm1_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + pwm2: pwm@0x1611b020 { + compatible = "loongson,ls2k-pwm"; + reg = <0 0x1611b020 0 0xf>; + interrupt-parent = <&liointc0>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + #pwm-cells = <2>; + pinctrl-0 = <&pwm2_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + pwm3: pwm@0x1611b030 { + compatible = "loongson,ls2k-pwm"; + reg = <0 0x1611b030 0 0xf>; + interrupt-parent = <&liointc0>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + #pwm-cells = <2>; + pinctrl-0 = <&pwm3_pins>; + pinctrl-names = "default"; + clock-frequency = <200000000>; + status = "disabled"; + }; + + rtc0: rtc@0x16128000{ + compatible = "loongson,ls2k-rtc"; + reg = <0 0x16128000 0 0x100>; + interrupt-parent = <&liointc1>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; // 41 - 32 = 9 + status = "disabled"; + }; + + tsensor: tsensor@0x16001500 { + compatible = "loongson,ls2k300-thermal"; + reg = <0 0x16001500 0 0x30>; + id = <0>; + interrupt-parent = <&liointc1>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; // 52 - 32 = 20 + #thermal-sensor-cells = <1>; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + thermal-sensors = <&tsensor 0>; + + trips { + cpu_alert: cpu-alert { + temperature = <55000>; + hysteresis = <5000>; + type = "active"; + }; + + cpu_crit: cpu-crit { + temperature = <125000>; + hysteresis = <5000>; + type = "critical"; + }; + }; + }; + }; + + i2s: i2s@0x16114000{ + compatible = "loongson,ls2k0300-i2s"; + reg = <0 0x16114000 0 0x4000>; + interrupt-parent = <&liointc0>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <100000000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2s_pins>; + status = "disabled"; + }; + + audio: audio@0x16114000 { + compatible = "loongson,ls-pcm-generic-audio"; + reg = <0 0x16114000 0 0x4000>; + dmas = <&dma 0 0xaf + &dma 1 0xbf>; + dma-names = "i2s_record", "i2s_play"; + dma-mask = <0xffffffff 0xffffffff>; + status = "disabled"; + }; + + sound: sound{ + compatible = "loongson,ls-sound"; + loongson,i2s-controller = <&i2s>; + status = "disabled"; + }; + }; +}; diff --git a/arch/loongarch/boot/dts/loongson_panel_param.dtsi b/arch/loongarch/boot/dts/loongson_panel_param.dtsi new file mode 100644 index 000000000..7f4fbc81d --- /dev/null +++ b/arch/loongarch/boot/dts/loongson_panel_param.dtsi @@ -0,0 +1,98 @@ +/ { + panel_param: panel_param_set { + #address-cells = <1>; + #size-cells = <0>; + + panel-BP101WX1-206 { + width-mm = <223>; + height-mm = <125>; + label = "BP101WX1-206"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <71000000>; + hactive = <1280>; + vactive = <800>; + hsync-len = <32>; + hfront-porch = <8>; + hback-porch = <10>; + vfront-porch = <3>; + vback-porch = <3>; + vsync-len = <6>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + }; + + panel-ATK-MD0430R-800x480 { + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0430R-800x480"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <27000000>; + hactive = <800>; + vactive = <480>; + hsync-len = <40>; + hfront-porch = <48>; + hback-porch = <40>; + vfront-porch = <13>; + vback-porch = <32>; + vsync-len = <0>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + }; + + panel-ATK-MD0700R-1024x600 { + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0700R-1024x600"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <51200000>; + hactive = <1024>; + vactive = <600>; + hsync-len = <20>; + hfront-porch = <160>; + hback-porch = <140>; + vfront-porch = <12>; + vback-porch = <20>; + vsync-len = <3>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + }; + + panel-ATK-MD1010R-1280x800{ + width-mm = <229>; + height-mm = <149>; + label = "ATK-MD1010R-1280x800"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <71100000>; + hactive = <1280>; + vactive = <800>; + hsync-len = <10>; + hfront-porch = <70>; + hback-porch = <80>; + vfront-porch = <10>; + vback-porch = <10>; + vsync-len = <3>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + }; + }; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_g2k03z0_core_v10.dtsi b/arch/loongarch/boot/dts/ls2k300_g2k03z0_core_v10.dtsi new file mode 100644 index 000000000..f43bd695c --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_g2k03z0_core_v10.dtsi @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#include "loongson2k300.dtsi" + +/ { + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "earlycon"; + }; + + /* not need memory node, we get memory info from boot params. */ + //memory { + // name = "memory"; + // device_type = "memory"; + // reg = <0 0x00200000 0 0x0ee00000 + // 0 0x90000000 0 0x10000000>; + //}; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0 0x02000000>; + linux,cma-default; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + spi-flash@0 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <25000000>; + reg = <0>; + }; +}; + +&gmac0 { + status = "okay"; + phy-handle = <&yt8511h0>; + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + yt8511h0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + motorcomm,led0_cfg = <0x40>; //千兆常亮 + motorcomm,led1_cfg = <0x30>; //百兆 十兆常亮 + motorcomm,led2_cfg = <0x2600>; //有数据时闪烁 + }; + }; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_mini_dp.dts b/arch/loongarch/boot/dts/ls2k300_mini_dp.dts new file mode 100644 index 000000000..0442e7676 --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_mini_dp.dts @@ -0,0 +1,228 @@ +#include "ls2k300_g2k03z0_core_v10.dtsi" +#include "loongson_panel_param.dtsi" +#include +#include + +/ { + model = "LS2K300-MINI-DP"; + + leds { + compatible = "gpio-leds"; + + led1 { + label = "led1"; + gpios = <&gpio 85 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + + }; + + lcd_backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpio 84 GPIO_ACTIVE_HIGH>; + default-on; + }; + + panel: dvo-connector@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "panel-lvds", "auo,b101ean01"; + backlight = <&lcd_backlight>; + + /* lcd power on control gpio */ + // enable-gpios = <&pioA 3 GPIO_ACTIVE_HIGH>; + // power-supply = <&vdd_lcd>; +#if 0 // 4.3' LCD + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0430R-800x480"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <27000000>; + hactive = <800>; + vactive = <480>; + hsync-len = <40>; + hfront-porch = <48>; + hback-porch = <40>; + vfront-porch = <13>; + vback-porch = <32>; + vsync-len = <1>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#else // 7' LCD + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0700R-1024x600"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <51200000>; + hactive = <1024>; + vactive = <600>; + hsync-len = <20>; + hfront-porch = <160>; + hback-porch = <140>; + vfront-porch = <12>; + vback-porch = <20>; + vsync-len = <3>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#endif + port@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + dvo_connector_in: endpoint@0 { + reg = <0>; + remote-endpoint = <&dc_out_rgb0>; + }; + }; + }; +}; + +&dc { + status = "ok"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dc_out_rgb0: endpoint { + remote-endpoint = <&dvo_connector_in>; + }; + }; + }; +}; + +&i2c0 { + status = "ok"; +}; + +&i2c1 { + status = "ok"; + + // 两款芯片不能同时支持,因为用同一个GPIO + // 但可以在两个屏幕间切换 + + // 4.3寸触屏芯片 + touchpad: gt9147@14 { + compatible = "goodix,gt9147"; + reg = <0x14>; + // 2K300 GPIO 支持中断控制 + // GPIO 中断号为 vhwirq-base + pinnum (210 = 128 + 82) + interrupt-parent = <&gpio>; + interrupts = <210 IRQ_TYPE_EDGE_FALLING>; + irq-gpios = <&gpio 82 GPIO_ACTIVE_LOW>; + }; + + // 7寸触屏芯片 + touchpad_1: gt911@5d { + compatible = "goodix,gt911"; + reg = <0x5d>; + interrupt-parent = <&gpio>; + interrupts = <210 IRQ_TYPE_EDGE_FALLING>; + irq-gpios = <&gpio 82 GPIO_ACTIVE_LOW>; + }; +}; + +&i2c2 { + status = "ok"; +}; + +&i2c3 { + status = "ok"; +}; + +&uart1 { + status = "ok"; +}; + +&uart2 { + status = "ok"; +}; + +&uart3 { + status = "ok"; +}; + +&pwm0 { + status = "ok"; +}; + +&pwm1 { + status = "ok"; +}; + +&pwm2 { + status = "ok"; +}; + +&pwm3 { + status = "ok"; +}; + +&can0 { + status = "ok"; +}; + +&can1 { + status = "ok"; +}; + +&can2 { + status = "ok"; +}; + +&can3 { + status = "ok"; +}; + +&spi1 { + status = "ok"; + + spidev0@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <50000000>; + }; +}; + +&spi2 { + status = "ok"; + + spidev1@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <25000000>; + }; +}; + +&adc { + status = "okay"; +}; + +&rtc0 { + status = "okay"; +}; + +&sdio1 { + pinctrl-0 = <&sdio1_pins>; + pinctrl-names = "default"; + cap-sd-highspeed; + cap-mmc-highspeed; + bus-width = <4>; + cd-gpios = <&gpio 81 GPIO_ACTIVE_LOW>; + status = "okay"; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_tewei.dts b/arch/loongarch/boot/dts/ls2k300_tewei.dts new file mode 100644 index 000000000..8da3d418e --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_tewei.dts @@ -0,0 +1,234 @@ +#include "ls2k300_g2k03z0_core_v10.dtsi" +#include "loongson_panel_param.dtsi" +#include +#include + +/ { + model = "LS2K300-TEWEI"; + + leds { + compatible = "gpio-leds"; + + run_led { + label = "run_led"; + gpios = <&gpio 85 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + + err_led { + label = "err_led"; + gpios = <&gpio 82 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + }; + + lcd_backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm0 0 5000000 PWM_POLARITY_INVERTED>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + + panel: dvo-connector@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "panel-lvds", "auo,b101ean01"; + backlight = <&lcd_backlight>; + + width-mm = <261>; + height-mm = <163>; + label = "TM121JDGP30-1280x800"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <72200000>; + hactive = <1280>; + vactive = <800>; + hsync-len = <4>; + hfront-porch = <72>; + hback-porch = <84>; + vfront-porch = <15>; + vback-porch = <23>; + vsync-len = <4>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + + port@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + dvo_connector_in: endpoint@0 { + reg = <0>; + remote-endpoint = <&dc_out_rgb0>; + }; + }; + }; + + vccio_codec: 3_3V{ + compatible = "regulator-fixed"; + regulator-name = "VCC_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + // 虽然这个时钟在从模式下没用,但 es8388 驱动必须指定这个时钟 + es8388_clk: es8388-clock { + compatible = "fixed-clock"; + clock-frequency = <12288000>; + #clock-cells = <1>; + }; +}; + +&dc { + status = "ok"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dc_out_rgb0: endpoint { + remote-endpoint = <&dvo_connector_in>; + }; + }; + }; +}; + +&i2c0 { + status = "ok"; + + lm75bdp@48 { + compatible = "national,lm75b"; + reg = <0x48>; + }; + + eeprom@50 { + compatible = "atmel,24c64"; + reg = <0x50>; + pagesize = <32>; + }; +}; + +&i2c1 { + status = "ok"; + + codec: es8388@11{ + compatible = "everest,es8388"; + reg = <0x11>; + AVDD-supply = <&vccio_codec>; + DVDD-supply = <&vccio_codec>; + HPVDD-supply = <&vccio_codec>; + PVDD-supply = <&vccio_codec>; + clocks = <&es8388_clk 0>; + }; +}; + +&i2c2 { + status = "ok"; + + pcf8574@20 { + compatible = "nxp,pcf8574"; + reg = <0x20>; + // 2K300 GPIO 支持中断控制 + // GPIO 中断号为 vhwirq-base + pinnum (216 = 128 + 88) + interrupt-parent = <&gpio>; + interrupts = <216 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; +}; + +&i2c3 { + status = "ok"; +}; + +&uart1 { + status = "ok"; +}; + +&uart2 { + status = "ok"; +}; + +&uart3 { + status = "ok"; +}; + +&can2 { + status = "ok"; +}; + +&spi1 { + status = "ok"; + + spi_wk2xxx0: spi_wk2xxx@0 { + status = "okay"; + compatible = "wkmic,wk2124_spi"; + reg = <0x0>; + spi-max-frequency = <10000000>; + reset-gpio = <&gpio 83 GPIO_ACTIVE_HIGH>; + irq-gpio = <&gpio 84 IRQ_TYPE_EDGE_FALLING>; + use-common-reset-gpio; + }; +}; + +&spi2 { + status = "ok"; + + spi_wk2xxx1: spi_wk2xxx@0 { + status = "okay"; + compatible = "wkmic,wk2124_spi"; + reg = <0x0>; + spi-max-frequency = <10000000>; + reset-gpio = <&gpio 83 GPIO_ACTIVE_HIGH>; + irq-gpio = <&gpio 69 IRQ_TYPE_EDGE_FALLING>; + use-common-reset-gpio; + }; +}; + +&rtc0 { + status = "okay"; +}; + +&sdio1 { + pinctrl-0 = <&sdio1_pins>; + pinctrl-names = "default"; + cap-sd-highspeed; + cap-mmc-highspeed; + bus-width = <4>; + cd-gpios = <&gpio 81 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pwm0 { + status = "okay"; + #pwm-cells = <3>; +}; + +&i2s { + status = "okay"; + //slave-mode; + //mclk-fixed = <12288000>; +}; + +&audio { + status = "okay"; +}; + +&sound { + status = "okay"; + loongson,audio-codec = <&codec>; + // Easy Compatible for asound.state Configure + loongson,sound-card-name = "ES8388"; +}; + diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi.dts b/arch/loongarch/boot/dts/ls2k300_vanguard_pi.dts new file mode 100644 index 000000000..52d939b34 --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi.dts @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include "ls2k300_vanguard_pi_common.dtsi" + +&uart2 { + status = "ok"; +}; + +&uart5 { + status = "disabled"; +}; + +&uart6 { + status = "disabled"; +}; + +&uart7 { + status = "disabled"; +}; + +&uart8 { + status = "disabled"; +}; + +&spi1 { + status = "okay"; + + spidev0@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <25000000>; + }; +}; + +&spi2 { + status = "ok"; + pinctrl-0 = <&spi2_cs_as_gpio>; + pinctrl-names = "default"; + num-chipselects = <2>; + cs-gpios = <&gpio 67 0>, + <&gpio 85 0>; + + spidev1@0 { + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <25000000>; + }; + spidev2@1 { + compatible = "rohm,dh2228fv"; + reg = <1>; + spi-max-frequency = <25000000>; + }; +}; + +&can0 { + status = "disabled"; +}; + +&can1 { + status = "disabled"; +}; + +&can2 { + status = "disabled"; +}; + +&can3 { + status = "disabled"; +}; + +&pwm0 { + status = "okay"; +}; + +&pwm1 { + status = "okay"; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi_common.dtsi b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_common.dtsi new file mode 100644 index 000000000..229974c47 --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_common.dtsi @@ -0,0 +1,251 @@ +#include +#include +#include +#include + +#include "loongson2k300.dtsi" +#include "loongson_panel_param.dtsi" + +/ { + model = "LS2K300-PAI"; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "earlycon"; + }; + + /* not need memory node, we get memory info from boot params. */ + + leds { + compatible = "gpio-leds"; + + led1 { + label = "led1"; + gpios = <&gpio 54 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + }; + + lcd_backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; + default-on; + }; + + panel: dvo-connector@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "panel-lvds", "auo,b101ean01"; + backlight = <&lcd_backlight>; + + /* lcd power on control gpio */ + // enable-gpios = <&pioA 3 GPIO_ACTIVE_HIGH>; + // power-supply = <&vdd_lcd>; +#if 0 // 4.3' LCD + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0430R-800x480"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <27000000>; + hactive = <800>; + vactive = <480>; + hsync-len = <40>; + hfront-porch = <48>; + hback-porch = <40>; + vfront-porch = <13>; + vback-porch = <32>; + vsync-len = <1>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#else // 7' LCD + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0700R-1024x600"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <51200000>; + hactive = <1024>; + vactive = <600>; + hsync-len = <20>; + hfront-porch = <160>; + hback-porch = <140>; + vfront-porch = <12>; + vback-porch = <20>; + vsync-len = <3>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#endif + port@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + dvo_connector_in: endpoint@0 { + reg = <0>; + remote-endpoint = <&dc_out_rgb0>; + }; + }; + }; + + vccio_codec: 3_3V{ + compatible = "regulator-fixed"; + regulator-name = "VCC_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + // 虽然这个时钟在从模式下没用,但 es8388 驱动必须指定这个时钟 + es8388_clk: es8388-clock { + compatible = "fixed-clock"; + clock-frequency = <12288000>; + #clock-cells = <1>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + spi-flash@0 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <25000000>; + reg = <0>; + }; +}; + +&gmac0 { + status = "okay"; + phy-handle = <&yt8511h0>; + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + yt8511h0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + rx-internal-delay-ps = <450>; + tx-internal-delay-ps = <150>; + motorcomm,led0_cfg = <0x40>; //千兆常亮 + motorcomm,led1_cfg = <0x30>; //百兆 十兆常亮 + motorcomm,led2_cfg = <0x2600>; //有数据时闪烁 + }; + }; +}; + +&dc { + status = "ok"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dc_out_rgb0: endpoint { + remote-endpoint = <&dvo_connector_in>; + }; + }; + }; +}; + +&i2c0 { + status = "ok"; + + touchpad: gt9147@14 { + compatible = "goodix,gt9147"; + reg = <0x14>; + // 2K300 GPIO 支持中断控制 + // GPIO 中断号为 vhwirq-base + pinnum (170 = 128 + 42) + interrupt-parent = <&gpio>; + interrupts = <170 IRQ_TYPE_EDGE_FALLING>; + irq-gpios = <&gpio 42 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 55 GPIO_ACTIVE_LOW>; + }; +}; + +&i2c1 { + status = "ok"; +}; + +&i2c2 { + status = "okay"; + + codec: es8388@11{ + compatible = "everest,es8388"; + reg = <0x11>; + AVDD-supply = <&vccio_codec>; + DVDD-supply = <&vccio_codec>; + HPVDD-supply = <&vccio_codec>; + PVDD-supply = <&vccio_codec>; + clocks = <&es8388_clk 0>; + }; +}; + + +&can0 { + status = "disabled"; +}; + +&can1 { + status = "disabled"; +}; + +&can2 { + status = "disabled"; +}; + +&can3 { + status = "disabled"; +}; + +&adc { + status = "okay"; +}; + +&rtc0 { + status = "okay"; +}; + +&sdio1 { + pinctrl-0 = <&sdio1_pins>; + pinctrl-names = "default"; + cap-sd-highspeed; + cap-mmc-highspeed; + bus-width = <4>; + cd-gpios = <&gpio 82 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&i2s { + status = "okay"; + //slave-mode; + //mclk-fixed = <12288000>; +}; + +&audio { + status = "okay"; +}; + +&sound { + status = "okay"; + loongson,audio-codec = <&codec>; + // Easy Compatible for asound.state Configure + loongson,sound-card-name = "ES8388"; +}; + diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi_factory.dts b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_factory.dts new file mode 100644 index 000000000..d3aabfabd --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_factory.dts @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +#include "ls2k300_vanguard_pi_common.dtsi" + +/ { + factory { + tags = "factory-test"; + }; +}; + +&uart2 { + status = "disable"; +}; + +&uart5 { + status = "disabled"; +}; + +&uart6 { + status = "disabled"; +}; + +&uart7 { + status = "disabled"; +}; + +&uart8 { + status = "disabled"; +}; + +&can0 { + status = "disabled"; +}; + +&can1 { + status = "disabled"; +}; + +&can2 { + status = "disabled"; +}; + +&can3 { + status = "disabled"; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi_func2.dts b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_func2.dts new file mode 100644 index 000000000..352fec0d9 --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_func2.dts @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "ls2k300_vanguard_pi_common.dtsi" + +&uart2 { + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +&uart5 { + status = "okay"; +}; + +&uart6 { + status = "okay"; +}; + +&uart7 { + status = "disabled"; +}; + +&uart8 { + status = "disabled"; +}; + +&uart9 { + status = "okay"; +}; + +&spi1 { + status = "disabled"; +}; + +&spi2 { + status = "disabled"; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&can2 { + status = "okay"; +}; + +&can3 { + status = "okay"; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi_pihat.dts b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_pihat.dts new file mode 100644 index 000000000..7a84b8341 --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_pihat.dts @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#include "ls2k300_vanguard_pi_common.dtsi" + +/ { + onewire { + compatible = "w1-gpio"; + gpios = <&gpio 68 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart2 { + status = "ok"; +}; + +&uart5 { + status = "disabled"; +}; + +&uart6 { + status = "disabled"; +}; + +&uart7 { + status = "disabled"; +}; + +&uart8 { + status = "disabled"; +}; + +&spi1 { + status = "disabled"; +}; + +&spi2 { + status = "ok"; + pinctrl-0 = <&spi2_cs_as_gpio>; + pinctrl-names = "default"; + num-chipselects = <2>; + cs-gpios = <&gpio 67 0>, + <&gpio 85 0>; + + mcp3004@0 { + compatible = "mcp3004"; + reg = <0>; + spi-max-frequency = <1500000>; + }; + + spidev1@1 { + compatible = "rohm,dh2228fv"; + reg = <1>; + spi-max-frequency = <25000000>; + }; +}; + +&can0 { + status = "disabled"; +}; + +&can1 { + status = "disabled"; +}; + +&can2 { + status = "disabled"; +}; + +&can3 { + status = "disabled"; +}; \ No newline at end of file diff --git a/arch/loongarch/boot/dts/ls2k300_vanguard_pi_v10.dts b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_v10.dts new file mode 100644 index 000000000..8825be4ac --- /dev/null +++ b/arch/loongarch/boot/dts/ls2k300_vanguard_pi_v10.dts @@ -0,0 +1,290 @@ +#include +#include +#include +#include + +#include "loongson2k300.dtsi" +#include "loongson_panel_param.dtsi" + +/ { + model = "LS2K300-PAI"; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "earlycon"; + }; + + /* not need memory node, we get memory info from boot params. */ + //memory { + // name = "memory"; + // device_type = "memory"; + // reg = <0 0x00200000 0 0x0ee00000 + // 0 0x90000000 0 0x10000000>; + //}; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0 0x02000000>; + linux,cma-default; + }; + }; + + leds { + compatible = "gpio-leds"; + + led1 { + label = "led1"; + gpios = <&gpio 54 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + }; + + panel: dvo-connector@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "panel-lvds", "auo,b101ean01"; + //backlight = <&lcd_backlight>; + + /* lcd power on control gpio */ + // enable-gpios = <&pioA 3 GPIO_ACTIVE_HIGH>; + // power-supply = <&vdd_lcd>; +#if 0 // 4.3' LCD + width-mm = <223>; + height-mm = <125>; + label = "ATK-MD0430R-800x480"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <27000000>; + hactive = <800>; + vactive = <480>; + hsync-len = <40>; + hfront-porch = <48>; + hback-porch = <40>; + vfront-porch = <13>; + vback-porch = <32>; + vsync-len = <1>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#else // 7' LCD + width-mm = <180>; + height-mm = <110>; + label = "ATK-MD0700R-1024x600"; + data-mapping = "jeida-24"; + + panel-timing { + clock-frequency = <51200000>; + hactive = <1024>; + vactive = <600>; + hsync-len = <20>; + hfront-porch = <160>; + hback-porch = <140>; + vfront-porch = <12>; + vback-porch = <20>; + vsync-len = <3>; + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; +#endif + port@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + dvo_connector_in: endpoint@0 { + reg = <0>; + remote-endpoint = <&dc_out_rgb0>; + }; + }; + }; + + vccio_codec: 3_3V{ + compatible = "regulator-fixed"; + regulator-name = "VCC_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + humidity_sensor { + compatible = "dht11-sensor"; + SHT11-gpios = <&gpio 61 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + // 虽然这个时钟在从模式下没用,但 es8388 驱动必须指定这个时钟 + es8388_clk: es8388-clock { + compatible = "fixed-clock"; + clock-frequency = <11289600>; + #clock-cells = <1>; + }; + + onewire { + compatible = "w1-gpio"; + gpios = <&gpio 68 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + spi-flash@0 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <25000000>; + reg = <0>; + }; +}; + +&gmac0 { + status = "okay"; + phy-handle = <&yt8511h0>; + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + yt8511h0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + motorcomm,led0_cfg = <0x40>; //千兆常亮 + motorcomm,led1_cfg = <0x30>; //百兆 十兆常亮 + motorcomm,led2_cfg = <0x2600>; //有数据时闪烁 + }; + }; +}; + +&dc { + status = "ok"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dc_out_rgb0: endpoint { + remote-endpoint = <&dvo_connector_in>; + }; + }; + }; +}; + +&i2c0 { + status = "ok"; + + codec: es8388@11{ + compatible = "everest,es8388"; + reg = <0x11>; + AVDD-supply = <&vccio_codec>; + DVDD-supply = <&vccio_codec>; + HPVDD-supply = <&vccio_codec>; + PVDD-supply = <&vccio_codec>; + clocks = <&es8388_clk 0>; + }; + + touchpad: gt9147@14 { + compatible = "goodix,gt9147"; + reg = <0x14>; + // 2K300 GPIO 支持中断控制 + // 暂时PCB布局有问题,所以随便写个引脚 + // GPIO 中断号为 vhwirq-base + pinnum (181 = 128 + 53) + interrupt-parent = <&gpio>; + interrupts = <181 IRQ_TYPE_EDGE_FALLING>; + irq-gpios = <&gpio 53 GPIO_ACTIVE_LOW>; + }; +}; + +&i2c1 { + status = "ok"; +}; + +// &i2c2 { +// status = "okay"; +// }; + +&uart2 { + status = "ok"; +}; + +// &can0 { +// status = "okay"; +// }; + +// &can1 { +// status = "okay"; +// }; + +// &can2 { +// status = "okay"; +// }; + +// &can3 { +// status = "okay"; +// }; + +// &spi1 { +// status = "okay"; + +// spidev0@0 { +// compatible = "rohm,dh2228fv"; +// reg = <0>; +// spi-max-frequency = <50000000>; +// }; +// }; + +&spi2 { + status = "ok"; + + mcp3004@0 { + compatible = "mcp3004"; + reg = <0>; + spi-max-frequency = <1500000>; + }; +}; + +&adc { + status = "okay"; +}; + +&rtc0 { + status = "disable"; +}; + +&sdio1 { + pinctrl-0 = <&sdio1_pins>; + pinctrl-names = "default"; + cap-sd-highspeed; + cap-mmc-highspeed; + bus-width = <4>; + cd-gpios = <&gpio 82 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&i2s { + status = "okay"; +}; + +&audio { + status = "okay"; +}; + +&sound { + status = "okay"; +}; \ No newline at end of file diff --git a/arch/loongarch/configs/loongson_2k300_defconfig b/arch/loongarch/configs/loongson_2k300_defconfig new file mode 100644 index 000000000..0b717bccf --- /dev/null +++ b/arch/loongarch/configs/loongson_2k300_defconfig @@ -0,0 +1,830 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_PREEMPT_RT=y +CONFIG_SCHED_CORE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_RDMA=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_CGROUP_MISC=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y +CONFIG_LOONGSON_2K300=y +CONFIG_HZ_1000=y +CONFIG_CMDLINE="fbcon=logo-pos:center fbcon=logo-count:1" +CONFIG_CMDLINE_EXTEND=y +CONFIG_BUILTIN_DTB=y +CONFIG_SMP=y +CONFIG_HIBERNATION=y +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_PROCESSOR is not set +# CONFIG_ACPI_TABLE_UPGRADE is not set +# CONFIG_RANDOMIZE_KSTACK_OFFSET is not set +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_WBT=y +CONFIG_BLK_CGROUP_IOLATENCY=y +CONFIG_BLK_CGROUP_IOCOST=y +CONFIG_BLK_CGROUP_IOPRIO=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_BSD_DISKLABEL=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_CMDLINE_PARTITION=y +CONFIG_ZSWAP=y +CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y +CONFIG_ZBUD=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_PAGE_REPORTING=y +CONFIG_KSM=y +CONFIG_CMA=y +CONFIG_CMA_SYSFS=y +CONFIG_CMA_AREAS=20 +CONFIG_USERFAULTFD=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_TLS=m +CONFIG_TLS_DEVICE=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_XDP_SOCKETS=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_ESPINTCP=y +CONFIG_INET_IPCOMP=m +CONFIG_INET_UDP_DIAG=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BBR=m +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_ESPINTCP=y +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_MROUTE=y +CONFIG_MPTCP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NFT_CT=m +CONFIG_NFT_CONNLIMIT=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_TUNNEL=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_FIB_INET=m +CONFIG_NFT_SOCKET=m +CONFIG_NFT_OSF=m +CONFIG_NFT_TPROXY=m +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_NFCT=y +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=y +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SECURITY=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_SRH=m +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NF_CONNTRACK_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_IP_SCTP=m +CONFIG_RDS=y +CONFIG_L2TP=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_LLC2=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_BPF=m +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_BPF_STREAM_PARSER=y +CONFIG_CAN=y +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y +CONFIG_BT_HCIBTUSB_MTK=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_INTEL=y +CONFIG_BT_HCIUART_AG6XX=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_ATH3K=m +CONFIG_BT_VIRTIO=m +CONFIG_CFG80211=m +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_RFKILL=m +CONFIG_RFKILL_INPUT=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCIEASPM is not set +# CONFIG_VGA_ARB is not set +# CONFIG_HOTPLUG_PCI is not set +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCI_SW_SWITCHTEC=m +CONFIG_UEVENT_HELPER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_COMPRESS=y +CONFIG_FW_LOADER_COMPRESS_ZSTD=y +CONFIG_EFI_ZBOOT=y +CONFIG_EFI_BOOTLOADER_CONTROL=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_EFI_TEST=m +CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y +# CONFIG_EFI_DISABLE_RUNTIME is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_BLOCK2MTD=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=32768 +CONFIG_VIRTIO_BLK=y +CONFIG_EEPROM_AT24=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_ISCSI_ATTRS=m +CONFIG_SCSI_SAS_LIBSAS=y +CONFIG_SCSI_SAS_ATA=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_MOBILE_LPM_POLICY=0 +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_DWC=y +CONFIG_PATA_ATIIXP=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_DUMMY=y +CONFIG_WIREGUARD=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +CONFIG_VXLAN=y +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_VIRTIO_NET=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_AGERE is not set +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +CONFIG_BNX2=y +# CONFIG_NET_VENDOR_CAVIUM is not set +CONFIG_CHELSIO_T1=m +CONFIG_CHELSIO_T1_1G=y +CONFIG_CHELSIO_T3=m +CONFIG_CHELSIO_T4=m +# CONFIG_NET_VENDOR_CISCO is not set +# CONFIG_NET_VENDOR_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_E1000=y +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_META is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RDC is not set +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_R8169=y +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +CONFIG_NGBE=y +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_MOTORCOMM_PHY=y +CONFIG_CAN_LSCANFD=y +CONFIG_CAN_LSCANFD_PLATFORM=y +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_ATH9K=m +CONFIG_ATH9K_HTC=m +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_MT7601U=m +CONFIG_RT2X00=m +CONFIG_RT2800USB=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723AE=m +CONFIG_RTL8723BE=m +CONFIG_RTL8188EE=m +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m +CONFIG_RTL8192CU=m +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8XXXU=m +CONFIG_RTW88=m +CONFIG_RTW88_8822BE=m +CONFIG_RTW88_8822CE=m +CONFIG_RTW88_8723DE=m +CONFIG_RTW88_8821CE=m +CONFIG_RTW89=m +CONFIG_RTW89_8852AE=m +CONFIG_RTW89_8852CE=m +CONFIG_ZD1211RW=m +CONFIG_USB4_NET=m +CONFIG_INPUT_SPARSEKMAP=y +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_XTKBD=m +# CONFIG_MOUSE_PS2 is not set +CONFIG_MOUSE_SERIAL=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GOODIX=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m +CONFIG_SERIO_SERPORT=m +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_RAW=m +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=16 +CONFIG_SERIAL_8250_RUNTIME_UARTS=16 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_IPMI_HANDLER=m +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_PIIX4=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_LSFS=y +CONFIG_SPI=y +CONFIG_SPI_LOONGSON_PCI=m +CONFIG_SPI_LOONGSON_PLATFORM=m +CONFIG_SPI_LS=y +CONFIG_SPI_LS_IO=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_LOONGSON_IRQCHIP=y +CONFIG_GPIO_LOONGSON_64BIT=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_W83795=m +CONFIG_SENSORS_W83627HF=m +CONFIG_THERMAL=y +CONFIG_LOONGSON2_THERMAL=y +CONFIG_RC_CORE=m +CONFIG_LIRC=y +CONFIG_RC_DECODERS=y +CONFIG_IR_IMON_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_MEDIA_SUPPORT=y +# CONFIG_DVB_CORE is not set +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_VIDEO_IR_I2C is not set +# CONFIG_MEDIA_TUNER_E4000 is not set +# CONFIG_MEDIA_TUNER_FC0011 is not set +# CONFIG_MEDIA_TUNER_FC0012 is not set +# CONFIG_MEDIA_TUNER_FC0013 is not set +# CONFIG_MEDIA_TUNER_FC2580 is not set +# CONFIG_MEDIA_TUNER_IT913X is not set +# CONFIG_MEDIA_TUNER_M88RS6000T is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MSI001 is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MXL301RF is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set +# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_R820T is not set +# CONFIG_MEDIA_TUNER_SI2157 is not set +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18250 is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_TUA9001 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +CONFIG_DRM=y +CONFIG_DRM_PANEL_LVDS=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SIMPLE_BRIDGE=y +CONFIG_DRM_LOONGSON=y +CONFIG_DRM_SIMPLEDRM=y +CONFIG_FB=y +CONFIG_FB_EFI=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_BT87X=m +CONFIG_SND_BT87X_OVERCLOCK=y +CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_SIGMATEL=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_SOC_LOONGSON=y +CONFIG_SND_LS=y +CONFIG_SND_LS_PCM=y +CONFIG_SND_LS_PCM_GENERIC_DMA=y +CONFIG_SND_SOC_ES8328_I2C=y +CONFIG_SND_VIRTIO=m +CONFIG_HIDRAW=y +CONFIG_UHID=m +CONFIG_HID_A4TECH=m +CONFIG_HID_CHERRY=m +CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_DJ=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_SUNPLUS=m +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_OTG=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_UAS=y +CONFIG_USB_DWC2=y +CONFIG_USB_DWC2_HOST=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_GADGET=y +CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_TCPCI=m +CONFIG_TYPEC_UCSI=m +CONFIG_UCSI_ACPI=m +CONFIG_MMC=y +CONFIG_MMC_LS2K=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_LOONGSON=y +CONFIG_DMADEVICES=y +CONFIG_LOONGSON2_APB_DMA=y +CONFIG_LSIA_DMA=y +CONFIG_UDMABUF=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=y +CONFIG_UIO_DMEM_GENIRQ=y +CONFIG_UIO_PCI_GENERIC=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +# CONFIG_LOONGARCH_PLATFORM_DEVICES is not set +CONFIG_COMMON_CLK_LOONGSON2=y +CONFIG_LOONGSON2_GUTS=y +CONFIG_LOONGSON2_PM=y +CONFIG_IIO=y +CONFIG_LS_2K300_ADC=y +CONFIG_PWM=y +CONFIG_PWM_LOONGSON=y +CONFIG_GENERIC_PHY=y +CONFIG_RAS=y +CONFIG_USB4=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_XFS_FS=y +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_XFS_RT=y +CONFIG_XFS_ONLINE_SCRUB=y +CONFIG_XFS_ONLINE_REPAIR=y +CONFIG_XFS_DEBUG=y +CONFIG_XFS_DEBUG_EXPENSIVE=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_OVERLAY_FS_INDEX=y +CONFIG_OVERLAY_FS_XINO_AUTO=y +CONFIG_OVERLAY_FS_METACOPY=y +CONFIG_FSCACHE=y +CONFIG_CACHEFILES=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=936 +CONFIG_FAT_DEFAULT_IOCHARSET="gb2312" +CONFIG_EXFAT_FS=y +CONFIG_NTFS3_FS=y +CONFIG_NTFS3_64BIT_CLUSTER=y +CONFIG_NTFS3_LZX_XPRESS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set +CONFIG_NFSD=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CEPH_FS_SECURITY_LABEL=y +CONFIG_CIFS=m +# CONFIG_CIFS_DEBUG is not set +CONFIG_9P_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_KEY_DH_OPERATIONS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_YAMA=y +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_CRYPTO_USER=m +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_CRYPTD=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_CHACHA20POLY1305=m +CONFIG_CRYPTO_ESSIV=m +CONFIG_CRYPTO_BLAKE2B=y +CONFIG_CRYPTO_VMAC=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_XXHASH=y +CONFIG_CRYPTO_CRC32=m +CONFIG_CRYPTO_842=m +CONFIG_CRYPTO_LZ4=m +CONFIG_CRYPTO_LZ4HC=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_DEV_VIRTIO=m +CONFIG_XZ_DEC_MICROLZMA=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=0 +CONFIG_IRQ_POLL=y +CONFIG_PRINTK_TIME=y +CONFIG_STRIP_ASM_SYMS=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +# CONFIG_FTRACE is not set +CONFIG_UNWINDER_ORC=y diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index cbd3c09a9..987884e70 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -49,6 +49,12 @@ #include #include +#include + +#ifndef CONFIG_BUILTIN_DTB_NAME +#define CONFIG_BUILTIN_DTB_NAME "" +#endif + #define SMBIOS_BIOSSIZE_OFFSET 0x09 #define SMBIOS_BIOSEXTERN_OFFSET 0x13 #define SMBIOS_FREQLOW_OFFSET 0x16 @@ -73,6 +79,8 @@ static const char dmi_empty_string[] = " "; * These are initialized so they are in the .data section */ char init_command_line[COMMAND_LINE_SIZE] __initdata; +char board_name_desc[] = "board_name="; // 这是 command line 里面的内容 一定要有= +char bp_start_desc[] = "bp_start="; // 这是 command line 里面的内容 一定要有= static int num_standard_resources; static struct resource *standard_resources; @@ -81,6 +89,17 @@ static struct resource code_resource = { .name = "Kernel code", }; static struct resource data_resource = { .name = "Kernel data", }; static struct resource bss_resource = { .name = "Kernel bss", }; +////////////////////////////////////// +/////for bootloader mem map parse///// +////////////////////////////////////// +static unsigned long long bp_start; +struct loongsonlist_mem_map *loongson_mem_map; +static u8 __init ext_listhdr_checksum(u8 *buffer, u32 length); +static int __init parse_mem(struct _extention_list_hdr *head); +static void __init memmap_bootlaoder_parse(void); +static int __init parse_extlist(struct boot_params *bp); +static int __init bp_start_match(void); + const char *get_system_type(void) { return "generic-loongson-machine"; @@ -273,6 +292,106 @@ static void __init arch_reserve_crashkernel(void) reserve_crashkernel_generic(cmdline, crash_size, crash_base, low_size, high); } +#ifdef CONFIG_DTB_MATCH_BY_BOARD_NAME +static void* __init get_fdt_by_board_name(void) +{ + void *fdt = NULL; + char* board_name; + char temp[128]; // 不想申请空间 应该不会有这么长的名字吧 + board_name = strstr(boot_command_line, board_name_desc); + if (board_name) { + int i; + memset(temp, 0, 128); + board_name += strlen(board_name_desc); // 跳过 = 和前面的字段 + for (i = 0; i < 127; ++i) { + if (board_name[i] == 0 || board_name[i] == ' ') + break; + temp[i] = board_name[i]; + } + board_name = temp; + } + if (board_name) { +#ifdef CONFIG_LOONGSON_2K500 + int i; + if (!strncmp(board_name, "LS2K500-HL-MB", 13)) + fdt = &__dtb_ls2k500_hl_mb_begin; + else if (!strncmp(board_name, "LS2K500-MINI-DP", 15)) + fdt = &__dtb_ls2k500_mini_dp_begin; + else if (!strncmp(board_name, "LS2K500-DAYU400-MB", 17)) + fdt = &__dtb_ls2k500_dayu400_mb_begin; + else if (!strncmp(board_name, "LS2K500-MODI-HCT", 16)) + fdt = &__dtb_ls2k500_modi_hct_begin; + else if (!strncmp(board_name, "LS2K500-ZHENGTAI-PCS1800-V10", 28)) + fdt = &__dtb_ls2k500_zhengtai_pcs1800_v10_begin; + else if (!strncmp(board_name, "LS2K500-JIAOQIAN-V10", 20)) + fdt = &__dtb_ls2k500_jiaoqian_v10_begin; + else if (!strncmp(board_name, "LS2K500-ZJJZ", 12)) + fdt = &__dtb_ls2k500_zjjz_begin; + else + fdt = &__dtb_ls2k500_mini_dp_begin; + + // for (i = 0; i < NR_CPUS; ++i) + // if (check_cpu_full_name_invaild(i)) + // __cpu_full_name[i] = cpu_ls2k500_name; +#elif defined(CONFIG_LOONGSON_2K1000) + if (!strncmp(board_name, "LS2K1000-JL-MB", 14)) { + if (!strncmp(board_name, "LS2K1000-JL-MB-MU", 17)) + fdt = &__dtb_ls2k1000_jl_mb_mu_begin; + else if (!strncmp(board_name, "LS2K1000-JL-MB-NODVO", 20)) + fdt = &__dtb_ls2k1000_jl_mb_nodvo_begin; + else + fdt = &__dtb_ls2k1000_jl_mb_begin; + } else if (!strncmp(board_name, "LS2K1000-DP", 11)) { + #ifdef CONFIG_SND_LS1X_SOC_I2S + fdt = &__dtb_ls2k1000_dp_i2s_begin; + #else + if (!strncmp(board_name, "LS2K1000-DP-TEST", 16)) { + fdt = &__dtb_ls2k1000_dp_test_begin; + } else if (!strncmp(board_name, "LS2K1000-DP-FACTORY", 19)){ + fdt = &__dtb_ls2k1000_dp_factory_begin; + } else { + fdt = &__dtb_ls2k1000_dp_begin; + } + #endif + } else if (!strncmp(board_name, "HAC_MB_REVC", 11)) { + fdt = &__dtb_ksec_hac_mb_begin; + } else if (!strncmp(board_name, "GBKPDM0-V10", 11)) { + fdt = &__dtb_gbkpdm0_v10_begin; + } else if (!strncmp(board_name, "GBKPDM0-LITONG", 14)) { + fdt = &__dtb_gbkpdm0_litong_begin; + } else if (!strncmp(board_name, "LS2K1000-ChuangLong-MB", 22)) { + fdt = &__dtb_ls2k1000_cl_mb_begin; + } else if (!strncmp(board_name, "C4G-MB001", 9)) { + fdt = &__dtb_ls2k1000_c4g_mb001_begin; + } else if (!strncmp(board_name, "ZNWLIDZD-LITONG", 15)) { + fdt = &__dtb_ls2k1000_znwlidzd_litong_begin; + } else { + fdt = &__dtb_ls2k1000_dp_begin; + } +#elif defined(CONFIG_LOONGSON_2P500) + if (!strncmp(board_name, "LS2P500-EVB", 11)) + fdt = &__dtb_ls2p500_evb_begin; + else if (!strncmp(board_name, "LS2P500-RSJ", 11)) + fdt = &__dtb_ls2p500_rsj_mb_v10_begin; + else if (!strncmp(board_name, "LS2P500-GBKPJM0", 15)) + fdt = &__dtb_ls2p500_gbkpjm0_v10_begin; + else + fdt = &__dtb_ls2p500_evb_begin; +#elif defined(CONFIG_LOONGSON_2K300) + if (!strncmp(board_name, "LS2K300-MINI-DP", 15)) + fdt = &__dtb_ls2k300_mini_dp_begin; + else if (!strncmp(board_name, "LS2K300-PAI", 11)) + fdt = &__dtb_ls2k300_vanguard_pi_begin; + else if (!strncmp(board_name, "LS2K300-TEWEI", 13)) + fdt = &__dtb_ls2k300_tewei_begin; + else + fdt = &__dtb_ls2k300_mini_dp_begin; +#endif + } + return fdt; +} +#endif + static void __init fdt_setup(void) { #ifdef CONFIG_OF_EARLY_FLATTREE @@ -282,11 +401,19 @@ static void __init fdt_setup(void) if (acpi_os_get_root_pointer()) return; + fdt_pointer = NULL; + +#ifdef CONFIG_DTB_MATCH_BY_BOARD_NAME + fdt_pointer = efi_fdt_pointer(); /* Fallback to firmware dtb */ + if (!fdt_pointer) + fdt_pointer = get_fdt_by_board_name(); +#else /* Prefer to use built-in dtb, checking its legality first. */ - if (IS_ENABLED(CONFIG_BUILTIN_DTB) && !fdt_check_header(__dtb_start)) + if (!fdt_check_header(__dtb_start) && strcmp(CONFIG_BUILTIN_DTB_NAME, "")) fdt_pointer = __dtb_start; else fdt_pointer = efi_fdt_pointer(); /* Fallback to firmware dtb */ +#endif if (!fdt_pointer || fdt_check_header(fdt_pointer)) return; @@ -586,6 +713,31 @@ static void __init prefill_possible_map(void) } #endif +#ifdef CONFIG_BLK_DEV_INITRD +static int __init rd_start_early(char *p) +{ + phys_initrd_start = __pa(memparse(p, &p)); + return 0; +} +early_param("rd_start", rd_start_early); + +static int __init rd_size_early(char *p) +{ + phys_initrd_size = memparse(p, &p); + + return 0; +} +early_param("rd_size", rd_size_early); +#endif + +static int __init bp_start_early(char *p) +{ + bp_start = __pa(memparse(p, &p)); // 这里拿到的是物理地址 + bp_start = TO_CACHE(bp_start); // 再转换成当前可以访问的地址 + return 0; +} +early_param("bp_start", bp_start_early); + void __init setup_arch(char **cmdline_p) { cpu_probe(); @@ -593,6 +745,9 @@ void __init setup_arch(char **cmdline_p) init_environ(); efi_init(); + if (!bp_start_match() && bp_start) + parse_extlist((struct boot_params*)bp_start); + fdt_setup(); memblock_init(); pagetable_init(); @@ -617,3 +772,125 @@ void __init setup_arch(char **cmdline_p) kasan_init(); #endif } + +////////////////////////////////////// +/////for bootloader mem map parse///// +////////////////////////////////////// +static __init u8 ext_listhdr_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) { + sum = (u8)(sum + *(buffer++)); + } + + return (sum); +} + +static __init int parse_mem(struct _extention_list_hdr *head) +{ + struct loongsonlist_mem_map_legacy *ptr; + static struct loongsonlist_mem_map mem_map; + int i; + + loongson_mem_map = (struct loongsonlist_mem_map *)head; + + if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) { + printk("mem checksum error\n"); + return -EPERM; + } + + ptr = (struct loongsonlist_mem_map_legacy *)head; + + pr_info("convert legacy mem map to new mem map.\n"); + memcpy(&mem_map, ptr, sizeof(mem_map.header)); + mem_map.map_count = ptr->map_count; + for (i = 0; i < ptr->map_count; i++) { + mem_map.map[i].mem_type = ptr->map[i].mem_type; + mem_map.map[i].mem_start = ptr->map[i].mem_start; + mem_map.map[i].mem_size = ptr->map[i].mem_size; + pr_info("bootloader memmap block %d type : %d start : %.llx size : %.llx\n", + i, mem_map.map[i].mem_type, mem_map.map[i].mem_start, mem_map.map[i].mem_size); + } + loongson_mem_map = &mem_map; + return 0; +} + +static __init void memmap_bootlaoder_parse(void) +{ + int i; + u32 mem_type; + u64 mem_start, mem_end, mem_size; + /* Parse memory information */ + for (i = 0; i < loongson_mem_map->map_count; i++) { + mem_type = loongson_mem_map->map[i].mem_type; + mem_start = loongson_mem_map->map[i].mem_start; + mem_size = loongson_mem_map->map[i].mem_size; + mem_end = mem_start + mem_size; + + switch (mem_type) { + case ADDRESS_TYPE_SYSRAM: + memblock_add(mem_start, mem_size); + if (max_low_pfn < (mem_end >> PAGE_SHIFT)) + max_low_pfn = mem_end >> PAGE_SHIFT; + break; + } + } +} + +static __init int parse_extlist(struct boot_params *bp) +{ + unsigned long next_offset; + struct _extention_list_hdr *fhead; + + // 原本有几种转换版本,但是这边就这一个 + fhead = (struct _extention_list_hdr *)bp->extlist_offset; + + if (fhead == NULL) { + printk("the ext struct is empty!\n"); + return -1; + } + + do { + if (memcmp(&(fhead->signature), LOONGSON_MEM_SIGNATURE, 3) == 0) { + if (parse_mem(fhead) != 0) { + printk("parse mem failed\n"); + return -EPERM; + } + memmap_bootlaoder_parse(); + } + + fhead = (struct _extention_list_hdr *)fhead->next_offset; + next_offset = (unsigned long)fhead; + + } while (next_offset); + + return 0; +} + +static int __init bp_start_match(void) +{ + char* bp_start_value; + char temp[128]; // 不想申请空间 应该不会有这么长的值吧 + + bp_start = 0; + bp_start_value = strstr(boot_command_line, bp_start_desc); + if (bp_start_value) { + int i; + memset(temp, 0, 128); + bp_start_value += strlen(bp_start_desc); // 跳过 = 和前面的字段 + for (i = 0; i < 127; ++i) { + if (bp_start_value[i] == 0 || bp_start_value[i] == ' ') + break; + temp[i] = bp_start_value[i]; + } + bp_start_value = temp; + } else + return 1; + return bp_start_early(bp_start_value); +} + +////////////////////////////////////// +/////for bootloader mem map parse///// +////////////////////////////////////// diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index e2d3bfeb6..9c13315f1 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -210,6 +212,27 @@ int __init constant_clocksource_init(void) void __init time_init(void) { +#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK_LOONGSON2X) + struct clk* cpu_clk; + struct clk* ref_clk; + of_clk_init(NULL); + + if (!cpu_clock_freq) { + cpu_clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(cpu_clk)) + pr_info("cpu clk cant get from clock driver"); + else + cpu_clock_freq = clk_get_rate(cpu_clk); + } + if (!const_clock_freq) { + ref_clk = clk_get(NULL, "ref_clk"); + if (IS_ERR(ref_clk)) + pr_info("reference clock cant get from clock driver"); + else + const_clock_freq = clk_get_rate(ref_clk); + } +#endif + if (!cpu_has_cpucfg) const_clock_freq = cpu_clock_freq; else diff --git a/buildenv.sh b/buildenv.sh new file mode 100755 index 000000000..e12d1d584 --- /dev/null +++ b/buildenv.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e + +PS3="Please enter your choice:" + +conf="" + +function get_conf() +{ + plat=loongson${1} + opts=(`cd arch/loongarch/configs && ls ${plat}*`) + select opt in "${opts[@]}" + do + conf=$opt + break + done + + echo + echo "Your select is:${conf}" + echo +} + +function usage() +{ + echo "Usage:" + echo "./buildenv.sh [options]" + echo "chip: <2k300/2k500/2k1000/2p500/all>" + echo "options: it's same as u-boot make defconfig" +} + +function main() +{ + if [ $# -ge 1 ]; then + param=${1} + if [ ${param} = "all" ]; then + get_conf + else + get_conf "_"$param + fi + + shift + make $conf $@ + else + usage + fi +} + +main $@ + diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 299bc678e..77c181975 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -483,6 +483,7 @@ source "drivers/clk/imgtec/Kconfig" source "drivers/clk/imx/Kconfig" source "drivers/clk/ingenic/Kconfig" source "drivers/clk/keystone/Kconfig" +source "drivers/clk/loongson/Kconfig" source "drivers/clk/mediatek/Kconfig" source "drivers/clk/meson/Kconfig" source "drivers/clk/mstar/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fb8878a5d..f353b39c2 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -108,6 +108,7 @@ obj-y += imx/ obj-y += ingenic/ obj-$(CONFIG_ARCH_K3) += keystone/ obj-$(CONFIG_ARCH_KEYSTONE) += keystone/ +obj-$(CONFIG_LOONGARCH) += loongson/ obj-y += mediatek/ obj-$(CONFIG_ARCH_MESON) += meson/ obj-y += microchip/ diff --git a/drivers/clk/loongson/Kconfig b/drivers/clk/loongson/Kconfig new file mode 100644 index 000000000..9727c9fd0 --- /dev/null +++ b/drivers/clk/loongson/Kconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config COMMON_CLK_LOONGSON2X + bool "Clock drivers for Loongson2 SOCs" + depends on LOONGSON_2K1000 || LOONGSON_2K500 || LOONGSON_2K300 + default LOONGSON_2K1000 || LOONGSON_2K500 || LOONGSON_2K300 + help + Supports clock drivers for Loongson2 SOCs. + +choice + bool "System clock mode Selection" + depends on COMMON_CLK_LOONGSON2X + default LOONGSON2_SYSCLK_SOFT + + config LOONGSON2_SYSCLK_HW_LOWFREQ + bool "Hardware low frequency mode" + help + System clock select Hardware low frequency mode. + + config LOONGSON2_SYSCLK_HW_HIGFREQ + bool "Hardware hige frequency mode" + help + System clock select Hardware hige frequency mode. + + config LOONGSON2_SYSCLK_SOFT + bool "Soft mode" + help + System clock select Soft mode. + + config LOONGSON2_SYSCLK_HW_BYPASS + bool "Hardware bypass mode" + help + System clock select Hardware bypass mode. + +endchoice diff --git a/drivers/clk/loongson/Makefile b/drivers/clk/loongson/Makefile new file mode 100644 index 000000000..6c9526d33 --- /dev/null +++ b/drivers/clk/loongson/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += clk.o +obj-$(CONFIG_LOONGSON1_LS1B) += clk-loongson1b.o +obj-$(CONFIG_LOONGSON1_LS1C) += clk-loongson1c.o +obj-$(CONFIG_LOONGSON_2K1000) += clk-loongson2k1000.o +obj-$(CONFIG_LOONGSON_2K500) += clk-loongson2k500.o +obj-$(CONFIG_LOONGSON_2K300) += clk-loongson2k300.o diff --git a/drivers/clk/loongson/clk-loongson1b.c b/drivers/clk/loongson/clk-loongson1b.c new file mode 100644 index 000000000..13a2ca23a --- /dev/null +++ b/drivers/clk/loongson/clk-loongson1b.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2012-2016 Zhang, Keguang + */ + +#include +#include +#include +#include + +#include +#include "clk.h" + +#define OSC (33 * 1000000) +#define DIV_APB 2 + +static DEFINE_SPINLOCK(_lock); + +static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 pll, rate; + + pll = __raw_readl(LS1X_CLK_PLL_FREQ); + rate = 12 + (pll & GENMASK(5, 0)); + rate *= OSC; + rate >>= 1; + + return rate; +} + +static const struct clk_ops ls1x_pll_clk_ops = { + .recalc_rate = ls1x_pll_recalc_rate, +}; + +static const char *const cpu_parents[] = { "cpu_clk_div", "osc_clk", }; +static const char *const ahb_parents[] = { "ahb_clk_div", "osc_clk", }; +static const char *const dc_parents[] = { "dc_clk_div", "osc_clk", }; + +void __init ls1x_clk_init(void) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC); + clk_hw_register_clkdev(hw, "osc_clk", NULL); + + /* clock derived from 33 MHz OSC clk */ + hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk", + &ls1x_pll_clk_ops, 0); + clk_hw_register_clkdev(hw, "pll_clk", NULL); + + /* clock derived from PLL clk */ + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ CPU CLK + * \___ PLL ___ CPU DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk", + CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV, + DIV_CPU_SHIFT, DIV_CPU_WIDTH, + CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ROUND_CLOSEST, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "cpu_clk", cpu_parents, + ARRAY_SIZE(cpu_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk", NULL); + + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ DC CLK + * \___ PLL ___ DC DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, + DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_hw_register_clkdev(hw, "dc_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "dc_clk", dc_parents, + ARRAY_SIZE(dc_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "dc_clk", NULL); + + /* _____ + * _______________________| | + * OSC ___/ | MUX |___ DDR CLK + * \___ PLL ___ DDR DIV ___| | + * |_____| + */ + hw = clk_hw_register_divider(NULL, "ahb_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT, + DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, + &_lock); + clk_hw_register_clkdev(hw, "ahb_clk_div", NULL); + hw = clk_hw_register_mux(NULL, "ahb_clk", ahb_parents, + ARRAY_SIZE(ahb_parents), + CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV, + BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock); + clk_hw_register_clkdev(hw, "ahb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-dma", NULL); + clk_hw_register_clkdev(hw, "stmmaceth", NULL); + + /* clock derived from AHB clk */ + /* APB clk is always half of the AHB clk */ + hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, + DIV_APB); + clk_hw_register_clkdev(hw, "apb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-ac97", NULL); + clk_hw_register_clkdev(hw, "ls1x-i2c", NULL); + clk_hw_register_clkdev(hw, "ls1x-nand", NULL); + clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL); + clk_hw_register_clkdev(hw, "ls1x-spi", NULL); + clk_hw_register_clkdev(hw, "ls1x-wdt", NULL); + clk_hw_register_clkdev(hw, "serial8250", NULL); +} diff --git a/drivers/clk/loongson/clk-loongson1c.c b/drivers/clk/loongson/clk-loongson1c.c new file mode 100644 index 000000000..703f87622 --- /dev/null +++ b/drivers/clk/loongson/clk-loongson1c.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2016 Yang Ling + */ + +#include +#include +#include + +#include +#include "clk.h" + +#define OSC (24 * 1000000) +#define DIV_APB 1 + +static DEFINE_SPINLOCK(_lock); + +static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 pll, rate; + + pll = __raw_readl(LS1X_CLK_PLL_FREQ); + rate = ((pll >> 8) & 0xff) + ((pll >> 16) & 0xff); + rate *= OSC; + rate >>= 2; + + return rate; +} + +static const struct clk_ops ls1x_pll_clk_ops = { + .recalc_rate = ls1x_pll_recalc_rate, +}; + +static const struct clk_div_table ahb_div_table[] = { + [0] = { .val = 0, .div = 2 }, + [1] = { .val = 1, .div = 4 }, + [2] = { .val = 2, .div = 3 }, + [3] = { .val = 3, .div = 3 }, +}; + +void __init ls1x_clk_init(void) +{ + struct clk_hw *hw; + + hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC); + clk_hw_register_clkdev(hw, "osc_clk", NULL); + + /* clock derived from 24 MHz OSC clk */ + hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk", + &ls1x_pll_clk_ops, 0); + clk_hw_register_clkdev(hw, "pll_clk", NULL); + + hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk", + CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV, + DIV_CPU_SHIFT, DIV_CPU_WIDTH, + CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ROUND_CLOSEST, &_lock); + clk_hw_register_clkdev(hw, "cpu_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "cpu_clk", "cpu_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "cpu_clk", NULL); + + hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk", + 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, + DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_hw_register_clkdev(hw, "dc_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "dc_clk", "dc_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "dc_clk", NULL); + + hw = clk_hw_register_divider_table(NULL, "ahb_clk_div", "cpu_clk_div", + 0, LS1X_CLK_PLL_FREQ, DIV_DDR_SHIFT, + DIV_DDR_WIDTH, CLK_DIVIDER_ALLOW_ZERO, + ahb_div_table, &_lock); + clk_hw_register_clkdev(hw, "ahb_clk_div", NULL); + hw = clk_hw_register_fixed_factor(NULL, "ahb_clk", "ahb_clk_div", + 0, 1, 1); + clk_hw_register_clkdev(hw, "ahb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-dma", NULL); + clk_hw_register_clkdev(hw, "stmmaceth", NULL); + + /* clock derived from AHB clk */ + hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, + DIV_APB); + clk_hw_register_clkdev(hw, "apb_clk", NULL); + clk_hw_register_clkdev(hw, "ls1x-ac97", NULL); + clk_hw_register_clkdev(hw, "ls1x-i2c", NULL); + clk_hw_register_clkdev(hw, "ls1x-nand", NULL); + clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL); + clk_hw_register_clkdev(hw, "ls1x-spi", NULL); + clk_hw_register_clkdev(hw, "ls1x-wdt", NULL); + clk_hw_register_clkdev(hw, "serial8250", NULL); +} diff --git a/drivers/clk/loongson/clk-loongson2k1000.c b/drivers/clk/loongson/clk-loongson2k1000.c new file mode 100644 index 000000000..8b3262002 --- /dev/null +++ b/drivers/clk/loongson/clk-loongson2k1000.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2022 Michael + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define NODE_CLK_FREQ_DEF 900000000 + +static unsigned long refclk_rate; +static void __iomem *reg; + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT +struct pll_config { + unsigned int l1_div_ref; + unsigned int l1_loopc; + unsigned int l2_divout; +}; + +// 系统时钟需满足如下几个限制: +// 1.可配分频器的输出 refclk/L1_div_ref 在 20~40MHz 范围内 +// 2.PLL 倍频值 refclk/L1_div_ref*L1_loopc 需要在 1.2GHz~3.2GHz +static int ls2k_caculate_freq(unsigned long freq, struct pll_config *pll_cfg) +{ + unsigned long pll_in_clk; + unsigned long loopc_min, loopc_max, loopc; + unsigned long div, div_max; + int found = 0; + + if (!pll_cfg) + return -EINVAL; + + // Keep refclk/L1_div_ref in range 20MHz ~ 40Mhz + pll_cfg->l1_div_ref = 4; + pll_in_clk = refclk_rate / pll_cfg->l1_div_ref; // 25MHz + loopc_min = 1200000000 / pll_in_clk; + loopc_max = 3200000000 / pll_in_clk; + div_max = loopc_max; // pll_in_clk * loopc / div > pll_in_clk + + for (div = 1; div < div_max; div++) { + loopc = freq * div / pll_in_clk; + if (loopc >= loopc_min && loopc <= loopc_max) { + found = 1; + break; + } + } + + if (!found) { + pll_cfg->l1_div_ref = 0; + pll_cfg->l1_loopc = 0; + pll_cfg->l2_divout = 0; + return -EINVAL; + } + + pll_cfg->l1_loopc = loopc; + pll_cfg->l2_divout = div; + return 0; +} + +static void ls2k_config_pll(void __iomem *pll_base, struct pll_config *pll_cfg) +{ + int timeout = 200; + u64 val; + + /* set sel_pll_out0 0 */ + val = __raw_readq(pll_base); + val &= ~BIT(0); + __raw_writeq(val, pll_base); + + /* pll_pd 1 */ + val = __raw_readq(pll_base); + val |= BIT(19); + __raw_writeq(val, pll_base); + + /* set_pll_param 0 */ + val = __raw_readq(pll_base); + val &= ~BIT(2); + __raw_writeq(val, pll_base); + + /* set new div ref, loopc, div out */ + /* clear old value first*/ + val = BIT(7) | (BIT(10) | BIT(11)) | + ((u64)(pll_cfg->l1_loopc) << 32) | + (pll_cfg->l1_div_ref << 26); + __raw_writeq(val, pll_base); + __raw_writeq(pll_cfg->l2_divout, pll_base + 8); + + /* set_pll_param 1 */ + val = __raw_readq(pll_base); + val |= BIT(2); + __raw_writeq(val, pll_base); + + /* pll_pd 0 */ + val = __raw_readq(pll_base); + val &= ~BIT(19); + __raw_writeq(val, pll_base); + + /* wait pll lock */ + while(!(__raw_readq(pll_base) & BIT(16)) && (timeout-- > 0)) { + udelay(1); + } + + /* set sel_pll_out0 1 */ + val = __raw_readq(pll_base); + val |= BIT(0); + __raw_writeq(val, pll_base); +} + + +static unsigned long ls2x_node_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 ctrl; + unsigned int l1div_loopc, l1div_ref, l2div_out; + unsigned int mult, div; + unsigned long rate; + + /* node cpu clk */ + ctrl = __raw_readq(reg + PLL_SYS0_OFF); + l1div_loopc = (ctrl >> NODE_L1DIV_LOOPC_SHIFT) & NODE_L1DIV_LOOPC_MARK; + l1div_ref = (ctrl >> NODE_L1DIV_REF_SHIFT) & NODE_L1DIV_REF_MARK; + ctrl = __raw_readq(reg + PLL_SYS1_OFF); + l2div_out = (ctrl >> NODE_L2DIV_OUT_SHIFT) & NODE_L2DIV_OUT_MARK; + mult = l1div_loopc; + div = l1div_ref * l2div_out; + if (div == 0) { + rate = NODE_CLK_FREQ_DEF; + } else { + rate = refclk_rate * mult / div; + } + + return rate; +} + +static long ls2x_node_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct pll_config pll_cfg; + unsigned int mult, div; + + if (rate == 0 || *parent_rate == 0) + return 0; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return NODE_CLK_FREQ_DEF; + } + + mult = pll_cfg.l1_loopc; + div = pll_cfg.l1_div_ref * pll_cfg.l2_divout; + + return refclk_rate * mult / div; +} + +static int ls2x_node_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pll_config pll_cfg; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return -EINVAL; + } + + ls2k_config_pll(reg + PLL_SYS0_OFF, &pll_cfg); + return 0; +} + +static struct clk_ops ls2x_node_clk_ops = { + .recalc_rate = ls2x_node_recalc_rate, + .round_rate = ls2x_node_round_rate, + .set_rate = ls2x_node_set_rate, +}; + +static unsigned long ls2x_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int freqscale; + u64 val; + + val = __raw_readq(reg + PLL_FREQ_SC); + freqscale = (val >> FREQSCALE_NODE_SHIFT) & FREQSCALE_NODE_MARK; + + return parent_rate * (freqscale + 1) / 8; +} + +static long ls2x_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + int scale; + if (!rate || !*parent_rate) + return 0; + + scale = rate * 8 / (*parent_rate) - 1; + return (*parent_rate) * (scale + 1) / 8; +} + +static int ls2x_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int scale; + u64 val; + + if (!rate || !parent_rate) + return -EINVAL; + + scale = rate * 8 / parent_rate - 1; + val = __raw_readq(reg + PLL_FREQ_SC); + val &= (~FREQSCALE_NODE_MARK) << FREQSCALE_NODE_SHIFT; + val |= (scale & FREQSCALE_NODE_MARK) << FREQSCALE_NODE_SHIFT; + __raw_writeq(val, reg + PLL_FREQ_SC); + return 0; +} + +static struct clk_ops ls2x_cpu_clk_ops = { + .recalc_rate = ls2x_cpu_recalc_rate, + .round_rate = ls2x_cpu_round_rate, + .set_rate = ls2x_cpu_set_rate, +}; + +#ifdef CONFIG_SND_LS1X_SOC_I2S +#if defined(CONFIG_MIPS) +#define REMAP CKSEG1ADDR +#else +#define REMAP TO_UNCAC +#endif +#define LS2X_APB_BASE 0x1fe00000 +#define LS2X_I2S_CONFIG_REG \ + ((void __iomem *)REMAP(LS2X_APB_BASE + (0xd004))) +#define LS2X_I2S_CONFIG_MCLK_MASK (0xFF) +#define LS2X_I2S_CONFIG_MCLK_SHIFT (0) + +static unsigned long ls2x_i2s_mclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 ratio, val; + + val = __raw_readl(LS2X_I2S_CONFIG_REG); + ratio = (val >> LS2X_I2S_CONFIG_MCLK_SHIFT) & LS2X_I2S_CONFIG_MCLK_MASK; + + return parent_rate / (2 * (ratio + 1)); +} + +static long ls2x_i2s_mclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 ratio; + if (!rate || !*parent_rate) + return 0; + + ratio = (*parent_rate) / (2 * rate) - 1; + return (*parent_rate) / (2 * (ratio + 1)); +} + +static int ls2x_i2s_mclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 ratio, val; + + ratio = parent_rate / (2 * rate) - 1; + val = __raw_readl(LS2X_I2S_CONFIG_REG); + val &= ~(LS2X_I2S_CONFIG_MCLK_MASK << LS2X_I2S_CONFIG_MCLK_SHIFT); + val |= (ratio & LS2X_I2S_CONFIG_MCLK_MASK) << LS2X_I2S_CONFIG_MCLK_SHIFT; + __raw_writel(val, LS2X_I2S_CONFIG_REG); + return 0; +} + +static struct clk_ops ls2x_i2s_mclk_ops = { + .recalc_rate = ls2x_i2s_mclk_recalc_rate, + .round_rate = ls2x_i2s_mclk_round_rate, + .set_rate = ls2x_i2s_mclk_set_rate, + +}; +#endif // CONFIG_SND_LS1X_SOC_I2S +#endif // CONFIG_LOONGSON2K_SYSCLK_SOFT + + +static struct clk ** __init ls2x_clk_setup(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + struct clk **clks; + int ndivs = CLK_CNT; + + char *con_ids[] = {"ref_clk", "node_clk", "cpu_clk", "ddr_clk", + "gpu_clk", "hda_clk", "dc_clk", "pix0_clk", "pix1_clk", + "gmac_clk", "sata_clk", "usb_clk", "apb_clk", "spi_clk", "i2s_mclk"}; + + int err = -EINVAL; + + reg = of_iomap(node, 0); + if(!reg) { + pr_err("Could not map registers for divs-clk: %pOF\n", node); + return NULL; + } + pr_debug("lsdbg---> ls2x_clk_setup reg:0x%p 0x%lx", reg, (ulong)(reg - PLL_SYS0_OFF)); + reg -= PLL_SYS0_OFF; + + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if(!clk_data) + goto out_unmap; + clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); + if(!clks) + goto free_clkdata; + + clk_data->clks = clks; + clk_data->clk_num = CLK_CNT; + clks[CLK_REF] = of_clk_get_by_name(node, con_ids[CLK_REF]); + if (IS_ERR(clks[CLK_REF])) { + pr_err("%s: no external clock '%s' provided\n", + __func__, con_ids[CLK_REF]); + err = -ENODEV; + goto free_clks; + } + err = clk_register_clkdev(clks[CLK_REF], con_ids[CLK_REF], NULL); + if (err) { + goto free_clks; + } + + refclk_rate = clk_get_rate(clks[CLK_REF]); + pr_debug("lsdbg--->ls2x_clk_setup refclk rate:%ld", refclk_rate); + if (!refclk_rate) { + err = -EINVAL; + goto free_clks; + } + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_LOWFREQ + int fixed_clks[] = {875000000, 875000000, 480000000, 320000000, 24000000, + 200000000, 100000000, 100000000, 125000000, 85000000, 85000000, 85000000}; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_HIGFREQ + int fixed_clks[] = {1000000000, 1000000000, 600000000, 400000000, 24000000, + 400000000, 200000000, 200000000, 125000000, 150000000, 150000000, 150000000}; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT + { + u64 ctrl; + unsigned int l1div_out, l1div_loopc, l1div_ref; + unsigned int l2div_out; + unsigned int mult, div; + struct clk_hw *node_clk_hw; + struct clk_hw *cpu_clk_hw; + #ifdef CONFIG_SND_LS1X_SOC_I2S + struct clk_hw *i2s_mclk_hw; + #endif + + node_clk_hw = clk_hw_register_pll(NULL, "node_clk", "ref_clk", &ls2x_node_clk_ops, 0); + clks[CLK_NODE] = node_clk_hw->clk; + clk_register_clkdev(clks[CLK_NODE], "node_clk", NULL); + + cpu_clk_hw = clk_hw_register_pll(NULL, "cpu_clk", "node_clk", &ls2x_cpu_clk_ops, 0); + clks[CLK_CPU] = cpu_clk_hw->clk; + clk_register_clkdev(clks[CLK_CPU], "cpu_clk", NULL); + + /* ddr gpu hda clk */ + ctrl = __raw_readq(reg + PLL_DLL0_OFF); + l1div_out = (ctrl >> DDR_L1DIV_OUT_SHIFT) & DDR_L1DIV_OUT_MARK; + l1div_loopc = (ctrl >> DDR_L1DIV_LOOPC_SHIFT) & DDR_L1DIV_LOOPC_MARK; + l1div_ref = (ctrl >> DDR_L1DIV_REF_SHIFT) & DDR_L1DIV_REF_MARK; + ctrl = __raw_readq(reg + PLL_DLL1_OFF); + l2div_out = (ctrl >> DDR_L2DIV_OUT_DDR_SHIFT) & DDR_L2DIV_OUT_DDR_MARK; + mult = l1div_loopc; + div = l1div_ref * l2div_out; + + clks[CLK_DDR] = clk_register_fixed_factor(NULL, "ddr_clk", "ref_clk", 0, mult, div); + clk_register_clkdev(clks[CLK_DDR], "ddr_clk", NULL); + + l2div_out = (ctrl >> DDR_L2DIV_OUT_GPU_SHIFT) & DDR_L2DIV_OUT_GPU_MARK; + div = l1div_ref * l2div_out; + clks[CLK_GPU] = clk_register_fixed_factor(NULL, "gpu_clk", "ref_clk", 0, mult, div); + clk_register_clkdev(clks[CLK_GPU], "gpu_clk", NULL); + + l2div_out = (ctrl >> DDR_L2DIV_OUT_HDA_SHIFT) & DDR_L2DIV_OUT_HDA_MARK; + div = l1div_ref * l2div_out; + clks[CLK_HDA] = clk_register_fixed_factor(NULL, "hda_clk", "ref_clk", 0, mult, div); + clk_register_clkdev(clks[CLK_HDA], "hda_clk", NULL); + + /* dc gmac clk */ + ctrl = __raw_readq(reg + PLL_DC0_OFF); + l1div_out = (ctrl >> DC_L1DIV_OUT_SHIFT) & DC_L1DIV_OUT_MARK; + l1div_loopc = (ctrl >> DC_L1DIV_LOOPC_SHIFT) & DC_L1DIV_LOOPC_MARK; + l1div_ref = (ctrl >> DC_L1DIV_REF_SHIFT) & DC_L1DIV_REF_MARK; + ctrl = __raw_readq(reg + PLL_DC1_OFF); + l2div_out = (ctrl >> DC_L2DIV_OUT_DC_SHIFT) & DC_L2DIV_OUT_DC_MARK; + mult = l1div_loopc; + div = l1div_ref * l2div_out; + + clks[CLK_DC] = clk_register_fixed_factor(NULL, "dc_clk", "ref_clk", 0, mult, div); + clk_register_clkdev(clks[CLK_DC], "dc_clk", NULL); + + l2div_out = (ctrl >> DC_L2DIV_OUT_GMAC_SHIFT) & DC_L2DIV_OUT_GMAC_MARK; + div = l1div_ref * l2div_out; + clks[CLK_GMAC] = clk_register_fixed_factor(NULL, "gmac_clk", "ref_clk", 0, mult, div); + clk_register_clkdev(clks[CLK_GMAC], "gmac_clk", NULL); + + /* apb usb sata clk */ + ctrl = __raw_readq(reg + PLL_FREQ_SC); + { + unsigned int sata_freqscale; + unsigned int usb_freqscale; + unsigned int apb_freqscale; + + usb_freqscale = (ctrl >> FREQSCALE_USB_SHIFT) & FREQSCALE_USB_MARK; + clks[CLK_USB] = clk_register_fixed_factor(NULL, "usb_clk", "gmac_clk", 0, usb_freqscale + 1, 8); + clk_register_clkdev(clks[CLK_USB], "usb_clk", NULL); + sata_freqscale = (ctrl >> FREQSCALE_SATA_SHIFT) & FREQSCALE_SATA_MARK; + clks[CLK_SATA] = clk_register_fixed_factor(NULL, "sata_clk", "gmac_clk", 0, sata_freqscale + 1, 8); + clk_register_clkdev(clks[CLK_SATA], "sata_clk", NULL); + apb_freqscale = (ctrl >> FREQSCALE_APB_SHIFT) & FREQSCALE_APB_MARK; + clks[CLK_APB] = clk_register_fixed_factor(NULL, "apb_clk", "gmac_clk", 0, apb_freqscale + 1, 8); + clk_register_clkdev(clks[CLK_APB], "apb_clk", NULL); + } + #ifdef CONFIG_SND_LS1X_SOC_I2S + i2s_mclk_hw = clk_hw_register_pll(NULL, "mclk", "apb_clk", &ls2x_i2s_mclk_ops, 0); + clks[CLK_I2S_MCLK] = i2s_mclk_hw->clk; + clk_register_clkdev(clks[CLK_I2S_MCLK], "mclk", NULL); + #endif + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_BYPASS + for (i = start_; i <= end_; i++) { + clks[i] = clks[CLK_REF]; + clk_register_clkdev(clks[i], con_ids[i], NULL); + } +#endif + + clks[CLK_SPI] = clks[CLK_REF]; + clk_register_clkdev(clks[CLK_SPI], con_ids[CLK_SPI], NULL); + + if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) { + pr_err("%s: failed to add clock provider for\n", + __func__); + goto free_clks; + } + + return clks; + +free_clks: + kfree(clks); +free_clkdata: + kfree(clk_data); +out_unmap: + iounmap(reg); + return NULL; +} + +static void __init ls2x_clk_init(struct device_node *node) +{ + ls2x_clk_setup(node); +} + +CLK_OF_DECLARE(ls2x_clk, "loongson,ls2x-clk", ls2x_clk_init); \ No newline at end of file diff --git a/drivers/clk/loongson/clk-loongson2k300.c b/drivers/clk/loongson/clk-loongson2k300.c new file mode 100644 index 000000000..668f59d5b --- /dev/null +++ b/drivers/clk/loongson/clk-loongson2k300.c @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2024 Loongson(GD) Inc. + * Author: loongson-gz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define REF_CLK_NAME "ref_clk" +#define NODE_CLK_FREQ_DEF 1000000000 + +static unsigned long refclk_rate; +static void __iomem *reg; + +static struct clk** clks = NULL; + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT + +// 还没实现 CPU 变频支持 代码是从 2K500 那里拿过来的 +#define CPU_CLK_CHANGE 0 + +#if 0 +#define DEBUG_INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__) +#else +#define DEBUG_INFO(fmt, ...) +#endif + +#define PARENT_BRANCH 0 +#define PARENT_CLONE 1 + +struct ls2k_reg_field_desc { + unsigned int reg_offset; + unsigned int bit_offset; + unsigned int bit_mask; +}; + +typedef unsigned long (*clock_magnification_calu_func)(struct ls2k_reg_field_desc*); + +/* + * 本驱动中用于描述时钟结构的单个时钟的结构体,其组成的数组用于描述整个时钟结构 + * 其中不同的变量的值用于表示这个时钟的一些定义 + * name: 这是时钟的名字 + * parent_name: 这是时钟的上一层时钟的名字,比如DDR PLL的时钟 分频后 就是 CPU的时钟 那么可以说 CPU的 parent_name 是 Node PLL + * parent_relevant: 和上一层时钟的关系,这个变量用于 fixed 为 1的时候的处理流程,主要是为了区分 clks* 怎么来 + * PARENT_BRANCH 代表是一个分支,PARENT_CLONE 代表是同一个时钟,而定义是不是分支主要是看两个时钟之间有没有分频或倍频 + * 比如 DRR_PLL的时钟 分频后 获得 HDA 的时钟 那么就是 PARENT_BRANCH + * 比如 BOOT 的时钟 直接引到 BOOT SPI里面 那么就是 代表是一个分支, PARENT_CLONE + * dts_desc: 这是一个重要的变量 为1 的时候,意味着要去dts里面找这个时钟的声明,比如参考时钟 + * essential_n: 在 dts_desc 为 1的时候,如果找不到该时钟,如果这个值为 1 将不会报错,只是会提示,不会影响该时钟管理驱动的初始化。例如 PCI的外部时钟,不提供也没什么事 + * fixed: 这个值为1的时候,代表这个时钟在本内核中是固定不变的 不为1的时候,就可以改变 + * *_reg_set 和 *_calu_func: 在 fixed == 1 的时候有效 *_reg_set 用于登记 分频 和 倍频的寄存器位域 + * *calu_func是怎么处理这些寄存器的值的函数(就是怎么算系数) 不过我们也有提供通用的 ls2k_mul_calu ls2k_div_calu 等函数 + * clk_ops: 这个值就是一个结构体 需要填充 recalc_rate round_rate set_rate 函数的实现(linux这边要求的) 详见 ls2k300_node_clk_ops 和 ls2k300_cpu_clk_ops + * + * 所以 需要注意 + * name parent_name 是必填的 + * dts_desc 为 1的时候 相关的要填的变量是 essential_n(可选 不填的话 static 下 自动为0) 其他变量不起效 + * parent_relevant 为 PARENT_BRANCH 的时候 + * fixed 为 1的时候 *_reg_set 和 *_calu_func(可选 不填的话 static 下 自动为0) 其他变量不起效 + * fixed 为 0的时候 clk_ops(一定要实现) 其他变量不起效 + * parent_relevant 为 PARENT_CLONE + * parent_name 不能为空 其他变量不起效 + * + * *_reg_set 声明的时候 必须在在末尾添加 {.bit_mask = 0} 这是判断数组结束的标志 + * *_calu_func 可以为空 + * + * static 声明 ls2k_clock_desc数组时, 可不填的变量 就是 0 + */ +struct ls2k_clock_desc { + char* name; + char* parent_name; + int parent_relevant; //branch or clone if branch reg_set or clk_ops enable if clone just clockA == clockB + int dts_desc; + int essential_n; + int fixed; // if fixed reg_set enable or clk_ops enable + + struct ls2k_reg_field_desc* mul_reg_set; + clock_magnification_calu_func mul_calu_func; + struct ls2k_reg_field_desc* div_reg_set; + clock_magnification_calu_func div_calu_func; + struct ls2k_reg_field_desc* freqscale_reg_set; + clock_magnification_calu_func freqscale_mul_calu_func; + clock_magnification_calu_func freqscale_div_calu_func; + + struct clk_ops* clk_ops; +}; + +#if (CPU_CLK_CHANGE == 1) + +struct pll_config { + unsigned int l1_div_ref; + unsigned int l1_loopc; + unsigned int l1_odiv; +}; + +// 系统时钟需满足如下几个限制: +// 1.可配分频器的输出 refclk/L1_div_ref 在 20~40MHz 范围内 +// 2.PLL 倍频值 refclk/L1_div_ref*L1_loopc 需要在 1.2GHz~3.2GHz +static int ls2k_caculate_freq(unsigned long freq, struct pll_config *pll_cfg) +{ + unsigned long pll_in_clk; + unsigned long loopc; + unsigned long div; + int found = 0; + + if (!pll_cfg) + return -EINVAL; + + // Keep refclk/L1_div_ref in range 20MHz ~ 40Mhz + pll_cfg->l1_div_ref = 4; + pll_in_clk = refclk_rate >> 2; // 25MHz // 100/4 = 25 + + // 2*7 = 14 1.4Ghz 3*7 = 21 2.1GHz + // 3*6 = 18 1.8GHz + if(freq >= 700000000) + div = 2; + else + div = 3; + + //keep 1.2 ~ 3.2 + // and keep 1.2 ~ 2.1 will be better + loopc = (freq * div) / pll_in_clk; + + if (!found) { + pll_cfg->l1_div_ref = 0; + pll_cfg->l1_loopc = 0; + pll_cfg->l1_odiv = 0; + return -EINVAL; + } + + pll_cfg->l1_loopc = loopc; + pll_cfg->l1_odiv = div; + return 0; +} + +//PLL 寄存器设置 用于 时钟频率设置生效 +/* + * 注意手册上写的 3.5.2 软件配置 + * 第二步和第三步是一起的,也就是代码中 + * li.w t1, (NODE_DIV << 24) | (NODE_LOOPC << 16) | (NODE_REFC << 8) + * 没有管 pd_pll 会被设置成 0 的原因 + */ +static void ls2k_config_pll(void __iomem *pll_base, struct pll_config *pll_cfg) +{ + int timeout = 200; + u32 val; + + /* pd_pll 1 */ + val = __raw_readl(pll_base); + val |= BIT(5); + __raw_writel(val, pll_base); + + /* 除了 pll_sel_* [2:0] 设置为0 pll_soft_set (3) 设置为0 其他的寄存器按需设置*/ + /* pd_pll 0 */ + val = (pll_cfg->l1_odiv << PLL_GENERIC_0_ODIV_SHIFT) | + (pll_cfg->l1_loopc << PLL_GENERIC_0_LOOPC_SHIFT) | + (pll_cfg->l1_div_ref << PLL_GENERIC_0_REFC_SHIFT); + __raw_writel(val, pll_base); + + /* 其他信号不变 pll_soft_set (3) 信号设置为 1 */ + val = __raw_readl(pll_base); + val |= BIT(3); + __raw_writel(val, pll_base); + + /* 等待 pll_lock (7) 锁定 也就是变成 1 */ + while(!(__raw_readl(pll_base) & BIT(7)) && (timeout-- > 0)) { + udelay(1); + } + + /* pll_sel_* [2:0] 设置为1 */ + val = __raw_readl(pll_base); + val |= 0x7; + __raw_writel(val, pll_base); +} + + +// 这是计算当前频率的 +static unsigned long ls2k300_node_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 ctrl; + unsigned int div_loopc, div_ref, odiv; + unsigned int mult, div; + unsigned long rate; + + /* node cpu clk */ + ctrl = __raw_readl(reg + PLL_NODE_0_OFFSET); + div_loopc = (ctrl >> PLL_NODE_0_LOOPC_SHIFT) & PLL_NODE_0_LOOPC_MASK; + div_ref = (ctrl >> PLL_NODE_0_REFC_SHIFT) & PLL_NODE_0_REFC_MASK; + odiv = (ctrl >> PLL_NODE_0_ODIV_SHIFT) & PLL_NODE_0_ODIV_MASK; + mult = div_loopc; + // div = div_ref * odiv; + div = div_ref; + if (div == 0) { + rate = NODE_CLK_FREQ_DEF; + } else { + rate = refclk_rate * mult / div; + } + + return rate; +} + +static long ls2k300_node_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct pll_config pll_cfg; + unsigned int mult, div; + + if (rate == 0 || *parent_rate == 0) + return 0; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return NODE_CLK_FREQ_DEF; + } + + mult = pll_cfg.l1_loopc; + div = pll_cfg.l1_div_ref * pll_cfg.l1_odiv; + + return refclk_rate * mult / div; +} + +static int ls2k300_node_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pll_config pll_cfg; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return -EINVAL; + } + + ls2k_config_pll(reg + PLL_NODE_0_OFFSET, &pll_cfg); + return 0; +} + +static struct clk_ops ls2k300_node_clk_ops = { + .recalc_rate = ls2k300_node_recalc_rate, + .round_rate = ls2k300_node_round_rate, + .set_rate = ls2k300_node_set_rate, +}; + +static unsigned long ls2k300_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int freqscale; + u32 val; + + val = __raw_readl(reg + PLL_FREQ_SC); + freqscale = (val >> PLL_FREQSCALE_NODE_SHIFT) & PLL_FREQSCALE_NODE_MASK; + ++freqscale; + freqscale >>= 3; // /8 + + return parent_rate / freqscale; +} + +static long ls2k300_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + int scale; + if (!rate || !*parent_rate) + return 0; + + scale = rate * 8 / (*parent_rate) - 1; + return (*parent_rate) * (scale + 1) / 8; +} + +static int ls2k300_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int scale; + u32 val; + + if (!rate || !parent_rate) + return -EINVAL; + + scale = rate * 8 / parent_rate - 1; + val = __raw_readl(reg + PLL_FREQ_SC); + val &= (~PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT; + val |= (scale & PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT; + __raw_writel(val, reg + PLL_FREQ_SC); + return 0; +} + +static struct clk_ops ls2k300_cpu_clk_ops = { + .recalc_rate = ls2k300_cpu_recalc_rate, + .round_rate = ls2k300_cpu_round_rate, + .set_rate = ls2k300_cpu_set_rate, +}; +#endif // end #if (CPU_CLK_CHANGE == 1) + +//通用的倍率计算函数 不管是乘法还是除法 +static unsigned int ls2k_generic_magnification_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + total *= value; + ++i; + } + return total; +} + +static unsigned int ls2k_mul_calu(struct ls2k_reg_field_desc* reg_set) +{ + return ls2k_generic_magnification_calu(reg_set); +} + +static unsigned int ls2k_div_calu(struct ls2k_reg_field_desc* reg_set) +{ + return ls2k_generic_magnification_calu(reg_set); +} + +//通用的freqscale 乘法部分计算 需要 + 1 (7+1) +// 最高bit 作为 freq_mode +// freq_mode = 0 fout=fin*(freqscale[2:0]+1)/8 +// freq_mode = 1 fout=fin/(freqscale[2:0]+1) +static unsigned long ls2k_freqscale_mul_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value, mode; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + + // 判断最高bit 长度为 4, 右移 3 bit 就知道mode了 + mode = value >> 3; + if (mode) + total *= 1; // fout=fin/(freqscale[2:0]+1) 也就是 x1 + else + total *= ((value & 0x7) + 1); // fout=fin*(freqscale[2:0]+1)/8 也就是 低 3 bit 有效 + ++i; + } + return total; +} + +//通用的freqscale 除法部分计算 按照位域的最大值 + 1 比如 usb 就是 (freqscale + 1 / 8) 这个8就是返回值 +// 这是 最高bit 作为 freq_mode +// freq_mode = 0 fout=fin*(freqscale[2:0]+1)/8 +// freq_mode = 1 fout=fin/(freqscale[2:0]+1) +static unsigned long ls2k_freqscale_div_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value, mode; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + + // 判断最高bit 长度为 4, 右移 3 bit 就知道mode了 + mode = value >> 3; + + if (mode) + total *= ((value & 0x7) + 1); // fout=fin/(freqscale[2:0]+1) 也就是 低 3 bit 有效 + else + total *= 8; // fout=fin*(freqscale[2:0]+1)/8 也就是 / 8 + ++i; + } + return total; +} + +static unsigned long ls2k300_sdio_freqscale_mul_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + if(!reg_set[i].bit_mask) + break; + + // CLK/freqscale[27:24] 所以 * 1 + total *= 1; + ++i; + } + return total; +} + +static unsigned long ls2k300_sdio_freqscale_div_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + + // CLK/freqscale[27:24] 是 0 或者 1 都是除以 1,也就是不分频 + if (!value) + value = 1; + + total /= value; + ++i; + } + return total; +} + +/* + * @brief: 倍频系数和分频系数的计算函数 + * 逻辑上就是对*_reg_set分析 把全部倍频系数 乘起来 把全部分频系数 乘起来 + * 然后 赋值到 参数 mul_result 和 div_result + * @param[in]: 参数1(cur_clock): 这个要计算的时钟的描述结构体 里面包含 *_reg_set + * @param[out]: 参数2(mul_result): 倍频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回 + * @param[out]: 参数3(div_result): 分频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回 + * @return: 参数传进来的指针有一个为NULL 返回 -EFAULT 否则返回 0 代表计算成功 + */ +static int ls2k_mul_div_total_calu(struct ls2k_clock_desc* cur_clock, unsigned long* mul_result, unsigned long* div_result) +{ + unsigned long mul = 1, div = 1, freq_sc_mul = 1, freq_sc_div = 1; + + if(!mul_result || !div_result) + return -EFAULT; + if(!cur_clock) + return -EFAULT; + + if(cur_clock->mul_reg_set) + mul = cur_clock->mul_calu_func ? cur_clock->mul_calu_func(cur_clock->mul_reg_set) + : ls2k_mul_calu(cur_clock->mul_reg_set); + if(cur_clock->div_reg_set) + div = cur_clock->div_calu_func ? cur_clock->div_calu_func(cur_clock->div_reg_set) + : ls2k_div_calu(cur_clock->div_reg_set); + if(cur_clock->freqscale_reg_set) { + freq_sc_mul = cur_clock->freqscale_mul_calu_func ? cur_clock->freqscale_mul_calu_func(cur_clock->freqscale_reg_set) + : ls2k_freqscale_mul_calu(cur_clock->freqscale_reg_set); + freq_sc_div = cur_clock->freqscale_div_calu_func ? cur_clock->freqscale_div_calu_func(cur_clock->freqscale_reg_set) + : ls2k_freqscale_div_calu(cur_clock->freqscale_reg_set); + } + mul *= freq_sc_mul; + div *= freq_sc_div; + mul_result[0] = mul; + div_result[0] = div; + return 0; +} + +/* + * 时钟的描述结构体的数组 用于描述 整个时钟结构 + * 注意 必须要让父节点先于子节点声明,否则在处理逻辑中会引发bug + * 参考时钟 REF_CLK_NAME 必须是第一个变量 + */ +static struct ls2k_clock_desc ls2k_clock_desc_set[CLK_CNT] = { + { + .name = REF_CLK_NAME, + .parent_name = NULL, + .dts_desc = 1, + }, + + // node pll + { + .name = "node_pll_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, +#if (CPU_CLK_CHANGE == 1) + // 还没实现 + .clk_ops = &ls2k300_node_clk_ops, +#else + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_0_OFFSET, .bit_offset = PLL_NODE_0_LOOPC_SHIFT, .bit_mask = PLL_NODE_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_0_OFFSET, .bit_offset = PLL_NODE_0_REFC_SHIFT, .bit_mask = PLL_NODE_0_REFC_MASK}, {.bit_mask = 0}}, +#endif + }, + { + .name = "cpu_clk", + .parent_name = "node_pll_clk", + .parent_relevant = PARENT_BRANCH, +#if (CPU_CLK_CHANGE == 1) + // 还没实现 + .clk_ops = &ls2k300_cpu_clk_ops, +#else + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_0_OFFSET, .bit_offset = PLL_NODE_0_ODIV_SHIFT, .bit_mask = PLL_NODE_0_ODIV_MASK}, {.bit_mask = 0}}, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_NODE_SHIFT, .bit_mask = PLL_FREQSCALE_NODE_MASK}, {.bit_mask = 0}}, +#endif + }, + { + .name = "scache_clk", + .parent_name = "node_pll_clk", + .parent_relevant = PARENT_BRANCH, +#if (CPU_CLK_CHANGE == 1) + // 还没实现 + .clk_ops = &ls2k300_cpu_clk_ops, +#else + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_0_OFFSET, .bit_offset = PLL_NODE_0_ODIV_SHIFT, .bit_mask = PLL_NODE_0_ODIV_MASK}, {.bit_mask = 0}}, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_NODE_SHIFT, .bit_mask = PLL_FREQSCALE_NODE_MASK}, {.bit_mask = 0}}, +#endif + }, + { + .name = "iodma_clk", + .parent_name = "node_pll_clk", + .parent_relevant = PARENT_BRANCH, +#if (CPU_CLK_CHANGE == 1) + // 还没实现 + .clk_ops = &ls2k300_cpu_clk_ops, +#else + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_0_OFFSET, .bit_offset = PLL_NODE_0_ODIV_SHIFT, .bit_mask = PLL_NODE_0_ODIV_MASK}, {.bit_mask = 0}}, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_NODE_SHIFT, .bit_mask = PLL_FREQSCALE_NODE_MASK}, {.bit_mask = 0}}, +#endif + }, + { + .name = "gmac_clk", + .parent_name = "node_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_1_OFFSET, .bit_offset = PLL_NODE_1_ODIV_GMAC_SHIFT, .bit_mask = PLL_NODE_1_ODIV_GMAC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "i2s_clk", + .parent_name = "node_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_NODE_1_OFFSET, .bit_offset = PLL_NODE_1_ODIV_I2S_SHIFT, .bit_mask = PLL_NODE_1_ODIV_I2S_MASK}, {.bit_mask = 0}}, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_I2S_SHIFT, .bit_mask = PLL_FREQSCALE_I2S_MASK}, {.bit_mask = 0}}, + }, + + // ddr pll + { + .name = "ddr_pll_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_LOOPC_SHIFT, .bit_mask = PLL_DDR_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_REFC_SHIFT, .bit_mask = PLL_DDR_0_REFC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "ddr_clk", + .parent_name = "ddr_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_ODIV_SHIFT, .bit_mask = PLL_DDR_0_ODIV_MASK}, {.bit_mask = 0}}, + }, + { + .name = "net_clk", + .parent_name = "ddr_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_NET_SHIFT, .bit_mask = PLL_DDR_1_ODIV_NET_MASK}, {.bit_mask = 0}}, + }, + { + .name = "devs_clk", + .parent_name = "ddr_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_DEVS_SHIFT, .bit_mask = PLL_DDR_1_ODIV_DEVS_MASK}, {.bit_mask = 0}}, + }, + { + .name = "usb_clk", + .parent_name = "devs_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_USB_SHIFT, .bit_mask = PLL_FREQSCALE_USB_MASK}, {.bit_mask = 0}}, + }, + { + .name = "apb_clk", + .parent_name = "devs_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_APB_SHIFT, .bit_mask = PLL_FREQSCALE_APB_MASK}, {.bit_mask = 0}}, + }, + { + .name = "boot_clk", + .parent_name = "devs_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_BOOT_SHIFT, .bit_mask = PLL_FREQSCALE_BOOT_MASK}, {.bit_mask = 0}}, + }, + { + .name = "sdio_clk", + .parent_name = "devs_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_mul_calu_func = ls2k300_sdio_freqscale_mul_calu, + .freqscale_div_calu_func = ls2k300_sdio_freqscale_div_calu, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_SDIO_SHIFT, .bit_mask = PLL_FREQSCALE_SDIO_MASK}, {.bit_mask = 0}}, + }, + + // pix pll + { + .name = "pix_pll_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX_0_OFFSET, .bit_offset = PLL_PIX0_0_LOOPC_SHIFT, .bit_mask = PLL_PIX0_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX_0_OFFSET, .bit_offset = PLL_PIX0_0_REFC_SHIFT, .bit_mask = PLL_PIX0_0_REFC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "pix_clk", + .parent_name = "pix_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX_0_OFFSET, .bit_offset = PLL_PIX0_0_ODIV_SHIFT, .bit_mask = PLL_PIX0_0_ODIV_MASK}, {.bit_mask = 0}}, + }, + { + .name = "gmacbp_clk", + .parent_name = "pix_pll_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX_1_OFFSET, .bit_offset = PLL_PIX0_1_ODIV_GMACBP_SHIFT, .bit_mask = PLL_PIX0_1_ODIV_GMACBP_MASK}, {.bit_mask = 0}}, + }, +}; + +/* + * @brief: CONFIG_LOONGSON2_SYSCLK_SOFT 的情况下 ls2k300_clk_setup 函数要做的主要任务 + * 扫描上面的 ls2k_clock_desc_set 得到整个时钟结构 + * 然而第一个时钟默认不用扫,在 ls2k300_clk_setup 开头就会对 参数时钟中注册 + * @param[in]: 参数1(node): 这是dts中 始终管理驱动节点的信息 例如这个 clocks:clock-controller@1fe10400 + * 用于扫描dts中指定的外部时钟 + * @return: 如果成功返回 0 + * clks 为空则返回 -EFAULT + * node 为空 | dts里面没有要扫描的必要的时钟声明 | ls2k_clock_desc_set里面的某一个 PARENT_CLONE 的 父节点不存在 返回 -ENODEV + */ +static int register_clock(struct device_node *node) +{ + int i = 1; // ref clk is registered! + struct ls2k_clock_desc* cur_clock; + unsigned long rate; + + if(!clks) { + pr_err("%s func %s: ls2k300_clks kalloc failed\n", __FILE__, __func__); + return -EFAULT; + } + + for(; i < CLK_CNT; ++i) { + int err; + cur_clock = ls2k_clock_desc_set + i; + clks[i] = NULL; + if(cur_clock->dts_desc) { + //dts 扫描 + if(!node && !cur_clock->essential_n) { + pr_err("%s func %s: device node is NULL can't scan dts\n", __FILE__, __func__); + return -ENODEV; + } + + clks[i] = of_clk_get_by_name(node, cur_clock->name); + if (IS_ERR(clks[i])) { + pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n", + __FILE__, __func__, cur_clock->name, node->full_name); + if(cur_clock->essential_n) { + clks[i] = NULL; + continue; + } else + return -ENODEV; + } + err = clk_register_clkdev(clks[i], cur_clock->name, NULL); + if (err && !cur_clock->essential_n) + return err; + } + else if(cur_clock->parent_relevant == PARENT_BRANCH) { + if(cur_clock->fixed) { + //从父节点有分频倍频而来的的时钟 且固定不变 + unsigned long mul_total = 1, div_total = 1; + ls2k_mul_div_total_calu(cur_clock, &mul_total, &div_total); + clks[i] = clk_register_fixed_factor(NULL, cur_clock->name, cur_clock->parent_name, 0, mul_total, div_total); + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + else { + //从父节点有分频倍频而来的的时钟 且会改变 + struct clk_hw *cur_cpu_hw; + cur_cpu_hw = clk_hw_register_pll(NULL, cur_clock->name, cur_clock->parent_name, cur_clock->clk_ops, 0); + clks[i] = cur_cpu_hw->clk; + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + } + else if(cur_clock->parent_relevant == PARENT_CLONE) { + //从父节点直接使用的时钟 同一个时钟 + int parent_index = 0; + //null name not found + if(!cur_clock->parent_name) + parent_index = i; + for(; parent_index < i; ++parent_index) { + if(!strcmp(cur_clock->parent_name, ls2k_clock_desc_set[parent_index].name)) { + clks[i] = clks[parent_index]; + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + } + //not found parent + if(parent_index == i) { + //not found + printk(KERN_INFO "%s func %s: warning: clk not found parent clk, name is %s, parent is %s\n", + __FILE__, __func__, cur_clock->name, cur_clock->parent_name); + if(!cur_clock->essential_n) + return -ENODEV; + } + } + else + printk(KERN_INFO "%s func %s: warning: clk register type unkonwn! name is %s\n", __FILE__, __func__, cur_clock->name); + + if(clks[i]) { + rate = clk_get_rate(clks[i]); + //文件开头 #if 判断开的话 就能看到所有扫描的时钟的信息 + DEBUG_INFO("cur clock info name:%s\tpname:%s\trate:%ld\tdts_desc:%d\tparent_relevant:%d\tfixed:%d\n", + cur_clock->name, cur_clock->parent_name, rate, cur_clock->dts_desc, cur_clock->parent_relevant, cur_clock->fixed); + } + } + return 0; +} +#endif + +static struct clk ** __init ls2k300_clk_setup(struct device_node *node) +{ +#ifndef CONFIG_LOONGSON2_SYSCLK_SOFT + int start_ = CLK_NODE; + int end_ = CLK_PCI; + int i = 0; + + char *con_ids[] = { REF_CLK_NAME, + "node_pll_clk", "cpu_clk", "scache_clk", "iodma_clk", "gmac_clk", "i2s_clk" + "ddr_pll_clk", "ddr_clk", "net_clk", "devs_clk", "usb_clk", "apb_clk", "boot_clk", "sdio_clk", + "pix_pll_clk", "pix_clk", "gmacbp_clk" + }; +#endif + struct clk_onecell_data *clk_data; + int ndivs = CLK_CNT; + int err = -EINVAL; + + reg = of_iomap(node, 0); + if(!reg) { + pr_err("%s: Could not map registers for divs-clk: %s\n", __func__, node->full_name); + return NULL; + } + pr_debug("lsdbg---> %s reg:0x%lx", __func__, (unsigned long)reg); + + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if(!clk_data) + goto out_unmap; + clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); + if(!clks) + goto free_clkdata; + + clk_data->clks = clks; + clk_data->clk_num = CLK_CNT; + + clks[CLK_REF] = of_clk_get_by_name(node, REF_CLK_NAME); + if (IS_ERR(clks[CLK_REF])) { + pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n", + __FILE__, __func__, REF_CLK_NAME, node->full_name); + err = -ENODEV; + goto free_clks; + } + err = clk_register_clkdev(clks[CLK_REF], REF_CLK_NAME, NULL); + if (err) { + goto free_clks; + } + + refclk_rate = clk_get_rate(clks[CLK_REF]); + pr_debug("lsdbg--->%s refclk rate:%ld", __func__, refclk_rate); + if (!refclk_rate) { + err = -EINVAL; + goto free_clks; + } + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_LOWFREQ + { + int fixed_clks[] = {120000000, + 750000000, 750000000, 750000000, 750000000, 125000000, 750000000, + 800000000, 800000000, 266000000, 100000000, 100000000, 100000000, 100000000, + 100000000, 100000000, 100000000 }; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_HIGFREQ + { + int fixed_clks[] = {120000000, + 1000000000, 1000000000, 1000000000, 1000000000, 125000000, 1000000000, + 1200000000, 1200000000, 400000000, 150000000, 150000000, 150000000, 150000000, + 200000000, 200000000, 200000000 + }; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT + if(register_clock(node)) + goto free_clks; +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_BYPASS + for (i = start_; i <= end_; i++) { + clks[i] = clks[CLK_REF]; + clk_register_clkdev(clks[i], con_ids[i], NULL); + } +#endif + + if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) { + pr_err("%s: failed to add clock provider for\n", + __func__); + goto free_clks; + } + + return clks; + +free_clks: + kfree(clks); +free_clkdata: + kfree(clk_data); +out_unmap: + iounmap(reg); + return NULL; +} + +static void __init ls2k300_clk_init(struct device_node *node) +{ + ls2k300_clk_setup(node); +} + +CLK_OF_DECLARE(ls2k300_clk, "loongson,ls2k300-clk", ls2k300_clk_init); diff --git a/drivers/clk/loongson/clk-loongson2k500.c b/drivers/clk/loongson/clk-loongson2k500.c new file mode 100644 index 000000000..9e890ef2b --- /dev/null +++ b/drivers/clk/loongson/clk-loongson2k500.c @@ -0,0 +1,767 @@ +/* + * Copyright (c) 2022 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define REF_CLK_NAME "ref_clk" +#define NODE_CLK_FREQ_DEF 500000000 + +static unsigned long refclk_rate; +static void __iomem *reg; + +static struct clk** clks = NULL; + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT + +#if 0 +#define DEBUG_INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__) +#else +#define DEBUG_INFO(fmt, ...) +#endif + +#define PARENT_BRANCH 0 +#define PARENT_CLONE 1 + +struct ls2k_reg_field_desc { + unsigned int reg_offset; + unsigned int bit_offset; + unsigned int bit_mask; +}; + +typedef unsigned long (*clock_magnification_calu_func)(struct ls2k_reg_field_desc*); + +/* + * 本驱动中用于描述时钟结构的单个时钟的结构体,其组成的数组用于描述整个时钟结构 + * 其中不同的变量的值用于表示这个时钟的一些定义 + * name: 这是时钟的名字 + * parent_name: 这是时钟的上一层时钟的名字,比如DDR PLL的时钟 分频后 就是 CPU的时钟 那么可以说 CPU的 parent_name 是 Node PLL + * parent_relevant: 和上一层时钟的关系,这个变量用于 fixed 为 1的时候的处理流程,主要是为了区分 clks* 怎么来 + * PARENT_BRANCH 代表是一个分支,PARENT_CLONE 代表是同一个时钟,而定义是不是分支主要是看两个时钟之间有没有分频或倍频 + * 比如 DRR_PLL的时钟 分频后 获得 HDA 的时钟 那么就是 PARENT_BRANCH + * 比如 BOOT 的时钟 直接引到 BOOT SPI里面 那么就是 代表是一个分支, PARENT_CLONE + * dts_desc: 这是一个重要的变量 为1 的时候,意味着要去dts里面找这个时钟的声明,比如参考时钟 + * essential_n: 在 dts_desc 为 1的时候,如果找不到该时钟,如果这个值为 1 将不会报错,只是会提示,不会影响该时钟管理驱动的初始化。例如 PCI的外部时钟,不提供也没什么事 + * fixed: 这个值为1的时候,代表这个时钟在本内核中是固定不变的 不为1的时候,就可以改变 + * *_reg_set 和 *_calu_func: 在 fixed == 1 的时候有效 *_reg_set 用于登记 分频 和 倍频的寄存器位域 + * *calu_func是怎么处理这些寄存器的值的函数(就是怎么算系数) 不过我们也有提供通用的 ls2k_mul_calu ls2k_div_calu 等函数 + * clk_ops: 这个值就是一个结构体 需要填充 recalc_rate round_rate set_rate 函数的实现(linux这边要求的) 详见 ls2k500_node_clk_ops 和 ls2k500_cpu_clk_ops + * + * 所以 需要注意 + * name parent_name 是必填的 + * dts_desc 为 1的时候 相关的要填的变量是 essential_n(可选 不填的话 static 下 自动为0) 其他变量不起效 + * parent_relevant 为 PARENT_BRANCH 的时候 + * fixed 为 1的时候 *_reg_set 和 *_calu_func(可选 不填的话 static 下 自动为0) 其他变量不起效 + * fixed 为 0的时候 clk_ops(一定要实现) 其他变量不起效 + * parent_relevant 为 PARENT_CLONE + * parent_name 不能为空 其他变量不起效 + * + * *_reg_set 声明的时候 必须在在末尾添加 {.bit_mask = 0} 这是判断数组结束的标志 + * *_calu_func 可以为空 + * + * static 声明 ls2k_clock_desc数组时, 可不填的变量 就是 0 + */ +struct ls2k_clock_desc { + char* name; + char* parent_name; + int parent_relevant; //branch or clone if branch reg_set or clk_ops enable if clone just clockA == clockB + int dts_desc; + int essential_n; + int fixed; // if fixed reg_set enable or clk_ops enable + + struct ls2k_reg_field_desc* mul_reg_set; + clock_magnification_calu_func mul_calu_func; + struct ls2k_reg_field_desc* div_reg_set; + clock_magnification_calu_func div_calu_func; + struct ls2k_reg_field_desc* freqscale_reg_set; + clock_magnification_calu_func freqscale_mul_calu_func; + clock_magnification_calu_func freqscale_div_calu_func; + + struct clk_ops* clk_ops; +}; + +struct pll_config { + unsigned int l1_div_ref; + unsigned int l1_loopc; + unsigned int l1_odiv; +}; + +// 系统时钟需满足如下几个限制: +// 1.可配分频器的输出 refclk/L1_div_ref 在 20~40MHz 范围内 +// 2.PLL 倍频值 refclk/L1_div_ref*L1_loopc 需要在 1.2GHz~3.2GHz +static int ls2k_caculate_freq(unsigned long freq, struct pll_config *pll_cfg) +{ + unsigned long pll_in_clk; + unsigned long loopc; + unsigned long div; + int found = 0; + + if (!pll_cfg) + return -EINVAL; + + // Keep refclk/L1_div_ref in range 20MHz ~ 40Mhz + pll_cfg->l1_div_ref = 4; + pll_in_clk = refclk_rate >> 2; // 25MHz // 100/4 = 25 + + // 2*7 = 14 1.4Ghz 3*7 = 21 2.1GHz + // 3*6 = 18 1.8GHz + if(freq >= 700000000) + div = 2; + else + div = 3; + + //keep 1.2 ~ 3.2 + // and keep 1.2 ~ 2.1 will be better + loopc = (freq * div) / pll_in_clk; + + if (!found) { + pll_cfg->l1_div_ref = 0; + pll_cfg->l1_loopc = 0; + pll_cfg->l1_odiv = 0; + return -EINVAL; + } + + pll_cfg->l1_loopc = loopc; + pll_cfg->l1_odiv = div; + return 0; +} + +//PLL 寄存器设置 用于 时钟频率设置生效 +/* + * 注意手册上写的 3.5.2 软件配置 + * 第二步和第三步是一起的,也就是代码中 + * li.w t1, (NODE_DIV << 24) | (NODE_LOOPC << 16) | (NODE_REFC << 8) + * 没有管 pd_pll 会被设置成 0 的原因 + */ +static void ls2k_config_pll(void __iomem *pll_base, struct pll_config *pll_cfg) +{ + int timeout = 200; + u32 val; + + /* pd_pll 1 */ + val = __raw_readl(pll_base); + val |= BIT(5); + __raw_writel(val, pll_base); + + /* 除了 pll_sel_* [2:0] 设置为0 pll_soft_set (3) 设置为0 其他的寄存器按需设置*/ + /* pd_pll 0 */ + val = (pll_cfg->l1_odiv << PLL_GENERIC_0_ODIV_SHIFT) | + (pll_cfg->l1_loopc << PLL_GENERIC_0_LOOPC_SHIFT) | + (pll_cfg->l1_div_ref << PLL_GENERIC_0_REFC_SHIFT); + __raw_writel(val, pll_base); + + /* 其他信号不变 pll_soft_set (3) 信号设置为 1 */ + val = __raw_readl(pll_base); + val |= BIT(3); + __raw_writel(val, pll_base); + + /* 等待 pll_lock (7) 锁定 也就是变成 1 */ + while(!(__raw_readl(pll_base) & BIT(7)) && (timeout-- > 0)) { + udelay(1); + } + + /* pll_sel_* [2:0] 设置为1 */ + val = __raw_readl(pll_base); + val |= 0x7; + __raw_writel(val, pll_base); +} + + +static unsigned long ls2k500_node_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 ctrl; + unsigned int div_loopc, div_ref, odiv; + unsigned int mult, div; + unsigned long rate; + + /* node cpu clk */ + ctrl = __raw_readl(reg + PLL_NODE_0_OFFSET); + div_loopc = (ctrl >> PLL_NODE_0_LOOPC_SHIFT) & PLL_NODE_0_LOOPC_MASK; + div_ref = (ctrl >> PLL_NODE_0_REFC_SHIFT) & PLL_NODE_0_REFC_MASK; + odiv = (ctrl >> PLL_NODE_0_ODIV_SHIFT) & PLL_NODE_0_ODIV_MASK; + mult = div_loopc; + div = div_ref * odiv; + if (div == 0) { + rate = NODE_CLK_FREQ_DEF; + } else { + rate = refclk_rate * mult / div; + } + + return rate; +} + +static long ls2k500_node_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct pll_config pll_cfg; + unsigned int mult, div; + + if (rate == 0 || *parent_rate == 0) + return 0; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return NODE_CLK_FREQ_DEF; + } + + mult = pll_cfg.l1_loopc; + div = pll_cfg.l1_div_ref * pll_cfg.l1_odiv; + + return refclk_rate * mult / div; +} + +static int ls2k500_node_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pll_config pll_cfg; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + if (ls2k_caculate_freq(rate, &pll_cfg)) { + return -EINVAL; + } + + ls2k_config_pll(reg + PLL_NODE_0_OFFSET, &pll_cfg); + return 0; +} + +static struct clk_ops ls2k500_node_clk_ops = { + .recalc_rate = ls2k500_node_recalc_rate, + .round_rate = ls2k500_node_round_rate, + .set_rate = ls2k500_node_set_rate, +}; + +static unsigned long ls2k500_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int freqscale; + u32 val; + + val = __raw_readl(reg + PLL_FREQ_SC); + freqscale = (val >> PLL_FREQSCALE_NODE_SHIFT) & PLL_FREQSCALE_NODE_MASK; + ++freqscale; + freqscale >>= 3; // /8 + + return parent_rate / freqscale; +} + +static long ls2k500_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + int scale; + if (!rate || !*parent_rate) + return 0; + + scale = rate * 8 / (*parent_rate) - 1; + return (*parent_rate) * (scale + 1) / 8; +} + +static int ls2k500_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int scale; + u32 val; + + if (!rate || !parent_rate) + return -EINVAL; + + scale = rate * 8 / parent_rate - 1; + val = __raw_readl(reg + PLL_FREQ_SC); + val &= (~PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT; + val |= (scale & PLL_FREQSCALE_NODE_MASK) << PLL_FREQSCALE_NODE_SHIFT; + __raw_writel(val, reg + PLL_FREQ_SC); + return 0; +} + +static struct clk_ops ls2k500_cpu_clk_ops = { + .recalc_rate = ls2k500_cpu_recalc_rate, + .round_rate = ls2k500_cpu_round_rate, + .set_rate = ls2k500_cpu_set_rate, +}; + +//通用的倍率计算函数 不管是乘法还是除法 +static unsigned int ls2k_generic_magnification_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + total *= value; + ++i; + } + return total; +} + +static unsigned int ls2k_mul_calu(struct ls2k_reg_field_desc* reg_set) +{ + return ls2k_generic_magnification_calu(reg_set); +} + +static unsigned int ls2k_div_calu(struct ls2k_reg_field_desc* reg_set) +{ + return ls2k_generic_magnification_calu(reg_set); +} + +//通用的freqscale 乘法部分计算 需要 + 1 (7+1) +static unsigned long ls2k_freqscale_mul_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned int total = 1; + while(1) { + unsigned int reg_value, value; + if(!reg_set[i].bit_mask) + break; + reg_value = __raw_readl(reg + reg_set[i].reg_offset); + value = (reg_value >> reg_set[i].bit_offset) & reg_set[i].bit_mask; + total *= (value + 1); + ++i; + } + return total; +} + +//通用的freqscale 除法部分计算 按照位域的最大值 + 1 比如 usb 就是 (freqscale + 1 / 8) 这个8就是返回值 +static unsigned long ls2k_freqscale_div_calu(struct ls2k_reg_field_desc* reg_set) +{ + int i = 0; + unsigned long total = 1; + while(1) { + if(!reg_set[i].bit_mask) + break; + total *= (reg_set[i].bit_mask + 1); + ++i; + } + return total; +} + +/* + * @brief: 倍频系数和分频系数的计算函数 + * 逻辑上就是对*_reg_set分析 把全部倍频系数 乘起来 把全部分频系数 乘起来 + * 然后 赋值到 参数 mul_result 和 div_result + * @param[in]: 参数1(cur_clock): 这个要计算的时钟的描述结构体 里面包含 *_reg_set + * @param[out]: 参数2(mul_result): 倍频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回 + * @param[out]: 参数3(div_result): 分频的总系数 需要传入一个 unsigned long 的变量的地址 作为结果返回 + * @return: 参数传进来的指针有一个为NULL 返回 -EFAULT 否则返回 0 代表计算成功 + */ +static int ls2k_mul_div_total_calu(struct ls2k_clock_desc* cur_clock, unsigned long* mul_result, unsigned long* div_result) +{ + unsigned long mul = 1, div = 1, freq_sc_mul = 1, freq_sc_div = 1; + + if(!mul_result || !div_result) + return -EFAULT; + if(!cur_clock) + return -EFAULT; + + if(cur_clock->mul_reg_set) + mul = cur_clock->mul_calu_func ? cur_clock->mul_calu_func(cur_clock->mul_reg_set) + : ls2k_mul_calu(cur_clock->mul_reg_set); + if(cur_clock->div_reg_set) + div = cur_clock->div_calu_func ? cur_clock->div_calu_func(cur_clock->div_reg_set) + : ls2k_div_calu(cur_clock->div_reg_set); + if(cur_clock->freqscale_reg_set) { + freq_sc_mul = cur_clock->freqscale_mul_calu_func ? cur_clock->freqscale_mul_calu_func(cur_clock->freqscale_reg_set) + : ls2k_freqscale_mul_calu(cur_clock->freqscale_reg_set); + freq_sc_div = cur_clock->freqscale_div_calu_func ? cur_clock->freqscale_div_calu_func(cur_clock->freqscale_reg_set) + : ls2k_freqscale_div_calu(cur_clock->freqscale_reg_set); + } + mul *= freq_sc_mul; + div *= freq_sc_div; + mul_result[0] = mul; + div_result[0] = div; + return 0; +} + +/* + * 时钟的描述结构体的数组 用于描述 整个时钟结构 + * 注意 必须要让父节点先于子节点声明,否则在处理逻辑中会引发bug + * 参考时钟 REF_CLK_NAME 必须是第一个变量 + */ +static struct ls2k_clock_desc ls2k_clock_desc_set[CLK_CNT] = { + { + .name = REF_CLK_NAME, + .parent_name = NULL, + .dts_desc = 1, + }, + { + .name = "node_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .clk_ops = &ls2k500_node_clk_ops, + }, + { + .name = "cpu_clk", + .parent_name = "node_clk", + .parent_relevant = PARENT_BRANCH, + .clk_ops = &ls2k500_cpu_clk_ops, + }, + { + .name = "scache_clk", + .parent_name = "node_clk", + .parent_relevant = PARENT_BRANCH, + .clk_ops = &ls2k500_cpu_clk_ops, + }, + { + .name = "iodma_clk", + .parent_name = "node_clk", + .parent_relevant = PARENT_BRANCH, + .clk_ops = &ls2k500_cpu_clk_ops, + }, + { + .name = "ddr_p_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_LOOPC_SHIFT, .bit_mask = PLL_DDR_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_REFC_SHIFT, .bit_mask = PLL_DDR_0_REFC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "ddr_clk", + .parent_name = "ddr_p_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_0_OFFSET, .bit_offset = PLL_DDR_0_ODIV_SHIFT, .bit_mask = PLL_DDR_0_ODIV_MASK}, {.bit_mask = 0}}, + }, + { + .name = "hda_clk", + .parent_name = "ddr_p_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_HDA_SHIFT, .bit_mask = PLL_DDR_1_ODIV_HDA_MASK}, {.bit_mask = 0}}, + }, + { + .name = "net_clk", + .parent_name = "ddr_p_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_DDR_1_OFFSET, .bit_offset = PLL_DDR_1_ODIV_NET_SHIFT, .bit_mask = PLL_DDR_1_ODIV_NET_MASK}, {.bit_mask = 0}}, + }, + { + .name = "print_lsu_clk", + .parent_name = "net_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_PRINT_SHIFT, .bit_mask = PLL_FREQSCALE_PRINT_MASK}, + {.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_LSU_SHIFT, .bit_mask = PLL_FREQSCALE_LSU_MASK}, + {.bit_mask = 0}}, + }, + { + .name = "dc0_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_LOOPC_SHIFT, .bit_mask = PLL_PIX0_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_REFC_SHIFT, .bit_mask = PLL_PIX0_0_REFC_MASK}, + {.reg_offset = PLL_PIX0_0_OFFSET, .bit_offset = PLL_PIX0_0_ODIV_SHIFT, .bit_mask = PLL_PIX0_0_ODIV_MASK}, + {.bit_mask = 0}}, + }, + { + .name = "dc1_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_LOOPC_SHIFT, .bit_mask = PLL_PIX1_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_REFC_SHIFT, .bit_mask = PLL_PIX1_0_REFC_MASK}, + {.reg_offset = PLL_PIX1_0_OFFSET, .bit_offset = PLL_PIX1_0_ODIV_SHIFT, .bit_mask = PLL_PIX1_0_ODIV_MASK}, + {.bit_mask = 0}}, + }, + { + .name = "soc_clk", + .parent_name = "ref_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .mul_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_LOOPC_SHIFT, .bit_mask = PLL_SOC_0_LOOPC_MASK}, {.bit_mask = 0}}, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_REFC_SHIFT, .bit_mask = PLL_SOC_0_REFC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "gpu_clk", + .parent_name = "soc_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_0_OFFSET, .bit_offset = PLL_SOC_0_ODIV_SHIFT, .bit_mask = PLL_SOC_0_ODIV_MASK}, {.bit_mask = 0}}, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_GPU_SHIFT, .bit_mask = PLL_FREQSCALE_GPU_MASK}, {.bit_mask = 0}}, + }, + { + .name = "gmac_clk", + .parent_name = "soc_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_1_OFFSET, .bit_offset = PLL_SOC_1_ODIV_GMAC_SHIFT, .bit_mask = PLL_SOC_1_ODIV_GMAC_MASK}, {.bit_mask = 0}}, + }, + { + .name = "sb_clk", + .parent_name = "soc_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .div_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_SOC_1_OFFSET, .bit_offset = PLL_SOC_1_ODIV_SB_SHIFT, .bit_mask = PLL_SOC_1_ODIV_SB_MASK}, {.bit_mask = 0}}, + }, + + { + .name = "boot_clk", + .parent_name = "sb_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_SB_SHIFT, .bit_mask = PLL_FREQSCALE_SB_MASK}, {.bit_mask = 0}}, + }, + { + .name = "sata_clk", + .parent_name = "boot_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_SATA_SHIFT, .bit_mask = PLL_FREQSCALE_SATA_MASK}, {.bit_mask = 0}}, + }, + { + .name = "usb_clk", + .parent_name = "boot_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_USB_SHIFT, .bit_mask = PLL_FREQSCALE_USB_MASK}, {.bit_mask = 0}}, + }, + { + .name = "apb_clk", + .parent_name = "boot_clk", + .parent_relevant = PARENT_BRANCH, + .fixed = 1, + .freqscale_reg_set = (struct ls2k_reg_field_desc[]){{.reg_offset = PLL_FREQ_SC, .bit_offset = PLL_FREQSCALE_APB_SHIFT, .bit_mask = PLL_FREQSCALE_APB_MASK}, {.bit_mask = 0}}, + }, +}; + +/* + * @brief: CONFIG_LOONGSON2_SYSCLK_SOFT 的情况下 ls2k500_clk_setup 函数要做的主要任务 + * 扫描上面的 ls2k_clock_desc_set 得到整个时钟结构 + * 然而第一个时钟默认不用扫,在 ls2k500_clk_setup 开头就会对 参数时钟中注册 + * @param[in]: 参数1(node): 这是dts中 始终管理驱动节点的信息 例如这个 clocks:clock-controller@1fe10400 + * 用于扫描dts中指定的外部时钟 + * @return: 如果成功返回 0 + * clks 为空则返回 -EFAULT + * node 为空 | dts里面没有要扫描的必要的时钟声明 | ls2k_clock_desc_set里面的某一个 PARENT_CLONE 的 父节点不存在 返回 -ENODEV + */ +static int register_clock(struct device_node *node) +{ + int i = 1; // ref clk is registered! + struct ls2k_clock_desc* cur_clock; + unsigned long rate; + + if(!clks) { + pr_err("%s func %s: ls2k500_clks kalloc failed\n", __FILE__, __func__); + return -EFAULT; + } + + for(; i < CLK_CNT; ++i) { + int err; + cur_clock = ls2k_clock_desc_set + i; + clks[i] = NULL; + if(cur_clock->dts_desc) { + //dts 扫描 + if(!node && !cur_clock->essential_n) { + pr_err("%s func %s: device node is NULL can't scan dts\n", __FILE__, __func__); + return -ENODEV; + } + + clks[i] = of_clk_get_by_name(node, cur_clock->name); + if (IS_ERR(clks[i])) { + pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n", + __FILE__, __func__, cur_clock->name, node->full_name); + if(cur_clock->essential_n) { + clks[i] = NULL; + continue; + } else + return -ENODEV; + } + err = clk_register_clkdev(clks[i], cur_clock->name, NULL); + if (err && !cur_clock->essential_n) + return err; + } + else if(cur_clock->parent_relevant == PARENT_BRANCH) { + if(cur_clock->fixed) { + //从父节点有分频倍频而来的的时钟 且固定不变 + unsigned long mul_total = 1, div_total = 1; + ls2k_mul_div_total_calu(cur_clock, &mul_total, &div_total); + clks[i] = clk_register_fixed_factor(NULL, cur_clock->name, cur_clock->parent_name, 0, mul_total, div_total); + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + else { + //从父节点有分频倍频而来的的时钟 且会改变 + struct clk_hw *cur_cpu_hw; + cur_cpu_hw = clk_hw_register_pll(NULL, cur_clock->name, cur_clock->parent_name, cur_clock->clk_ops, 0); + clks[i] = cur_cpu_hw->clk; + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + } + else if(cur_clock->parent_relevant == PARENT_CLONE) { + //从父节点直接使用的时钟 同一个时钟 + int parent_index = 0; + //null name not found + if(!cur_clock->parent_name) + parent_index = i; + for(; parent_index < i; ++parent_index) { + if(!strcmp(cur_clock->parent_name, ls2k_clock_desc_set[parent_index].name)) { + clks[i] = clks[parent_index]; + clk_register_clkdev(clks[i], cur_clock->name, NULL); + } + } + //not found parent + if(parent_index == i) { + //not found + printk(KERN_INFO "%s func %s: warning: clk not found parent clk, name is %s, parent is %s\n", + __FILE__, __func__, cur_clock->name, cur_clock->parent_name); + if(!cur_clock->essential_n) + return -ENODEV; + } + } + else + printk(KERN_INFO "%s func %s: warning: clk register type unkonwn! name is %s\n", __FILE__, __func__, cur_clock->name); + + if(clks[i]) { + rate = clk_get_rate(clks[i]); + //文件开头 #if 判断开的话 就能看到所有扫描的时钟的信息 + DEBUG_INFO("cur clock info name:%s\tpname:%s\trate:%ld\tdts_desc:%d\tparent_relevant:%d\tfixed:%d\n", + cur_clock->name, cur_clock->parent_name, rate, cur_clock->dts_desc, cur_clock->parent_relevant, cur_clock->fixed); + } + } + return 0; +} +#endif + +static struct clk ** __init ls2k500_clk_setup(struct device_node *node) +{ +#ifndef CONFIG_LOONGSON2_SYSCLK_SOFT + int start_ = CLK_NODE; + int end_ = CLK_PCI; + int i = 0; + + char *con_ids[] = { REF_CLK_NAME, + "node_clk", "cpu_clk", "scache_clk", "iodma_clk", + "ddr_p_clk", "ddr_clk", "hda_clk", "net_clk", "print_lsu_clk", + "dc0_clk", + "dc1_clk", + "soc_clk", "gpu_clk", "gmac_clk", "sb_clk", "boot_clk", "sata_clk", "usb_clk", "apb_clk", + "pci_clk"}; +#endif + struct clk_onecell_data *clk_data; + int ndivs = CLK_CNT; + int err = -EINVAL; + + reg = of_iomap(node, 0); + if(!reg) { + pr_err("%s: Could not map registers for divs-clk: %s\n", __func__, node->full_name); + return NULL; + } + pr_debug("lsdbg---> %s reg:0x%lx", __func__, (unsigned long)reg); + + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if(!clk_data) + goto out_unmap; + clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); + if(!clks) + goto free_clkdata; + + clk_data->clks = clks; + clk_data->clk_num = CLK_CNT; + + clks[CLK_REF] = of_clk_get_by_name(node, REF_CLK_NAME); + if (IS_ERR(clks[CLK_REF])) { + pr_err("%s func %s: not found %s clock in device tree or maybe you should add this clock in %s clock-names key\n", + __FILE__, __func__, REF_CLK_NAME, node->full_name); + err = -ENODEV; + goto free_clks; + } + err = clk_register_clkdev(clks[CLK_REF], REF_CLK_NAME, NULL); + if (err) { + goto free_clks; + } + + refclk_rate = clk_get_rate(clks[CLK_REF]); + pr_debug("lsdbg--->%s refclk rate:%ld", __func__, refclk_rate); + if (!refclk_rate) { + err = -EINVAL; + goto free_clks; + } + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_LOWFREQ + { + int fixed_clks[] = {100000000, + 500000000, 500000000, 500000000, 500000000, + 480000000, 480000000, 24000000, 320000000, 1250000, + 200000000, + 200000000, + 200000000, 200000000, 125000000, 100000000, 100000000, 100000000, 100000000, 100000000, + 33000000}; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_HIGFREQ + { + int fixed_clks[] = {100000000, + 800000000, 800000000, 800000000, 800000000, + 600000000, 600000000, 24000000, 400000000, 1562500, + 400000000, + 400000000, + 300000000, 300000000, 125000000, 150000000, 150000000, 150000000, 150000000, 150000000, + 33000000}; + for (i = start_; i <= end_; i++) { + clks[i] = clk_register_fixed_rate(NULL, con_ids[i], NULL, 0, fixed_clks[i-start_]); + clk_register_clkdev(clks[i], con_ids[i], NULL); + } + } +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_SOFT + if(register_clock(node)) + goto free_clks; +#endif + +#ifdef CONFIG_LOONGSON2_SYSCLK_HW_BYPASS + for (i = start_; i <= end_; i++) { + clks[i] = clks[CLK_REF]; + clk_register_clkdev(clks[i], con_ids[i], NULL); + } +#endif + + if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) { + pr_err("%s: failed to add clock provider for\n", + __func__); + goto free_clks; + } + + return clks; + +free_clks: + kfree(clks); +free_clkdata: + kfree(clk_data); +out_unmap: + iounmap(reg); + return NULL; +} + +static void __init ls2k500_clk_init(struct device_node *node) +{ + ls2k500_clk_setup(node); +} + +CLK_OF_DECLARE(ls2k500_clk, "loongson,ls2k500-clk", ls2k500_clk_init); diff --git a/drivers/clk/loongson/clk.c b/drivers/clk/loongson/clk.c new file mode 100644 index 000000000..f336a3126 --- /dev/null +++ b/drivers/clk/loongson/clk.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2012-2016 Zhang, Keguang + */ + +#include +#include + +#include "clk.h" + +struct clk_hw *__init clk_hw_register_pll(struct device *dev, + const char *name, + const char *parent_name, + const struct clk_ops *ops, + unsigned long flags) +{ + int ret; + struct clk_hw *hw; + struct clk_init_data init; + + /* allocate the divider */ + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + hw->init = &init; + + /* register the clock */ + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(hw); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/loongson/clk.h b/drivers/clk/loongson/clk.h new file mode 100644 index 000000000..124642302 --- /dev/null +++ b/drivers/clk/loongson/clk.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2012-2016 Zhang, Keguang + */ + +#ifndef __LOONGSON1_CLK_H +#define __LOONGSON1_CLK_H + +struct clk_hw *clk_hw_register_pll(struct device *dev, + const char *name, + const char *parent_name, + const struct clk_ops *ops, + unsigned long flags); + +#endif /* __LOONGSON1_CLK_H */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e994d6e07..1a4aebd71 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -733,6 +733,14 @@ config XILINX_ZYNQMP_DPDMA driver provides the dmaengine required by the DisplayPort subsystem display driver. +config LSIA_DMA + tristate "Loongson LSIA DMA support" + depends on LOONGARCH + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the Loongson LSIA DMA controller. + # driver files source "drivers/dma/amd/Kconfig" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 5b2a52f4f..ce896e74a 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o obj-$(CONFIG_ST_FDMA) += st_fdma.o obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/ obj-$(CONFIG_INTEL_LDMA) += lgm/ +obj-$(CONFIG_LSIA_DMA) += lsia_dma.o obj-y += amd/ obj-y += mediatek/ diff --git a/drivers/dma/lsia_dma.c b/drivers/dma/lsia_dma.c new file mode 100644 index 000000000..a1127e033 --- /dev/null +++ b/drivers/dma/lsia_dma.c @@ -0,0 +1,703 @@ +/* + * LOONGSON LSIA DMA controller + * + * Copyright (C) 2024 Loongson Technology Corporation Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virt-dma.h" + +#define DMA_ISR 0x0000 /* DMA Int Status Reg */ +#define DMA_IFCR 0x0004 /* DMA Int Flag Clear Reg */ +#define DMA_TCI BIT(1) /* Transfer Complete Interrupt */ +#define DMA_HTI BIT(2) /* Half Transfer Interrupt */ +#define DMA_TEI BIT(3) /* Transfer Error Interrupt */ +#define DMA_MASKI (DMA_TCI \ + | DMA_HTI \ + | DMA_TEI) + +/* DMA channel x Configuration Register */ +#define DMA_CCR(x) (0x0008 + 0x14 * (x)) +#define DMA_CCR_M2M BIT(14) +#define DMA_CCR_PL_MASK GENMASK(13, 12) +#define DMA_CCR_PL(n) ((n & 0x3) << 12) +#define DMA_CCR_MSIZE_MASK GENMASK(11, 10) +#define DMA_CCR_MSIZE(n) ((n & 0x3) << 10) +#define DMA_CCR_PSIZE_MASK GENMASK(9, 8) +#define DMA_CCR_PSIZE(n) ((n & 0x3) << 8) +#define DMA_CCR_MINC BIT(7) /* Memory increment mode */ +#define DMA_CCR_PINC BIT(6) /* Peripheral increment mode */ +#define DMA_CCR_CIRC BIT(5) /* Circular mode */ +#define DMA_CCR_DIR BIT(4) +#define DMA_CCR_TEIE BIT(3) /* Transfer Error Int Enable */ +#define DMA_CCR_HTIE BIT(2) /* Half Transfer Complete Int Enable */ +#define DMA_CCR_TCIE BIT(1) /* Transfer Complete Int Enable */ +#define DMA_CCR_EN BIT(0) /* Stream Enable */ +#define DMA_CCR_CFG_MASK (DMA_CCR_PINC \ + | DMA_CCR_MINC \ + | DMA_CCR_PL_MASK) +#define DMA_CCR_IRQ_MASK (DMA_CCR_TCIE \ + | DMA_CCR_HTIE \ + | DMA_CCR_TEIE) + +#define DMA_CNDTR(x) (0x000c + 0x14 * (x)) +#define DMA_CPAR(x) (0x0010 + 0x14 * (x)) +#define DMA_CMAR(x) (0x0014 + 0x14 * (x)) + + +#define DMA_MAX_CHANNELS 8 + +#define LS_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +struct lsia_dma_chan_reg { + u32 dma_ccr; + u32 dma_cndtr; + u32 dma_cpar; + u32 dma_cmar; +}; + +struct lsia_dma_sg_req { + u32 len; + struct lsia_dma_chan_reg chan_reg; +}; + +struct lsia_dma_desc { + struct virt_dma_desc vdesc; + bool cyclic; + u32 num_sgs; + struct lsia_dma_sg_req sg_req[]; +}; + +struct lsia_dma_chan { + struct virt_dma_chan vchan; + u32 id; + u32 irq; + struct lsia_dma_desc *desc; + u32 next_sg; + struct dma_slave_config dma_sconfig; + struct lsia_dma_chan_reg chan_reg; +}; + +struct lsia_dma_device { + struct dma_device ddev; + void __iomem *base; + u32 dma_channels; + struct lsia_dma_chan chan[DMA_MAX_CHANNELS]; +}; + +static struct lsia_dma_device *lsia_dma_get_dev(struct lsia_dma_chan *chan) +{ + return container_of(chan->vchan.chan.device, struct lsia_dma_device, + ddev); +} + +static struct lsia_dma_chan *to_lsia_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct lsia_dma_chan, vchan.chan); +} + +static struct lsia_dma_desc *to_lsia_dma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct lsia_dma_desc, vdesc); +} + +static struct device *chan2dev(struct lsia_dma_chan *chan) +{ + return &chan->vchan.chan.dev->device; +} + +static u32 lsia_dma_read(struct lsia_dma_device *dmadev, u32 reg) +{ + return readl_relaxed(dmadev->base + reg); +} + +static void lsia_dma_write(struct lsia_dma_device *dmadev, u32 reg, u32 val) +{ + writel_relaxed(val, dmadev->base + reg); +} + +static struct lsia_dma_desc *lsia_dma_alloc_desc(u32 num_sgs) +{ + return kzalloc(sizeof(struct lsia_dma_desc) + + sizeof(struct lsia_dma_sg_req) * num_sgs, GFP_NOWAIT); +} + +static int lsia_dma_slave_config(struct dma_chan *c, + struct dma_slave_config *config) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + + memcpy(&chan->dma_sconfig, config, sizeof(*config)); + + return 0; +} + +static void lsia_dma_irq_clear(struct lsia_dma_chan *chan, u32 flags) +{ + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + u32 dma_ifcr; + + dma_ifcr = flags << (4 * chan->id); + lsia_dma_write(dmadev, DMA_IFCR, dma_ifcr); +} + +static void lsia_dma_stop(struct lsia_dma_chan *chan) +{ + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + u32 dma_ccr; + + dma_ccr = lsia_dma_read(dmadev, DMA_CCR(chan->id)); + dma_ccr &= ~(DMA_CCR_IRQ_MASK | DMA_CCR_EN); + lsia_dma_write(dmadev, DMA_CCR(chan->id), dma_ccr); + + lsia_dma_irq_clear(chan, DMA_MASKI); +} + +static int lsia_dma_terminate_all(struct dma_chan *c) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (chan->desc) { + vchan_terminate_vdesc(&chan->desc->vdesc); + lsia_dma_stop(chan); + chan->desc = NULL; + } + vchan_get_all_descriptors(&chan->vchan, &head); + spin_unlock_irqrestore(&chan->vchan.lock, flags); + vchan_dma_desc_free_list(&chan->vchan, &head); + + return 0; +} + +static void lsia_dma_synchronize(struct dma_chan *c) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + + vchan_synchronize(&chan->vchan); +} + +static void lsia_dma_dump_reg(struct lsia_dma_chan *chan) +{ + struct lsia_dma_device *dev = lsia_dma_get_dev(chan); + u32 id = chan->id; + + dev_dbg(chan2dev(chan), "CR: %x\n", lsia_dma_read(dev, DMA_CCR(id))); + dev_dbg(chan2dev(chan), "NDTR:%x\n", lsia_dma_read(dev, DMA_CNDTR(id))); + dev_dbg(chan2dev(chan), "PAR: %x\n", lsia_dma_read(dev, DMA_CPAR(id))); + dev_dbg(chan2dev(chan), "MAR: %x\n", lsia_dma_read(dev, DMA_CMAR(id))); +} + +static void lsia_dma_start_transfer(struct lsia_dma_chan *chan) +{ + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + struct virt_dma_desc *vdesc; + struct lsia_dma_sg_req *sg_req; + struct lsia_dma_chan_reg *reg; + + lsia_dma_stop(chan); + + if (!chan->desc) { + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return; + list_del(&vdesc->node); + chan->desc = to_lsia_dma_desc(vdesc); + chan->next_sg = 0; + } + + if (chan->next_sg == chan->desc->num_sgs) + chan->next_sg = 0; + + sg_req = &chan->desc->sg_req[chan->next_sg]; + reg = &sg_req->chan_reg; + + lsia_dma_write(dmadev, DMA_CCR(chan->id), reg->dma_ccr); + lsia_dma_write(dmadev, DMA_CNDTR(chan->id), reg->dma_cndtr); + lsia_dma_write(dmadev, DMA_CPAR(chan->id), reg->dma_cpar); + lsia_dma_write(dmadev, DMA_CMAR(chan->id), reg->dma_cmar); + + chan->next_sg++; + + lsia_dma_dump_reg(chan); + + /* Start DMA */ + reg->dma_ccr |= DMA_CCR_EN; + lsia_dma_write(dmadev, DMA_CCR(chan->id), reg->dma_ccr); + + dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); +} + +static void lsia_dma_configure_next_sg(struct lsia_dma_chan *chan) +{ + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + struct lsia_dma_sg_req *sg_req; + u32 dma_cmar, id; + u32 dma_ccr; + + id = chan->id; + + if (chan->next_sg == chan->desc->num_sgs) + chan->next_sg = 0; + + /* stop to update mem addr */ + dma_ccr = lsia_dma_read(dmadev, DMA_CCR(id)); + dma_ccr &= ~DMA_CCR_EN; + lsia_dma_write(dmadev, DMA_CCR(id), dma_ccr); + + sg_req = &chan->desc->sg_req[chan->next_sg]; + dma_cmar = sg_req->chan_reg.dma_cmar; + lsia_dma_write(dmadev, DMA_CMAR(id), dma_cmar); + + /* start transition */ + dma_ccr |= DMA_CCR_EN; + lsia_dma_write(dmadev, DMA_CCR(id), dma_ccr); +} + +static void lsia_dma_handle_chan_done(struct lsia_dma_chan *chan) +{ + if (chan->desc) { + if (chan->desc->cyclic) { + vchan_cyclic_callback(&chan->desc->vdesc); + /* DMA_CCR_CIRC mode don't need update register */ + if (chan->desc->num_sgs == 1) + return; + lsia_dma_configure_next_sg(chan); + chan->next_sg++; + } else { + if (chan->next_sg == chan->desc->num_sgs) { + vchan_cookie_complete(&chan->desc->vdesc); + chan->desc = NULL; + } + lsia_dma_start_transfer(chan); + } + } +} + +static irqreturn_t lsia_dma_chan_irq(int irq, void *devid) +{ + struct lsia_dma_chan *chan = devid; + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + u32 status, scr; + + spin_lock(&chan->vchan.lock); + + status = (lsia_dma_read(dmadev, DMA_ISR) >> (4 * chan->id)) & DMA_MASKI; + scr = lsia_dma_read(dmadev, DMA_CCR(chan->id)); + status &= scr; + + if (status & DMA_TCI) + lsia_dma_handle_chan_done(chan); + + if (status & DMA_HTI) + lsia_dma_irq_clear(chan, DMA_HTI); + + if (status & DMA_TEI) + dev_err(chan2dev(chan), "Trans Error\n"); + + lsia_dma_irq_clear(chan, status); + + spin_unlock(&chan->vchan.lock); + + return IRQ_HANDLED; +} + +static void lsia_dma_issue_pending(struct dma_chan *c) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + unsigned long flags; + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (vchan_issue_pending(&chan->vchan) && !chan->desc) { + dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan); + lsia_dma_start_transfer(chan); + + } + spin_unlock_irqrestore(&chan->vchan.lock, flags); +} + +static int lsia_dma_set_xfer_param(struct lsia_dma_chan *chan, + enum dma_transfer_direction direction, + enum dma_slave_buswidth *buswidth, + u32 buf_len) +{ + enum dma_slave_buswidth dev_width; + int mem_size, periph_size; + u32 dma_ccr; + + switch (direction) { + case DMA_MEM_TO_DEV: + dev_width = chan->dma_sconfig.dst_addr_width; + chan->chan_reg.dma_cpar = chan->dma_sconfig.dst_addr; + dma_ccr = DMA_CCR_DIR; + break; + case DMA_DEV_TO_MEM: + dev_width = chan->dma_sconfig.src_addr_width; + chan->chan_reg.dma_cpar = chan->dma_sconfig.src_addr; + dma_ccr = DMA_CCR_MINC; + break; + default: + return -EINVAL; + } + *buswidth = dev_width; + if ((dev_width > 4) || (dev_width < 0)) + return -EINVAL; + + periph_size = (dev_width >> 1) & 0x3; + /* Set memory data size to 4 bytes */ + mem_size = 0x2; + dma_ccr |= DMA_CCR_PSIZE(periph_size) | DMA_CCR_MSIZE(mem_size); + + /* Set DMA control register */ + chan->chan_reg.dma_ccr &= ~(DMA_CCR_PSIZE_MASK | DMA_CCR_MSIZE_MASK); + chan->chan_reg.dma_ccr |= dma_ccr; + + return 0; +} + +static struct dma_async_tx_descriptor *lsia_dma_prep_slave_sg( + struct dma_chan *c, struct scatterlist *sgl, + u32 sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + struct lsia_dma_desc *desc; + struct scatterlist *sg; + enum dma_slave_buswidth buswidth; + u32 nb_data_items; + int i; + + desc = lsia_dma_alloc_desc(sg_len); + if (!desc) + return NULL; + + for_each_sg(sgl, sg, sg_len, i) { + if (lsia_dma_set_xfer_param(chan, direction, &buswidth, + sg_dma_len(sg))) + return NULL; + + desc->sg_req[i].len = sg_dma_len(sg); + + nb_data_items = desc->sg_req[i].len / buswidth; + if (nb_data_items >= (1 << 16)) { + dev_err(chan2dev(chan), "nb items not supported\n"); + kfree(desc); + return NULL; + } + desc->sg_req[i].chan_reg.dma_ccr = chan->chan_reg.dma_ccr; + desc->sg_req[i].chan_reg.dma_cpar = chan->chan_reg.dma_cpar; + desc->sg_req[i].chan_reg.dma_cmar = sg_dma_address(sg); + desc->sg_req[i].chan_reg.dma_cndtr = nb_data_items; + } + + desc->num_sgs = sg_len; + desc->cyclic = false; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *lsia_dma_prep_dma_cyclic( + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + struct lsia_dma_desc *desc; + enum dma_slave_buswidth buswidth; + u32 num_periods, nb_data_items; + int i; + + if (buf_len % period_len) + return NULL; + + if (lsia_dma_set_xfer_param(chan, direction, &buswidth, period_len)) + return NULL; + + nb_data_items = period_len / buswidth; + if (nb_data_items >= (1 << 16)) { + dev_err(chan2dev(chan), "number of items not supported\n"); + return NULL; + } + + /* Enable Circular mode */ + if (buf_len == period_len) + chan->chan_reg.dma_ccr |= DMA_CCR_CIRC; + + num_periods = buf_len / period_len; + + desc = lsia_dma_alloc_desc(num_periods); + if (!desc) + return NULL; + + for (i = 0; i < num_periods; i++) { + desc->sg_req[i].len = period_len; + desc->sg_req[i].chan_reg.dma_ccr = chan->chan_reg.dma_ccr; + desc->sg_req[i].chan_reg.dma_cpar = chan->chan_reg.dma_cpar; + desc->sg_req[i].chan_reg.dma_cmar = buf_addr; + desc->sg_req[i].chan_reg.dma_cndtr = nb_data_items; + buf_addr += period_len; + } + + desc->num_sgs = num_periods; + desc->cyclic = true; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static size_t lsia_dma_desc_residue(struct lsia_dma_chan *chan, + struct lsia_dma_desc *desc, + u32 next_sg) +{ + u32 residue, width, ndtr; + int i; + struct lsia_dma_device *dmadev = lsia_dma_get_dev(chan); + + width = (lsia_dma_read(dmadev, DMA_CCR(chan->id)) >> 8) & 0x3; + ndtr = lsia_dma_read(dmadev, DMA_CNDTR(chan->id)); + residue = ndtr << width; + + if (chan->desc->cyclic && next_sg == 0) + return residue; + + for (i = next_sg; i < desc->num_sgs; i++) + residue += desc->sg_req[i].len; + + return residue; +} + +static enum dma_status lsia_dma_tx_status(struct dma_chan *c, + dma_cookie_t cookie, + struct dma_tx_state *state) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(c, cookie, state); + if (status == DMA_COMPLETE || !state) + return status; + + spin_lock_irqsave(&chan->vchan.lock, flags); + vdesc = vchan_find_desc(&chan->vchan, cookie); + if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) + state->residue = lsia_dma_desc_residue(chan, chan->desc, + chan->next_sg); + else if (vdesc) + state->residue = lsia_dma_desc_residue(chan, + to_lsia_dma_desc(vdesc), 0); + + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + return status; +} + +static int lsia_dma_alloc_chan_resources(struct dma_chan *c) +{ + struct lsia_dma_chan *chan = to_lsia_dma_chan(c); + + lsia_dma_stop(chan); + return 0; +} + +static void lsia_dma_free_chan_resources(struct dma_chan *c) +{ + vchan_free_chan_resources(to_virt_chan(c)); +} + +static void lsia_dma_desc_free(struct virt_dma_desc *vdesc) +{ + kfree(container_of(vdesc, struct lsia_dma_desc, vdesc)); +} + +static struct dma_chan *lsia_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct lsia_dma_device *dmadev = ofdma->of_dma_data; + struct device *dev = dmadev->ddev.dev; + struct lsia_dma_chan *chan; + struct dma_chan *c; + u32 channel_id, stream_config; + + if (dma_spec->args_count < 2) + return NULL; + + channel_id = dma_spec->args[0]; + stream_config = dma_spec->args[1]; + + if (channel_id >= DMA_MAX_CHANNELS) { + dev_err(dev, "Bad channel\n"); + return NULL; + } + + chan = &dmadev->chan[channel_id]; + + c = dma_get_slave_channel(&chan->vchan.chan); + if (!c) { + dev_err(dev, "No more channels available\n"); + return NULL; + } + + memset(&chan->chan_reg, 0, sizeof(struct lsia_dma_chan_reg)); + chan->chan_reg.dma_ccr = stream_config & (DMA_CCR_CFG_MASK + | DMA_CCR_IRQ_MASK); + + return c; +} + +static int lsia_dma_probe(struct platform_device *pdev) +{ + struct lsia_dma_chan *chan; + struct lsia_dma_device *dmadev; + struct dma_device *dd; + struct resource *res; /* IO mem resources */ + int chls, i, ret, irq; + + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); + if (!dmadev) + return -ENOMEM; + + dd = &dmadev->ddev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmadev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmadev->base)) + return PTR_ERR(dmadev->base); + + dd->dev = &pdev->dev; + + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_PRIVATE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + dd->device_alloc_chan_resources = lsia_dma_alloc_chan_resources; + dd->device_config = lsia_dma_slave_config; + dd->device_prep_slave_sg = lsia_dma_prep_slave_sg; + dd->device_prep_dma_cyclic = lsia_dma_prep_dma_cyclic; + dd->device_issue_pending = lsia_dma_issue_pending; + dd->device_synchronize = lsia_dma_synchronize; + dd->device_tx_status = lsia_dma_tx_status; + dd->device_terminate_all = lsia_dma_terminate_all; + dd->device_free_chan_resources = lsia_dma_free_chan_resources; + + dd->src_addr_widths = LS_DMA_BUSWIDTHS; + dd->dst_addr_widths = LS_DMA_BUSWIDTHS; + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + + INIT_LIST_HEAD(&dd->channels); + + if (of_property_read_u32((&pdev->dev)->of_node, "dma-channels", &chls)) + chls = DMA_MAX_CHANNELS; + dmadev->dma_channels = chls; + + for (i = 0; i < chls; i++) { + chan = &dmadev->chan[i]; + chan->id = i; + chan->vchan.desc_free = lsia_dma_desc_free; + vchan_init(&chan->vchan, dd); + } + + ret = dma_async_device_register(dd); + if (ret) + return ret; + + for (i = 0; i < chls; i++) { + chan = &dmadev->chan[i]; + irq = platform_get_irq(pdev, i); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ: %d\n", irq); + ret = -EINVAL; + goto err_unregister; + } + chan->irq = irq; + ret = request_irq(chan->irq, lsia_dma_chan_irq, 0, + dev_name(chan2dev(chan)), chan); + if (ret) { + dev_err(&pdev->dev, "request %d err %d\n", irq, ret); + goto err_unregister; + } + } + + ret = of_dma_controller_register(pdev->dev.of_node, + lsia_dma_of_xlate, dmadev); + if (ret) { + dev_err(&pdev->dev, "DMA registration failed %d\n", ret); + goto err_unregister; + } + + platform_set_drvdata(pdev, dmadev); + + dev_info(&pdev->dev, "DMA driver registered\n"); + + return 0; + +err_unregister: + dma_async_device_unregister(dd); + + return ret; +} + +static void lsia_dma_remove(struct platform_device *pdev) +{ + struct lsia_dma_device *dmadev = platform_get_drvdata(pdev); + int i; + + of_dma_controller_free(pdev->dev.of_node); + + for (i = 0; i < dmadev->dma_channels; i++) { + free_irq(dmadev->chan[i].irq, dmadev); + tasklet_kill(&dmadev->chan[i].vchan.task); + } + dma_async_device_unregister(&dmadev->ddev); +} + +static const struct of_device_id lsia_dma_of_match[] = { + { .compatible = "loongson,lsia-dma", }, + {}, +}; +MODULE_DEVICE_TABLE(of, lsia_dma_of_match); + +static struct platform_driver lsia_dma_driver = { + .driver = { + .name = "loongson-lsia-dma", + .of_match_table = lsia_dma_of_match, + }, + .probe = lsia_dma_probe, + .remove = lsia_dma_remove, +}; + +static int __init lsia_dma_init(void) +{ + return platform_driver_register(&lsia_dma_driver); +} +subsys_initcall(lsia_dma_init); + +static void __exit lsia_dma_exit(void) +{ + platform_driver_unregister(&lsia_dma_driver); +} +module_exit(lsia_dma_exit); + +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_DESCRIPTION("Looongson LSIA DMA Controller driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d93cd4f72..0a2f7ce25 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -411,6 +411,14 @@ config GPIO_LOONGSON help Driver for GPIO functionality on Loongson-2F/3A/3B processors. +config GPIO_LOONGSON_IRQCHIP + bool "Loongson GPIO support irqchip (e.g. 2P500)" + default n + depends on CPU_LOONGSON2EF || CPU_LOONGSON64 || LOONGARCH + select GPIOLIB_IRQCHIP + help + driver for GPIO With IRQCHIP. + config GPIO_LOONGSON_64BIT tristate "Loongson 64 bit GPIO support" depends on LOONGARCH || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1429e8c02..b4c890578 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_GPIO_LJCA) += gpio-ljca.o obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o +obj-$(CONFIG_GPIO_LOONGSON_IRQCHIP) += gpio-loongson-irq.o obj-$(CONFIG_GPIO_LOONGSON_64BIT) += gpio-loongson-64bit.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o @@ -202,4 +203,4 @@ obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o -obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o +obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o \ No newline at end of file diff --git a/drivers/gpio/gpio-loongson-irq.c b/drivers/gpio/gpio-loongson-irq.c new file mode 100644 index 000000000..13932e297 --- /dev/null +++ b/drivers/gpio/gpio-loongson-irq.c @@ -0,0 +1,550 @@ +/* + * irq-> hirq-> pin-> vhwirq-> rirq(remapped irq) + *GPIO0 40 100 0 vb+0 rib+0 200 + *GPIO1 40 100 1 vb+1 rib+1 201 + *GPIO2 41 101 2 vb+2 rib+2 202 + *GPIO3 41 101 3 vb+3 rib+3 203 + * |HIRQ-PIN-MAP |VIRQ-IRQ-MAP + * |hirq-pin-group |IRQ DOMAIN & CHIPGENERIC + * |vhwirq-base irq-base + * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_IRQNUM_PER_CHIP 64 + +#define REG_GPIO_DIR(s) (s->regtable->dir) +#define REG_GPIO_OUT(s) (s->regtable->out) +#define REG_GPIO_IN(s) (s->regtable->in) +#define REG_GPIO_IRQ(s) (s->regtable->irq) +#define REG_GPIO_IRQPOL(s) (s->regtable->irqpol) +#define REG_GPIO_IRQEDG(s) (s->regtable->irqedg) +#define REG_GPIO_IRQCLR(s) (s->regtable->irqclr) +#define REG_GPIO_IRQSTA(s) (s->regtable->irqsta) +#define REG_GPIO_IRQDUL(s) (s->regtable->irqdul) + +#define lsirq_gpio_set_reg_dir(self, p, val) \ + writeb(val, self->reg + REG_GPIO_DIR(self) + p) +#define lsirq_gpio_set_reg_out(self, p, val) \ + writeb(val, self->reg + REG_GPIO_OUT(self) + p) +#define lsirq_gpio_get_reg_in(self, p) \ + readb(self->reg + REG_GPIO_IN(self) + p) +#define lsirq_gpio_set_reg_irq(self, p, val) \ + writeb(val, self->reg + REG_GPIO_IRQ(self) + p) +#define lsirq_gpio_get_reg_irq(self, p) \ + readb(self->reg + REG_GPIO_IRQ(self) + p) +#define lsirq_gpio_set_reg_irqpol(self, p, val) \ + writeb(val, self->reg + REG_GPIO_IRQPOL(self) + p) +#define lsirq_gpio_set_reg_irqedg(self, p, val) \ + writeb(val, self->reg + REG_GPIO_IRQEDG(self) + p) +#define lsirq_gpio_set_reg_irqclr(self, p, val) \ + writeb(val, self->reg + REG_GPIO_IRQCLR(self) + p) +#define lsirq_gpio_get_reg_irqsta(self, p) \ + readb(self->reg + REG_GPIO_IRQSTA(self) + p) +#define lsirq_gpio_set_reg_irqdul(self, p, val) \ + writeb(val, self->reg + REG_GPIO_IRQDUL(self) + p) + +typedef struct lsirq_gpio_regtable_s { + u32 dir; + u32 out; + u32 in; + u32 irq; + u32 irqpol; + u32 irqedg; + u32 irqclr; + u32 irqsta; + u32 irqdul; +} lsirq_gpio_regtable_t; + +typedef struct lsirq_gpio_irqpinmap_s { + int irq; + int hwirq; + int pin_start; + int pin_num; + void* parent; + struct list_head next; +} lsirq_gpio_irqpinmap_t; + +typedef struct lsirq_gpio_irqdesc_s { + char name[32]; + int rirq_base; + int vhwirq_base; + int nr_irqs; + // HIRQ-PIN MAP + struct list_head ipm; + // VIRQ-IRQ MAP + struct irq_domain* domain; + struct irq_chip* ic; +} lsirq_gpio_irqdesc_t; + +typedef struct lsirq_gpio_priv_s { + spinlock_t lock; + void __iomem* reg; + struct device* dev; + struct gpio_chip gc; + lsirq_gpio_irqdesc_t irqd; + const lsirq_gpio_regtable_t* regtable; +} lsirq_gpio_priv_t; + +static lsirq_gpio_regtable_t generic_reg_table = { + .dir = 0x800, + .out = 0x900, + .in = 0xa00, + .irq = 0xb00, + .irqpol = 0xc00, + .irqedg = 0xd00, + .irqclr = 0xe00, + .irqsta = 0xf00, + .irqdul = 0xf80, +}; + +static int __map_hwirq_pin(lsirq_gpio_irqpinmap_t* map, int hwirq, + lsirq_gpio_priv_t* priv) +{ + int pin; + + if (map->hwirq == hwirq) + { + for (pin = map->pin_start; + pin < map->pin_start + map->pin_num; + pin++) + { + // 2k300 must check irq enable + if (lsirq_gpio_get_reg_irq(priv, pin) == 1 && + lsirq_gpio_get_reg_irqsta(priv, pin) == 1) + return pin; + } + } + + return -1; +} + +static int __map_vhwirq_pin(lsirq_gpio_priv_t* priv, int vhwirq) +{ + return vhwirq - priv->irqd.vhwirq_base; +} + +static int __map_pin_vhwirq(lsirq_gpio_priv_t* priv, int pin) +{ + return pin + priv->irqd.vhwirq_base; +} + +static int __map_vhwirq_rirq(lsirq_gpio_priv_t* priv, int vhwirq) +{ + return irq_find_mapping(priv->irqd.domain, vhwirq); +} + +static void irqfhd_lsirq_gpio_handler(struct irq_desc *desc) +{ + lsirq_gpio_irqpinmap_t* map = irq_desc_get_handler_data(desc); + lsirq_gpio_priv_t *priv = map->parent; + struct irq_chip *chip = irq_desc_get_chip(desc); + int pin, vhwirq, rirq; + + chained_irq_enter(chip, desc); + + pin = __map_hwirq_pin(map, desc->irq_data.hwirq, priv); + if (pin < 0) + goto end; + lsirq_gpio_set_reg_irqclr(priv, pin, 1); + + vhwirq = __map_pin_vhwirq(priv, pin); + rirq = __map_vhwirq_rirq(priv, vhwirq); + generic_handle_irq(rirq); +end: + chained_irq_exit(chip, desc); +} + +static int mthd_lsirq_gpio_request(struct gpio_chip *gc, unsigned pin) +{ + if (pin >= gc->ngpio) + return -EINVAL; + else + return 0; +} + +static int mthd_lsirq_gpio_direction_input(struct gpio_chip *gc, unsigned pin) +{ + lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + lsirq_gpio_set_reg_dir(priv, pin, 1); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int mthd_lsirq_gpio_direction_output(struct gpio_chip *gc, + unsigned pin, int value) +{ + lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + lsirq_gpio_set_reg_out(priv, pin, value); + lsirq_gpio_set_reg_dir(priv, pin, 0); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int mthd_lsirq_gpio_get(struct gpio_chip *gc, unsigned pin) +{ + lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + + return lsirq_gpio_get_reg_in(priv, pin) & 1; +} + +static void mthd_lsirq_gpio_set(struct gpio_chip *gc, unsigned pin, int value) +{ + lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + lsirq_gpio_set_reg_out(priv, pin, value); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int mthd_lsirq_gpio_to_irq(struct gpio_chip *gc, unsigned pin) +{ + int vhwirq, rirq; + lsirq_gpio_priv_t *priv = gpiochip_get_data(gc); + + vhwirq = __map_pin_vhwirq(priv, (int)pin); + rirq = __map_vhwirq_rirq(priv, vhwirq); + + return rirq; +} + +static int mthd_lsirq_gpio_irqsettype(struct irq_data *d, u32 type) +{ + lsirq_gpio_priv_t *priv = irq_data_get_irq_chip_data(d); + int vhwirq = d->hwirq; + int pin = __map_vhwirq_pin(priv, vhwirq); + unsigned long flags; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + lsirq_gpio_set_reg_irq(priv, pin, 1); + lsirq_gpio_set_reg_irqdul(priv, pin, 0); + lsirq_gpio_set_reg_irqpol(priv, pin, 1); + lsirq_gpio_set_reg_irqedg(priv, pin, 1); + break; + case IRQ_TYPE_EDGE_FALLING: + lsirq_gpio_set_reg_irq(priv, pin, 1); + lsirq_gpio_set_reg_irqdul(priv, pin, 0); + lsirq_gpio_set_reg_irqpol(priv, pin, 0); + lsirq_gpio_set_reg_irqedg(priv, pin, 1); + break; + case IRQ_TYPE_EDGE_BOTH: + lsirq_gpio_set_reg_irq(priv, pin, 1); + lsirq_gpio_set_reg_irqdul(priv, pin, 1); + lsirq_gpio_set_reg_irqedg(priv, pin, 1); + break; + case IRQ_TYPE_LEVEL_LOW: + lsirq_gpio_set_reg_irq(priv, pin, 1); + lsirq_gpio_set_reg_irqdul(priv, pin, 0); + lsirq_gpio_set_reg_irqpol(priv, pin, 0); + lsirq_gpio_set_reg_irqedg(priv, pin, 0); + break; + case IRQ_TYPE_LEVEL_HIGH: + lsirq_gpio_set_reg_irq(priv, pin, 1); + lsirq_gpio_set_reg_irqdul(priv, pin, 0); + lsirq_gpio_set_reg_irqpol(priv, pin, 1); + lsirq_gpio_set_reg_irqedg(priv, pin, 0); + break; + case IRQ_TYPE_NONE: + lsirq_gpio_set_reg_irq(priv, pin, 0); + break; + default: + return -EINVAL; + } + spin_lock_irqsave(&priv->lock, flags); + lsirq_gpio_set_reg_dir(priv, pin, 1); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void mthd_lsirq_gpio_irqack(struct irq_data *d) +{ + +} + +static void mthd_lsirq_gpio_irqmask(struct irq_data *d) +{ + lsirq_gpio_priv_t *priv = irq_data_get_irq_chip_data(d); + int vhwirq = d->hwirq; + int pin = __map_vhwirq_pin(priv, vhwirq); + unsigned long flags; + + // interrupt must gpio input + spin_lock_irqsave(&priv->lock, flags); + lsirq_gpio_set_reg_dir(priv, pin, 1); + spin_unlock_irqrestore(&priv->lock, flags); + + lsirq_gpio_set_reg_irq(priv, pin, 0); +} + +static void mthd_lsirq_gpio_irqunmask(struct irq_data *d) +{ + lsirq_gpio_priv_t *priv = irq_data_get_irq_chip_data(d); + int vhwirq = d->hwirq; + int pin = __map_vhwirq_pin(priv, vhwirq); + + lsirq_gpio_set_reg_irq(priv, pin, 1); +} + +static int lsirq_gpio_gc_remove(lsirq_gpio_priv_t* priv) +{ + struct gpio_chip* gc = &priv->gc; + + gpiochip_remove(gc); + + return 0; +} + +static int lsirq_gpio_gc_setup(lsirq_gpio_priv_t* priv) +{ + struct gpio_chip* gc = &priv->gc; + struct device_node *np = priv->dev->of_node; + + of_property_read_u32(np, "ngpios", (u32 *)&gc->ngpio); + of_property_read_u32(np, "gpio_base", (u32 *)&gc->base); + gc->request = mthd_lsirq_gpio_request; + gc->direction_input = mthd_lsirq_gpio_direction_input; + gc->get = mthd_lsirq_gpio_get; + gc->direction_output = mthd_lsirq_gpio_direction_output; + gc->set = mthd_lsirq_gpio_set; + gc->to_irq = mthd_lsirq_gpio_to_irq; + gc->can_sleep = 0; + gc->fwnode = &np->fwnode; + gc->parent = priv->dev; + + if (devm_gpiochip_add_data(priv->dev, gc, priv)) + { + dev_err(priv->dev, "gpiochip setup fail\n"); + return -1; + } + + return 0; +} + +static int lsirqmap_fill(struct list_head* map, + struct device_node *np, void* parent) +{ + int i = 0; + int irq, hwirq, pin_start, pin_num, nr_irqs; + struct irq_desc *desc; + lsirq_gpio_irqpinmap_t* m; + + nr_irqs = 0; + + INIT_LIST_HEAD(map); + for (i = 0; i < MAX_IRQNUM_PER_CHIP; i++) + { + irq = of_irq_get(np, i); + if (irq < 0) + break; + + desc = irq_to_desc(irq); + if (!desc) + break; + hwirq = desc->irq_data.hwirq; + + if (of_property_read_u32_index(np, "loongson,irqpin-group", + i*2, &pin_start)) + break; + if (of_property_read_u32_index(np, "loongson,irqpin-group", + i*2+1, &pin_num)) + break; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + break; + + m->irq = irq; + m->hwirq = hwirq; + m->pin_start = pin_start; + m->pin_num = pin_num; + m->parent = parent; + + list_add_tail(&m->next, map); + + irq_set_chained_handler_and_data(irq, + irqfhd_lsirq_gpio_handler, m); + nr_irqs += pin_num; + } + return nr_irqs; +} + +static int lsirqmap_clear(struct list_head* map) +{ + lsirq_gpio_irqpinmap_t* m; + + while (!list_empty(map)) { + m = list_entry(map->next, lsirq_gpio_irqpinmap_t, next); + list_del(&m->next); + kfree(m); + } + return 0; +} + +static int mthd_irq_domain_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + lsirq_gpio_priv_t* priv = domain->host_data; + lsirq_gpio_irqdesc_t* irqd = &priv->irqd; + + irq_set_chip_data(irq, priv); + irq_set_chip_and_handler(irq, irqd->ic, handle_edge_irq); + irq_set_probe(irq); + + return 0; +} + + +static const struct irq_domain_ops domain_ops_gpic = { + .map = mthd_irq_domain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +static int lsirqd_setup(lsirq_gpio_priv_t* priv) +{ + lsirq_gpio_irqdesc_t* irqd = &priv->irqd; + struct device_node *np = priv->dev->of_node; + + of_property_read_u32(np, "loongson,vhwirq-base", (u32 *)&irqd->vhwirq_base); + snprintf(irqd->name, sizeof(irqd->name), "GPIC-%s", np->name); + + /* + * Setup HWIRQ-PIN-MAP + * */ + irqd->nr_irqs = lsirqmap_fill(&irqd->ipm, np, priv); + if (irqd->nr_irqs <= 0) + { + dev_err(priv->dev, "IRQ Pin Map fail\n"); + goto err; + } + /* + * Setup VIRQ-IRQ-MAP + * Use IRQ DOMAIN and chip generic + * */ + // find valid ib + irqd->rirq_base = devm_irq_alloc_descs(priv->dev, -1, + 0, irqd->nr_irqs, numa_node_id()); + if (irqd->rirq_base < 0) { + dev_err(priv->dev, "Alloc IRQ fail\n"); + goto clear_pin_map; + } + // set ic prop + irqd->ic = kzalloc(sizeof(*irqd->ic), GFP_KERNEL); + if (!irqd->ic) + { + dev_err(priv->dev, "IRQ chip alloc fail\n"); + goto free_irq_desc; + } + irqd->ic->name = irqd->name; + irqd->ic->irq_ack = mthd_lsirq_gpio_irqack; + irqd->ic->irq_mask = mthd_lsirq_gpio_irqmask; + irqd->ic->irq_unmask = mthd_lsirq_gpio_irqunmask; + irqd->ic->irq_set_type = mthd_lsirq_gpio_irqsettype; + irqd->ic->flags = IRQCHIP_MASK_ON_SUSPEND; + // map ib&vb + irqd->domain = irq_domain_add_legacy(np, + irqd->nr_irqs, irqd->rirq_base, irqd->vhwirq_base, + &domain_ops_gpic, priv); + if (!irqd->domain) { + dev_err(priv->dev, "IRQ domain add fail\n"); + goto free_ic; + } + dev_info(priv->dev, "register irqnum=%d, vhwirq-base=%d, rirq-base=%d\n", + irqd->nr_irqs, irqd->vhwirq_base, irqd->rirq_base); + return 0; + +free_ic: + kfree(irqd->ic); +free_irq_desc: + irq_free_descs(irqd->rirq_base, irqd->nr_irqs); +clear_pin_map: + lsirqmap_clear(&irqd->ipm); +err: + return -1; +} + +static const struct of_device_id drvids_lsirq_gpio[] = { + { .compatible = "loongson,ls2p-gpio", .data = &generic_reg_table }, + { .compatible = "loongson,ls2k0300-gpio", .data = &generic_reg_table }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, drvids_lsirq_gpio); + +static int drv_lsirq_gpio_probe(struct platform_device *pdev) +{ + lsirq_gpio_priv_t* priv; + const struct of_device_id *of_id = + of_match_device(drvids_lsirq_gpio, &pdev->dev); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->reg)) + goto ioremap_fail; + spin_lock_init(&priv->lock); + priv->dev = &pdev->dev; + priv->regtable = of_id->data; + + if (lsirq_gpio_gc_setup(priv)) + goto gc_fail; + + if (lsirqd_setup(priv)) + goto irqd_fail; + + platform_set_drvdata(pdev, priv); + + return 0; + +irqd_fail: + lsirq_gpio_gc_remove(priv); +gc_fail: + devm_iounmap(&pdev->dev, priv->reg); +ioremap_fail: + devm_kfree(&pdev->dev, priv); + return -1; +} + +static struct platform_driver lsirq_gpio_driver = { + .driver = { + .name = "gpio-lsirq", + .owner = THIS_MODULE, + .of_match_table = drvids_lsirq_gpio, + }, + .probe = drv_lsirq_gpio_probe, +}; + +static int __init lsirq_gpio_init(void) +{ + return platform_driver_register(&lsirq_gpio_driver); +} +subsys_initcall(lsirq_gpio_init); + +MODULE_AUTHOR("Yize Niu "); +MODULE_DESCRIPTION("Loongson GPIO With IRQ Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig index 9ed463a76..e9f375ffc 100644 --- a/drivers/gpu/drm/loongson/Kconfig +++ b/drivers/gpu/drm/loongson/Kconfig @@ -7,6 +7,8 @@ config DRM_LOONGSON select DRM_KMS_HELPER select DRM_TTM select DRM_TTM_HELPER + select DRM_BRIDGE_CONNECTOR + select DRM_DISPLAY_HELPER select I2C select I2C_ALGOBIT help diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd90..d6e709e19 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -7,6 +7,7 @@ loongson-y := \ lsdc_drv.o \ lsdc_gem.o \ lsdc_gfxpll.o \ + lsdc_gfxpll_2k1000.o \ lsdc_i2c.o \ lsdc_irq.o \ lsdc_output_7a1000.o \ diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 9986c8a2a..07eda74b4 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -25,6 +25,15 @@ static const struct lsdc_kms_funcs ls7a2000_kms_funcs = { .crtc_init = ls7a2000_crtc_init, }; +static const struct lsdc_kms_funcs ls2k0300_kms_funcs = { + .create_i2c = lsdc_create_i2c_chan, + .irq_handler = ls7a2000_dc_irq_handler, + .output_init = ls7a1000_output_init, + .cursor_plane_init = ls7a1000_cursor_plane_init, + .primary_plane_init = lsdc_primary_plane_init, + .crtc_init = ls7a1000_crtc_init, +}; + static const struct loongson_gfx_desc ls7a1000_gfx = { .dc = { .num_of_crtc = 2, @@ -35,7 +44,9 @@ static const struct loongson_gfx_desc ls7a1000_gfx = { .hw_cursor_w = 32, .hw_cursor_h = 32, .pitch_align = 256, + .hw_vblank_enable = true, .has_vblank_counter = false, + .has_dedicated_vram = true, .funcs = &ls7a1000_kms_funcs, }, .conf_reg_base = LS7A1000_CONF_REG_BASE, @@ -43,6 +54,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx = { .reg_offset = LS7A1000_PLL_GFX_REG, .reg_size = 8, }, + .gfxpll_funcs = &ls7a1000_gfx_pll_funcs, .pixpll = { [0] = { .reg_offset = LS7A1000_PIXPLL0_REG, @@ -53,6 +65,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx = { .reg_size = 8, }, }, + .pixpll_funcs = &ls2k0300_pixpll_funcs, .chip_id = CHIP_LS7A1000, .model = "LS7A1000 bridge chipset", }; @@ -67,7 +80,9 @@ static const struct loongson_gfx_desc ls7a2000_gfx = { .hw_cursor_w = 64, .hw_cursor_h = 64, .pitch_align = 64, + .hw_vblank_enable = true, .has_vblank_counter = true, + .has_dedicated_vram = true, .funcs = &ls7a2000_kms_funcs, }, .conf_reg_base = LS7A2000_CONF_REG_BASE, @@ -75,6 +90,7 @@ static const struct loongson_gfx_desc ls7a2000_gfx = { .reg_offset = LS7A2000_PLL_GFX_REG, .reg_size = 8, }, + .gfxpll_funcs = &ls7a2000_gfx_pll_funcs, .pixpll = { [0] = { .reg_offset = LS7A2000_PIXPLL0_REG, @@ -85,13 +101,51 @@ static const struct loongson_gfx_desc ls7a2000_gfx = { .reg_size = 8, }, }, + .pixpll_funcs = &ls2k0300_pixpll_funcs, .chip_id = CHIP_LS7A2000, .model = "LS7A2000 bridge chipset", }; +static const struct loongson_gfx_desc ls2k0300_gfx = { + .dc = { + .num_of_crtc = 1, + .max_pixel_clk = 200000, + .max_width = 4096, + .max_height = 4096, + .num_of_hw_cursor = 1, + .hw_cursor_w = 32, + .hw_cursor_h = 32, + .pitch_align = 1, // 不需要考虑对齐,对小屏幕不友好 + .hw_vblank_enable = false, // 强制不使用 hw_vblank + .has_vblank_counter = false, + .has_dedicated_vram = false, + .funcs = &ls2k0300_kms_funcs, + }, + .conf_reg_base = LS2K0300_CFG_REG_BASE, + .gfxpll = { + .reg_offset = LS2K0300_DDR_PLL_REG, + .reg_size = 16 + 16, + }, + .gfxpll_funcs = &ls2k1000_gfx_pll_funcs, + .pixpll = { + [0] = { + .reg_offset = LS2K0300_PIX_PLL0_REG, + .reg_size = 16, + }, + [1] = { + .reg_offset = LS2K0300_PIX_PLL1_REG, + .reg_size = 16, + }, + }, + .pixpll_funcs = &ls2k0300_pixpll_funcs, + .chip_id = CHIP_LS2K0300, + .model = "LS2K300 SoC", +}; + static const struct lsdc_desc *__chip_id_desc_table[] = { [CHIP_LS7A1000] = &ls7a1000_gfx.dc, [CHIP_LS7A2000] = &ls7a2000_gfx.dc, + [CHIP_LS2K0300] = &ls2k0300_gfx.dc, [CHIP_LS_LAST] = NULL, }; diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c index d2a51bd39..071302ba5 100644 --- a/drivers/gpu/drm/loongson/loongson_module.c +++ b/drivers/gpu/drm/loongson/loongson_module.c @@ -4,6 +4,7 @@ */ #include +#include #include