mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-25 12:37:11 +08:00
Update to 14fc3074cfe039
1. 添加带irq处理的gpio驱动 2. 龙芯2k系列添加mmc驱动支持 3. 添加看门狗驱动 4. dtb支持从command line中的board_name字段判断 5. 支持内存分布描述数据从固件传入的数据获取 6. 支持从bootcmdline获取ramdisk启动的地址和所用大小 7. 添加2k300的i2c总线驱动 8. 2k300使用新的spi 和 spi-io 驱动 9. CPU芯片温度传感器支持2k300的计算方式 10. 添加2k300的adc驱动 11. pwm: update default pwm freq to 50Mhz 12. loongson pwm 驱动支持dts使用clock-frequency形式声明频率 13. loongson pwm 确保disable时输出的电平为空闲电平 14. 2k300编译配置使能pwm leds功能 15. 2k300的gmac的phy-mode默认使用rgmii-id 16. 2k300先锋派设置YT8531的内部延迟 17. YT8531 phy 支持设置led的工作模式 18. LCD屏幕 支持cmdline的panel属性选择某一款板卡的dts配置,实现屏幕切换 19. 添加脚本用于快速选择板卡配置文件 20. 添加CAN驱动 21. 添加音频驱动 22. 优化OTG兼容性 Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
This commit is contained in:
@@ -177,3 +177,4 @@ sphinx_*/
|
||||
|
||||
# Rust analyzer configuration
|
||||
/rust-project.json
|
||||
*uImage
|
||||
|
||||
+12
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,897 @@
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/clock/loongson2k300-clock.h>
|
||||
#include <dt-bindings/usb/dwc2-loongson-fix.h>
|
||||
|
||||
/ {
|
||||
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 = <DWC2_LOONGSON_FIX_DELAY>;
|
||||
//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";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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>; //有数据时闪烁
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,228 @@
|
||||
#include "ls2k300_g2k03z0_core_v10.dtsi"
|
||||
#include "loongson_panel_param.dtsi"
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
|
||||
/ {
|
||||
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";
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
#include "ls2k300_g2k03z0_core_v10.dtsi"
|
||||
#include "loongson_panel_param.dtsi"
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
|
||||
/ {
|
||||
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";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
@@ -0,0 +1,290 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/input/linux-event-codes.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
#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";
|
||||
};
|
||||
@@ -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
|
||||
@@ -49,6 +49,12 @@
|
||||
#include <asm/time.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
#include <boot_param.h>
|
||||
|
||||
#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/////
|
||||
//////////////////////////////////////
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of_clk.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/cpu-features.h>
|
||||
#include <asm/loongarch.h>
|
||||
@@ -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
|
||||
|
||||
Executable
+50
@@ -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 <chip> [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 $@
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <loongson1.h>
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <loongson1.h>
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Michael <michael5hzg@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <loongson-2k.h>
|
||||
#include <dt-bindings/clock/loongson2k1000-clock.h>
|
||||
|
||||
#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);
|
||||
@@ -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 <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <loongson-2k300.h>
|
||||
#include <dt-bindings/clock/loongson2k300-clock.h>
|
||||
|
||||
#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);
|
||||
@@ -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 <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <loongson-2k.h>
|
||||
#include <dt-bindings/clock/loongson2k500-clock.h>
|
||||
|
||||
#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);
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
|
||||
*/
|
||||
|
||||
#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 */
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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 <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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");
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#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 <niuyize@loongson.cn>");
|
||||
MODULE_DESCRIPTION("Loongson GPIO With IRQ Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <video/nomodeset.h>
|
||||
|
||||
@@ -16,12 +17,14 @@ module_param_named(modeset, loongson_modeset, int, 0400);
|
||||
int loongson_vblank = 1;
|
||||
MODULE_PARM_DESC(vblank, "Disable/Enable hw vblank support");
|
||||
module_param_named(vblank, loongson_vblank, int, 0400);
|
||||
extern struct platform_driver lsdc_platform_driver;
|
||||
|
||||
static int __init loongson_module_init(void)
|
||||
{
|
||||
if (!loongson_modeset || video_firmware_drivers_only())
|
||||
return -ENODEV;
|
||||
|
||||
platform_driver_register(&lsdc_platform_driver);
|
||||
return pci_register_driver(&lsdc_pci_driver);
|
||||
}
|
||||
module_init(loongson_module_init);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vgaarb.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <drm/drm_aperture.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
@@ -16,6 +17,12 @@
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
#include "loongson_module.h"
|
||||
#include "lsdc_drv.h"
|
||||
@@ -57,6 +64,9 @@ static const struct drm_mode_config_funcs lsdc_mode_config_funcs = {
|
||||
|
||||
/* Display related */
|
||||
|
||||
static int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc);
|
||||
static int lsdc_common_probe(struct lsdc_device *ldev);
|
||||
|
||||
static int lsdc_modeset_init(struct lsdc_device *ldev,
|
||||
unsigned int num_crtc,
|
||||
const struct lsdc_kms_funcs *funcs,
|
||||
@@ -78,18 +88,31 @@ static int lsdc_modeset_init(struct lsdc_device *ldev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_crtc; i++) {
|
||||
struct i2c_adapter *ddc = NULL;
|
||||
|
||||
dispipe = &ldev->dispipe[i];
|
||||
if (dispipe->li2c)
|
||||
ddc = &dispipe->li2c->adapter;
|
||||
|
||||
ret = funcs->output_init(ddev, dispipe, ddc, i);
|
||||
if (ldev->has_ports_node) {
|
||||
drm_info(ddev, "Has OF graph support\n");
|
||||
ret = lsdc_attach_output(ldev, num_crtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ldev->num_output++;
|
||||
/*
|
||||
* 之前的处理是 OF扫描到 dc之后,就按照ACPI的做法处理
|
||||
* 这样的话就不会扫到edid(2k300 没i2c去读)
|
||||
*/
|
||||
if (!ldev->num_output) {
|
||||
for (i = 0; i < num_crtc; i++) {
|
||||
struct i2c_adapter *ddc = NULL;
|
||||
|
||||
dispipe = &ldev->dispipe[i];
|
||||
if (dispipe->li2c)
|
||||
ddc = &dispipe->li2c->adapter;
|
||||
|
||||
ret = funcs->output_init(ddev, dispipe, ddc, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ldev->num_output++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_crtc; i++) {
|
||||
@@ -155,8 +178,7 @@ static int lsdc_mode_config_init(struct drm_device *ddev,
|
||||
* the DC could access the on-board VRAM.
|
||||
*/
|
||||
static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
|
||||
struct pci_dev *pdev_dc,
|
||||
const struct lsdc_desc *descp)
|
||||
struct pci_dev *pdev_dc)
|
||||
{
|
||||
struct drm_device *ddev = &ldev->base;
|
||||
struct pci_dev *pdev_gpu;
|
||||
@@ -187,27 +209,78 @@ static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
|
||||
return (size > SZ_1M) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int lsdc_of_get_reserved_mem(struct lsdc_device *ldev)
|
||||
{
|
||||
struct drm_device *ddev = &ldev->base;
|
||||
unsigned long size = 0;
|
||||
struct device_node *node;
|
||||
struct resource r;
|
||||
int ret;
|
||||
|
||||
if (!ddev->dev->of_node)
|
||||
return -ENOENT;
|
||||
|
||||
node = of_parse_phandle(ddev->dev->of_node, "memory-region", 0);
|
||||
if (!node) {
|
||||
drm_err(ddev, "No memory-region property\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(node, 0, &r);
|
||||
of_node_put(node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
size = r.end - r.start + 1;
|
||||
|
||||
ldev->vram_base = r.start;
|
||||
ldev->vram_size = size;
|
||||
|
||||
drm_info(ddev, "Using of reserved mem: %lx@%pa\n", size, &r.start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lsdc_device *
|
||||
lsdc_create_device(struct pci_dev *pdev,
|
||||
lsdc_create_device(struct device *dev,
|
||||
const struct lsdc_desc *descp,
|
||||
const struct drm_driver *driver)
|
||||
const struct drm_driver *driver,
|
||||
struct pci_dev *pdev,
|
||||
struct platform_device *pfdev)
|
||||
{
|
||||
struct lsdc_device *ldev;
|
||||
struct drm_device *ddev;
|
||||
int ret;
|
||||
|
||||
ldev = devm_drm_dev_alloc(&pdev->dev, driver, struct lsdc_device, base);
|
||||
ldev = devm_drm_dev_alloc(dev, driver, struct lsdc_device, base);
|
||||
if (IS_ERR(ldev))
|
||||
return ldev;
|
||||
|
||||
ldev->dc = pdev;
|
||||
ldev->descp = descp;
|
||||
|
||||
// 先注册了 dev, lsdc_common_probe里面需要扫描 OF
|
||||
ldev->dev = dev;
|
||||
ddev = &ldev->base;
|
||||
|
||||
loongson_gfxpll_create(ddev, &ldev->gfxpll);
|
||||
|
||||
ret = lsdc_get_dedicated_vram(ldev, pdev, descp);
|
||||
if (descp->has_dedicated_vram)
|
||||
ret = lsdc_get_dedicated_vram(ldev, pdev);
|
||||
else {
|
||||
ret = lsdc_of_get_reserved_mem(ldev);
|
||||
if (ret) {
|
||||
dma_addr_t daddr = 0;
|
||||
void *vaddr;
|
||||
ldev->vram_size = 0x900000;
|
||||
vaddr = dma_alloc_wc(dev, ldev->vram_size, &daddr,
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
|
||||
ret = !vaddr;
|
||||
ldev->vram_base = daddr;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
drm_err(ddev, "Init VRAM failed: %d\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
@@ -230,12 +303,20 @@ lsdc_create_device(struct pci_dev *pdev,
|
||||
lsdc_gem_init(ddev);
|
||||
|
||||
/* Bar 0 of the DC device contains the MMIO register's base address */
|
||||
ldev->reg_base = pcim_iomap(pdev, 0, 0);
|
||||
if (pdev)
|
||||
ldev->reg_base = pcim_iomap(pdev, 0, 0);
|
||||
else {
|
||||
struct resource *res;
|
||||
res = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
|
||||
ldev->reg_base = devm_ioremap_resource(dev, res);
|
||||
}
|
||||
if (!ldev->reg_base)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
spin_lock_init(&ldev->reglock);
|
||||
|
||||
lsdc_common_probe(ldev);
|
||||
|
||||
ret = lsdc_mode_config_init(ddev, descp);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
@@ -281,7 +362,11 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
dev_info(&pdev->dev, "Found %s, revision: %u",
|
||||
to_loongson_gfx(descp)->model, pdev->revision);
|
||||
|
||||
ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
|
||||
if (!descp->hw_vblank_enable)
|
||||
loongson_vblank = 0;
|
||||
|
||||
// ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
|
||||
ldev = lsdc_create_device(&pdev->dev, descp, &lsdc_drm_driver, pdev, NULL);
|
||||
if (IS_ERR(ldev))
|
||||
return PTR_ERR(ldev);
|
||||
|
||||
@@ -460,3 +545,246 @@ MODULE_DEVICE_TABLE(pci, lsdc_pciid_list);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static const struct of_device_id lsdc_dt_ids[] = {
|
||||
{ .compatible = "loongson,ls2k0300-dc", .data = (void *)CHIP_LS2K0300, },
|
||||
{}
|
||||
};
|
||||
|
||||
static int lsdc_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct lsdc_desc *descp = NULL;
|
||||
const struct of_device_id *of_id;
|
||||
struct lsdc_device *ldev;
|
||||
struct drm_device *ddev;
|
||||
int ret;
|
||||
|
||||
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set dma mask\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_id = of_match_device(lsdc_dt_ids, dev);
|
||||
if (of_id && of_id->data)
|
||||
descp = lsdc_device_probe(NULL, (enum loongson_chip_id)of_id->data);
|
||||
|
||||
if (!descp) {
|
||||
dev_err(dev, "unknown dc chip core\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!descp->hw_vblank_enable)
|
||||
loongson_vblank = 0;
|
||||
|
||||
ldev = lsdc_create_device(&pdev->dev, descp, &lsdc_drm_driver, NULL, pdev);
|
||||
if (IS_ERR(ldev))
|
||||
return PTR_ERR(ldev);
|
||||
|
||||
ddev = &ldev->base;
|
||||
|
||||
platform_set_drvdata(pdev, ddev);
|
||||
drm_kms_helper_poll_init(ddev);
|
||||
|
||||
if (loongson_vblank) {
|
||||
int irq;
|
||||
ret = drm_vblank_init(ddev, descp->num_of_crtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(&pdev->dev, irq,
|
||||
descp->funcs->irq_handler,
|
||||
IRQF_SHARED /* | IRQF_TRIGGER_RISING */,
|
||||
dev_name(&pdev->dev), ddev);
|
||||
if (ret) {
|
||||
drm_err(ddev, "Failed to register interrupt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_info(ddev, "registered irq: %u\n", irq);
|
||||
}
|
||||
|
||||
ret = drm_dev_register(ddev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_fbdev_ttm_setup(ddev, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lsdc_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
struct platform_driver lsdc_platform_driver = {
|
||||
.probe = lsdc_platform_probe,
|
||||
.remove = lsdc_platform_remove,
|
||||
.driver = {
|
||||
.name = "lsdc",
|
||||
.pm = &lsdc_pm_ops,
|
||||
.of_match_table = of_match_ptr(lsdc_dt_ids),
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, lsdc_dt_ids);
|
||||
|
||||
/*
|
||||
* 从 5.10 移植而来
|
||||
* 这一套就包括了对 encoder connector (bridge)初始化
|
||||
* 最终作用于 lsdc_modeset_init 函数对完整链路的扫描(替代了原来的类ACPI的处理)
|
||||
* 暂时应该不支持 i2c 对 encoder connector 的沟通
|
||||
* 以后遇到实际情况再修改
|
||||
*/
|
||||
|
||||
static void lsdc_encoder_reset(struct drm_encoder *encoder)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs lsdc_encoder_funcs = {
|
||||
.reset = lsdc_encoder_reset,
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int lsdc_attach_bridges(struct lsdc_device *ldev,
|
||||
struct device_node *ports,
|
||||
unsigned int i)
|
||||
{
|
||||
struct lsdc_display_pipe * const dispipe = &ldev->dispipe[i];
|
||||
struct drm_device *ddev = &ldev->base;
|
||||
struct drm_bridge *bridge = NULL;
|
||||
struct drm_panel *panel = NULL;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct lsdc_output *output;
|
||||
int ret;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(ports, i, 0, &panel, &bridge);
|
||||
|
||||
if (panel) {
|
||||
bridge = devm_drm_panel_bridge_add_typed(ddev->dev, panel, DRM_MODE_CONNECTOR_DPI);
|
||||
drm_info(ddev, "output-%u is a DPI panel\n", i);
|
||||
}
|
||||
|
||||
if (!bridge)
|
||||
return ret;
|
||||
|
||||
output = devm_kzalloc(ddev->dev, sizeof(*output), GFP_KERNEL);
|
||||
if (!output)
|
||||
return -ENOMEM;
|
||||
|
||||
encoder = &output->encoder;
|
||||
|
||||
ret = drm_encoder_init(ddev, encoder, &lsdc_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DPI, "encoder-%u", i);
|
||||
|
||||
if (ret) {
|
||||
drm_err(ddev, "Failed to init encoder: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = BIT(i);
|
||||
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
|
||||
//ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
connector = drm_bridge_connector_init(ddev, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
drm_err(ddev, "Unable to init connector\n");
|
||||
return PTR_ERR(connector);
|
||||
}
|
||||
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
}
|
||||
drm_info(ddev, "bridge-%u attached to %s\n", i, encoder->name);
|
||||
|
||||
dispipe->output = output[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc)
|
||||
{
|
||||
struct drm_device *ddev = &ldev->base;
|
||||
struct device_node *ports;
|
||||
struct lsdc_display_pipe *disp;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ldev->num_output = 0;
|
||||
|
||||
ports = of_get_child_by_name(ldev->dev->of_node, "ports");
|
||||
|
||||
for (i = 0; i < num_crtc; i++) {
|
||||
struct drm_bridge *b;
|
||||
struct drm_panel *p;
|
||||
|
||||
disp = &ldev->dispipe[i];
|
||||
disp->available = false;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(ports, i, 0, &p, &b);
|
||||
if (ret) {
|
||||
if (ret == -ENODEV) {
|
||||
drm_dbg(ddev, "No active panel or bridge for port%u\n", i);
|
||||
disp->available = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == -EPROBE_DEFER)
|
||||
drm_dbg(ddev, "Bridge for port%d is defer probed\n", i);
|
||||
|
||||
goto RET;
|
||||
}
|
||||
|
||||
disp->available = true;
|
||||
ldev->num_output++;
|
||||
}
|
||||
|
||||
if (ldev->num_output == 0) {
|
||||
drm_err(ddev, "No valid output, abort\n");
|
||||
ret = -ENODEV;
|
||||
goto RET;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_crtc; i++) {
|
||||
disp = &ldev->dispipe[i];
|
||||
if (disp->available) {
|
||||
ret = lsdc_attach_bridges(ldev, ports, i);
|
||||
if (ret)
|
||||
goto RET;
|
||||
} else {
|
||||
drm_info(ddev, "output-%u is not available\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
drm_info(ddev, "number of outputs: %u\n", ldev->num_output);
|
||||
RET:
|
||||
of_node_put(ports);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lsdc_of_probe(struct lsdc_device *ldev, struct device_node *np)
|
||||
{
|
||||
struct device_node *ports;
|
||||
|
||||
if (!np) {
|
||||
ldev->has_ports_node = false;
|
||||
dev_info(ldev->dev, "don't has DT support\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ports = of_get_child_by_name(np, "ports");
|
||||
ldev->has_ports_node = ports ? true : false;
|
||||
of_node_put(ports);
|
||||
}
|
||||
|
||||
static int lsdc_common_probe(struct lsdc_device *ldev)
|
||||
{
|
||||
lsdc_of_probe(ldev, ldev->dev->of_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
enum loongson_chip_id {
|
||||
CHIP_LS7A1000 = 0,
|
||||
CHIP_LS7A2000 = 1,
|
||||
CHIP_LS2K0300 = 2,
|
||||
CHIP_LS_LAST,
|
||||
};
|
||||
|
||||
@@ -60,7 +61,9 @@ struct lsdc_desc {
|
||||
u32 hw_cursor_w;
|
||||
u32 hw_cursor_h;
|
||||
u32 pitch_align; /* CRTC DMA alignment constraint */
|
||||
bool hw_vblank_enable;
|
||||
bool has_vblank_counter; /* 32 bit hw vsync counter */
|
||||
bool has_dedicated_vram;
|
||||
|
||||
/* device dependent ops, dc side */
|
||||
const struct lsdc_kms_funcs *funcs;
|
||||
@@ -78,12 +81,14 @@ struct loongson_gfx_desc {
|
||||
u32 reg_offset;
|
||||
u32 reg_size;
|
||||
} gfxpll;
|
||||
const struct loongson_gfxpll_funcs *gfxpll_funcs;
|
||||
|
||||
/* Pixel PLL, per display pipe */
|
||||
struct {
|
||||
u32 reg_offset;
|
||||
u32 reg_size;
|
||||
} pixpll[LSDC_NUM_CRTC];
|
||||
const struct lsdc_pixpll_funcs *pixpll_funcs;
|
||||
|
||||
enum loongson_chip_id chip_id;
|
||||
char model[64];
|
||||
@@ -191,6 +196,8 @@ struct lsdc_display_pipe {
|
||||
struct lsdc_output output;
|
||||
struct lsdc_i2c *li2c;
|
||||
unsigned int index;
|
||||
|
||||
bool available;
|
||||
};
|
||||
|
||||
static inline struct lsdc_display_pipe *
|
||||
@@ -263,6 +270,7 @@ struct lsdc_gem {
|
||||
};
|
||||
|
||||
struct lsdc_device {
|
||||
struct device *dev;
|
||||
struct drm_device base;
|
||||
struct ttm_device bdev;
|
||||
|
||||
@@ -295,6 +303,10 @@ struct lsdc_device {
|
||||
|
||||
/* @num_output: count the number of active display pipe */
|
||||
unsigned int num_output;
|
||||
|
||||
|
||||
/* @has_ports_node: true if there are OF graph in the DT */
|
||||
bool has_ports_node;
|
||||
};
|
||||
|
||||
static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev)
|
||||
|
||||
@@ -163,7 +163,14 @@ static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
|
||||
const struct loongson_gfxpll_funcs ls7a1000_gfx_pll_funcs = {
|
||||
.init = loongson_gfxpll_init,
|
||||
.update = loongson_gfxpll_update,
|
||||
.get_rates = loongson_gfxpll_get_rates,
|
||||
.print = loongson_gfxpll_print,
|
||||
};
|
||||
|
||||
const struct loongson_gfxpll_funcs ls7a2000_gfx_pll_funcs = {
|
||||
.init = loongson_gfxpll_init,
|
||||
.update = loongson_gfxpll_update,
|
||||
.get_rates = loongson_gfxpll_get_rates,
|
||||
@@ -185,7 +192,7 @@ int loongson_gfxpll_create(struct drm_device *ddev,
|
||||
this->ddev = ddev;
|
||||
this->reg_size = gfx->gfxpll.reg_size;
|
||||
this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
|
||||
this->funcs = &lsdc_gmc_gpu_funcs;
|
||||
this->funcs = gfx->gfxpll_funcs;
|
||||
|
||||
ret = this->funcs->init(this);
|
||||
if (unlikely(ret)) {
|
||||
|
||||
@@ -49,4 +49,9 @@ struct loongson_gfxpll {
|
||||
int loongson_gfxpll_create(struct drm_device *ddev,
|
||||
struct loongson_gfxpll **ppout);
|
||||
|
||||
extern const struct loongson_gfxpll_funcs ls2k0300_gfx_pll_funcs;
|
||||
extern const struct loongson_gfxpll_funcs ls2k1000_gfx_pll_funcs;
|
||||
extern const struct loongson_gfxpll_funcs ls7a1000_gfx_pll_funcs;
|
||||
extern const struct loongson_gfxpll_funcs ls7a2000_gfx_pll_funcs;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2023 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "lsdc_drv.h"
|
||||
|
||||
union ls2k1000_ddr_pll_reg_bitmap {
|
||||
struct ls2k1000_ddr_pll_bitmap {
|
||||
/* Byte 0 ~ Byte 3 */
|
||||
unsigned bypass : 1; /* 0: Bypass ddr pll entirely */
|
||||
unsigned oe_gpu : 1; /* 1: GPU output clk enable */
|
||||
unsigned write_en : 1; /* 2: Allow software update */
|
||||
unsigned _reserved_1_ : 23; /* 3 : 25 Don't care */
|
||||
unsigned div_ref : 6; /* 31 : 26 Input clk divider */
|
||||
/* Byte 4 ~ Byte 7 */
|
||||
unsigned loopc : 10; /* 41 : 32 Clock multiplier */
|
||||
unsigned _reserved_2_ : 22; /* 42 : 63 Useless */
|
||||
|
||||
/* Byte 8 ~ Byte 15 */
|
||||
unsigned div_out_ddr : 6; /* 5 : 0 DDR output divider */
|
||||
unsigned _reserved_3_ : 16; /* 21 : 6 */
|
||||
unsigned div_out_gpu : 6; /* 27 : 22 GPU output divider */
|
||||
unsigned _reserved_4_ : 16; /* 43 : 28 */
|
||||
unsigned div_out_hda : 7; /* 50 : 44 HDA output divider */
|
||||
unsigned _reserved_5_ : 13; /* 63 : 51 */
|
||||
} bitmap;
|
||||
u32 w[4];
|
||||
u64 d[2];
|
||||
};
|
||||
|
||||
union ls2k1000_dc_pll_reg_bitmap {
|
||||
struct ls2k1000_dc_pll_bitmap {
|
||||
/* Byte 0 ~ Byte 3 */
|
||||
unsigned _reserved_1_ : 26; /* 0 : 25 Don't care */
|
||||
unsigned div_ref : 6; /* 31 : 26 Input clk divider */
|
||||
/* Byte 4 ~ Byte 7 */
|
||||
unsigned loopc : 10; /* 41 : 32 Clock multiplier */
|
||||
unsigned _reserved_2_ : 22; /* 42 : 63 Useless */
|
||||
|
||||
/* Byte 8 ~ Byte 15 */
|
||||
unsigned div_out_dc : 6; /* 5 : 0 DC output divider */
|
||||
unsigned _reserved_3_ : 16; /* 21 : 6 */
|
||||
unsigned div_out_gmac : 6; /* 27 : 22 GMAC output divider */
|
||||
unsigned _reserved_4_ : 4; /* 31 : 28 */
|
||||
unsigned _reserved_5_ : 32; /* 63 : 32 */
|
||||
} bitmap;
|
||||
u32 w[4];
|
||||
u64 d[2];
|
||||
};
|
||||
|
||||
static int ls2k1000_gfxpll_init(struct loongson_gfxpll * const this)
|
||||
{
|
||||
struct drm_printer printer = drm_info_printer(this->ddev->dev);
|
||||
|
||||
this->mmio = ioremap(this->reg_base, this->reg_size);
|
||||
if (IS_ERR_OR_NULL(this->mmio))
|
||||
return -ENOMEM;
|
||||
|
||||
this->funcs->print(this, &printer, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __rreg_u128(void __iomem *mmio, u32 w[], u64 d[])
|
||||
{
|
||||
#if defined(CONFIG_64BIT)
|
||||
d[0] = readq(mmio);
|
||||
d[1] = readq(mmio + 8);
|
||||
#else
|
||||
w[0] = readl(mmio);
|
||||
w[1] = readl(mmio + 4);
|
||||
w[2] = readl(mmio + 8);
|
||||
w[3] = readl(mmio + 12);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ls2k1000_gfxpll_get_rates(struct loongson_gfxpll * const this,
|
||||
unsigned int *dc,
|
||||
unsigned int *ddr,
|
||||
unsigned int *gpu)
|
||||
{
|
||||
unsigned int ref_clock_mhz = LSDC_PLL_REF_CLK_KHZ / 1000;
|
||||
union ls2k1000_ddr_pll_reg_bitmap ddr_pll_reg_bitmap;
|
||||
union ls2k1000_dc_pll_reg_bitmap dc_pll_reg_bitmap;
|
||||
unsigned int div_ref;
|
||||
unsigned int loopc;
|
||||
unsigned int div_out_dc;
|
||||
unsigned int div_out_ddr;
|
||||
unsigned int div_out_gpu;
|
||||
unsigned int dc_mhz;
|
||||
unsigned int ddr_mhz;
|
||||
unsigned int gpu_mhz;
|
||||
|
||||
__rreg_u128(this->mmio, ddr_pll_reg_bitmap.w, ddr_pll_reg_bitmap.d);
|
||||
div_ref = ddr_pll_reg_bitmap.bitmap.div_ref;
|
||||
loopc = ddr_pll_reg_bitmap.bitmap.loopc;
|
||||
div_out_ddr = ddr_pll_reg_bitmap.bitmap.div_out_ddr;
|
||||
div_out_gpu = ddr_pll_reg_bitmap.bitmap.div_out_gpu;
|
||||
ddr_mhz = ref_clock_mhz / div_ref * loopc / div_out_ddr;
|
||||
gpu_mhz = ref_clock_mhz / div_ref * loopc / div_out_gpu;
|
||||
|
||||
__rreg_u128(this->mmio + 16, dc_pll_reg_bitmap.w, dc_pll_reg_bitmap.d);
|
||||
div_out_dc = dc_pll_reg_bitmap.bitmap.div_out_dc;
|
||||
div_ref = dc_pll_reg_bitmap.bitmap.div_ref;
|
||||
loopc = dc_pll_reg_bitmap.bitmap.loopc;
|
||||
dc_mhz = ref_clock_mhz / div_ref * loopc / div_out_dc;
|
||||
|
||||
if (dc)
|
||||
*dc = dc_mhz;
|
||||
|
||||
if (ddr)
|
||||
*ddr = ddr_mhz;
|
||||
|
||||
if (gpu)
|
||||
*gpu = gpu_mhz;
|
||||
}
|
||||
|
||||
static void ls2k1000_gfxpll_print(struct loongson_gfxpll * const this,
|
||||
struct drm_printer *p,
|
||||
bool verbose)
|
||||
{
|
||||
unsigned int dc, ddr, gpu;
|
||||
|
||||
this->funcs->get_rates(this, &dc, &ddr, &gpu);
|
||||
|
||||
drm_printf(p, "dc: %uMHz, ddr: %uMHz, gpu: %uMHz\n", dc, ddr, gpu);
|
||||
}
|
||||
|
||||
const struct loongson_gfxpll_funcs ls2k1000_gfx_pll_funcs = {
|
||||
.init = ls2k1000_gfxpll_init,
|
||||
.get_rates = ls2k1000_gfxpll_get_rates,
|
||||
.print = ls2k1000_gfxpll_print,
|
||||
};
|
||||
@@ -41,6 +41,26 @@ union lsdc_pixpll_reg_bitmap {
|
||||
u64 d;
|
||||
};
|
||||
|
||||
/* u32 */
|
||||
union ls2k0300_pixpll_reg_bitmap {
|
||||
struct ls2k0300_pixpll_bitmap {
|
||||
unsigned sel_out : 1;
|
||||
unsigned reserved_1 : 2;
|
||||
unsigned sw_adj_en : 1; /* allow software adjust */
|
||||
unsigned bypass : 1; /* bypass L1 PLL */
|
||||
unsigned powerdown : 1; /* write 1 to powerdown the PLL */
|
||||
unsigned reserved_2 : 1;
|
||||
unsigned locked : 1; /* 7 Is L1 PLL locked, read only */
|
||||
unsigned div_ref : 7; /* 8:14 ref clock divider */
|
||||
unsigned loopc : 9; /* 15:23 Clock Multiplier */
|
||||
unsigned div_out : 7; /* 24:30 output clock divider */
|
||||
unsigned reserved_4 : 1; /* 31 */
|
||||
} bitmap;
|
||||
|
||||
u32 w[4];
|
||||
u64 d[2];
|
||||
};
|
||||
|
||||
struct clk_to_pixpll_parms_lookup_t {
|
||||
unsigned int clock; /* kHz */
|
||||
|
||||
@@ -452,11 +472,11 @@ static void lsdc_pixpll_print(struct lsdc_pixpll * const this,
|
||||
}
|
||||
|
||||
/*
|
||||
* LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same,
|
||||
* LS7A1000, LS7A2000 and LS2K2000's pixel PLL register layout is same,
|
||||
* we take this as default, create a new instance if a different model is
|
||||
* introduced.
|
||||
*/
|
||||
static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
|
||||
const struct lsdc_pixpll_funcs ls7a1000_pixpll_funcs = {
|
||||
.setup = lsdc_pixel_pll_setup,
|
||||
.compute = lsdc_pixel_pll_compute,
|
||||
.update = lsdc_pixpll_update,
|
||||
@@ -464,6 +484,84 @@ static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
|
||||
.print = lsdc_pixpll_print,
|
||||
};
|
||||
|
||||
const struct lsdc_pixpll_funcs ls7a2000_pixpll_funcs = {
|
||||
.setup = lsdc_pixel_pll_setup,
|
||||
.compute = lsdc_pixel_pll_compute,
|
||||
.update = lsdc_pixpll_update,
|
||||
.get_rate = lsdc_pixpll_get_freq,
|
||||
.print = lsdc_pixpll_print,
|
||||
};
|
||||
|
||||
static int ls2k0300_pixpll_param_update(struct lsdc_pixpll * const this,
|
||||
struct lsdc_pixpll_parms const *pin)
|
||||
{
|
||||
void __iomem *reg = this->mmio;
|
||||
unsigned int counter = 0;
|
||||
unsigned long val;
|
||||
bool locked;
|
||||
|
||||
/* set sel_pll_out0 0 */
|
||||
val = readl(reg);
|
||||
val &= ~(1UL << 0);
|
||||
writel(val, reg);
|
||||
|
||||
/* bypass */
|
||||
val |= (1UL << 4);
|
||||
|
||||
/* allow software setting the PLL */
|
||||
val |= (1UL << 3);
|
||||
writel(val, reg);
|
||||
|
||||
/* pll powerdown */
|
||||
val = readl(reg);
|
||||
val |= (1UL << 5);
|
||||
writel(val, reg);
|
||||
|
||||
val = (pin->div_out << 24) |
|
||||
(pin->loopc << 15) |
|
||||
(pin->div_ref << 8);
|
||||
|
||||
writel(val, reg);
|
||||
/* unbypass */
|
||||
val &= ~(1UL << 4);
|
||||
/* power up */
|
||||
val &= ~(1UL << 5);
|
||||
|
||||
writel(val, reg);
|
||||
|
||||
/* wait pll setup and locked */
|
||||
do {
|
||||
val = readl(reg);
|
||||
locked = val & 0x80;
|
||||
counter++;
|
||||
} while (locked == false);
|
||||
|
||||
DRM_DEBUG_DRIVER("%u loop waited\n", counter);
|
||||
/* select PIX0 */
|
||||
writel((val | 1), reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
ls2k0300_pixpll_get_clock_rate(struct lsdc_pixpll * const this)
|
||||
{
|
||||
struct lsdc_pixpll_parms *ppar = this->priv;
|
||||
union ls2k0300_pixpll_reg_bitmap reg_bitmap;
|
||||
struct ls2k0300_pixpll_bitmap *obj = ®_bitmap.bitmap;
|
||||
|
||||
reg_bitmap.w[0] = readl(this->mmio);
|
||||
return ppar->ref_clock / obj->div_ref * obj->loopc / obj->div_out;
|
||||
}
|
||||
|
||||
const struct lsdc_pixpll_funcs ls2k0300_pixpll_funcs = {
|
||||
.setup = lsdc_pixel_pll_setup,
|
||||
.compute = lsdc_pixel_pll_compute,
|
||||
.update = ls2k0300_pixpll_param_update,
|
||||
.get_rate = ls2k0300_pixpll_get_clock_rate,
|
||||
.print = lsdc_pixpll_print,
|
||||
};
|
||||
|
||||
/* pixel pll initialization */
|
||||
|
||||
int lsdc_pixpll_init(struct lsdc_pixpll * const this,
|
||||
@@ -477,7 +575,7 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const this,
|
||||
this->ddev = ddev;
|
||||
this->reg_size = 8;
|
||||
this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset;
|
||||
this->funcs = &__pixpll_default_funcs;
|
||||
this->funcs = gfx->pixpll_funcs;
|
||||
|
||||
return this->funcs->setup(this);
|
||||
}
|
||||
|
||||
@@ -83,4 +83,9 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const this,
|
||||
struct drm_device *ddev,
|
||||
unsigned int index);
|
||||
|
||||
extern const struct lsdc_pixpll_funcs ls2k0300_pixpll_funcs;
|
||||
extern const struct lsdc_pixpll_funcs ls2k1000_pixpll_funcs;
|
||||
extern const struct lsdc_pixpll_funcs ls7a1000_pixpll_funcs;
|
||||
extern const struct lsdc_pixpll_funcs ls7a2000_pixpll_funcs;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
/* For LSDC_CRTCx_CFG_REG */
|
||||
#define CFG_PIX_FMT_MASK GENMASK(2, 0)
|
||||
|
||||
/* LS2K0300 */
|
||||
#define LS2K0300_DDR_PLL_REG 0x0408
|
||||
#define LS2K0300_PIX_PLL0_REG 0x0410
|
||||
#define LS2K0300_PIX_PLL1_REG 0x0414
|
||||
#define LS2K0300_CFG_REG_BASE 0x16000000
|
||||
|
||||
enum lsdc_pixel_format {
|
||||
LSDC_PF_NONE = 0,
|
||||
LSDC_PF_XRGB444 = 1, /* [12 bits] */
|
||||
|
||||
@@ -123,8 +123,62 @@ static const struct drm_panel_funcs panel_lvds_funcs = {
|
||||
.get_orientation = panel_lvds_get_orientation,
|
||||
};
|
||||
|
||||
static char cmdline_panel_buf[128];
|
||||
extern char* saved_command_line;
|
||||
|
||||
static int get_panel_by_cmdline(void)
|
||||
{
|
||||
char* cmdline_part;
|
||||
cmdline_part = strstr(saved_command_line, "panel=");
|
||||
if (cmdline_part) {
|
||||
memset(cmdline_panel_buf, 0, 128);
|
||||
sscanf(cmdline_part, "panel=%s", cmdline_panel_buf);
|
||||
cmdline_panel_buf[127] = 0;
|
||||
}
|
||||
|
||||
if (!strcmp(cmdline_panel_buf, "default"))
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmpare_panel_name_cmdline(struct device_node *panel)
|
||||
{
|
||||
const char* label = NULL;
|
||||
of_property_read_string(panel, "label", &label);
|
||||
if (!label)
|
||||
return -EINVAL;
|
||||
return strcmp(label, cmdline_panel_buf);
|
||||
}
|
||||
|
||||
static struct device_node* panel_lvds_cmdline_match_dt(void)
|
||||
{
|
||||
struct device_node *panel_set_node;
|
||||
struct device_node *child;
|
||||
panel_set_node = of_find_node_by_name(NULL, "panel_param_set");
|
||||
|
||||
if (!panel_set_node)
|
||||
return NULL;
|
||||
|
||||
child = NULL;
|
||||
do {
|
||||
child = of_get_next_child(panel_set_node, child);
|
||||
|
||||
if (!child)
|
||||
break;
|
||||
|
||||
if (!cmpare_panel_name_cmdline(child)) {
|
||||
pr_info("cmdline panel property(%s) select ofnode is %s\n", cmdline_panel_buf, child->full_name);
|
||||
return child;
|
||||
}
|
||||
} while(child);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int panel_lvds_parse_dt(struct panel_lvds *lvds)
|
||||
{
|
||||
struct device_node *panel_by_cmdline = NULL;
|
||||
struct device_node *np = lvds->dev->of_node;
|
||||
int ret;
|
||||
|
||||
@@ -134,6 +188,14 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
|
||||
return ret;
|
||||
}
|
||||
|
||||
cmdline_panel_buf[0] = 0;
|
||||
ret = get_panel_by_cmdline();
|
||||
if (!ret)
|
||||
panel_by_cmdline = panel_lvds_cmdline_match_dt();
|
||||
|
||||
if (panel_by_cmdline)
|
||||
np = panel_by_cmdline;
|
||||
|
||||
ret = of_get_drm_panel_display_mode(np, &lvds->dmode, &lvds->bus_flags);
|
||||
if (ret < 0) {
|
||||
dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",
|
||||
|
||||
@@ -822,6 +822,13 @@ config I2C_LS2X
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-ls2x.
|
||||
|
||||
config I2C_LSFS
|
||||
tristate "Loongson fast speed I2C adapter"
|
||||
depends on MACH_LOONGSON64
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
I2C interface on the Loongson's LS2K300.
|
||||
|
||||
config I2C_MLXBF
|
||||
tristate "Mellanox BlueField I2C controller"
|
||||
depends on MELLANOX_PLATFORM && ARM64
|
||||
|
||||
@@ -80,6 +80,7 @@ obj-$(CONFIG_I2C_KEBA) += i2c-keba.o
|
||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
|
||||
obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
|
||||
obj-$(CONFIG_I2C_LSFS) += i2c-lsfs.o
|
||||
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
|
||||
obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
|
||||
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* LOONGSON fast speed I2C 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 <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
/* Loongson I2C offset registers */
|
||||
#define I2C_CR1 0x00
|
||||
#define I2C_CR2 0x04
|
||||
#define I2C_DR 0x10
|
||||
#define I2C_SR1 0x14
|
||||
#define I2C_SR2 0x18
|
||||
#define I2C_CCR 0x1C
|
||||
#define I2C_TRISE 0x20
|
||||
#define I2C_FLTR 0x24
|
||||
|
||||
/* Loongson I2C control 1*/
|
||||
#define I2C_CR1_POS BIT(11)
|
||||
#define I2C_CR1_ACK BIT(10)
|
||||
#define I2C_CR1_STOP BIT(9)
|
||||
#define I2C_CR1_START BIT(8)
|
||||
#define I2C_CR1_PE BIT(0)
|
||||
|
||||
/* Loongson I2C control 2 */
|
||||
#define I2C_CR2_FREQ_MASK GENMASK(5, 0)
|
||||
#define I2C_CR2_FREQ(n) ((n) & I2C_CR2_FREQ_MASK)
|
||||
#define I2C_CR2_ITBUFEN BIT(10)
|
||||
#define I2C_CR2_ITEVTEN BIT(9)
|
||||
#define I2C_CR2_ITERREN BIT(8)
|
||||
#define I2C_CR2_IRQ_MASK (I2C_CR2_ITBUFEN | \
|
||||
I2C_CR2_ITEVTEN | \
|
||||
I2C_CR2_ITERREN)
|
||||
|
||||
/* Loongson I2C Status 1 */
|
||||
#define I2C_SR1_AF BIT(10)
|
||||
#define I2C_SR1_ARLO BIT(9)
|
||||
#define I2C_SR1_BERR BIT(8)
|
||||
#define I2C_SR1_TXE BIT(7)
|
||||
#define I2C_SR1_RXNE BIT(6)
|
||||
#define I2C_SR1_BTF BIT(2)
|
||||
#define I2C_SR1_ADDR BIT(1)
|
||||
#define I2C_SR1_SB BIT(0)
|
||||
#define I2C_SR1_ITEVTEN_MASK (I2C_SR1_BTF | \
|
||||
I2C_SR1_ADDR | \
|
||||
I2C_SR1_SB)
|
||||
#define I2C_SR1_ITBUFEN_MASK (I2C_SR1_TXE | \
|
||||
I2C_SR1_RXNE)
|
||||
#define I2C_SR1_ITERREN_MASK (I2C_SR1_AF | \
|
||||
I2C_SR1_ARLO | \
|
||||
I2C_SR1_BERR)
|
||||
|
||||
/* Loongson I2C Status 2 */
|
||||
#define I2C_SR2_BUSY BIT(1)
|
||||
|
||||
/* Loongson I2C Control Clock */
|
||||
#define I2C_CCR_FS BIT(15)
|
||||
#define I2C_CCR_DUTY BIT(14)
|
||||
|
||||
enum ls_i2c_speed {
|
||||
LS_I2C_SPEED_STANDARD, /* 100 kHz */
|
||||
LS_I2C_SPEED_FAST, /* 400 kHz */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct priv_msg - client specific data
|
||||
* @addr: 8-bit slave addr, including r/w bit
|
||||
* @count: number of bytes to be transferred
|
||||
* @buf: data buffer
|
||||
* @stop: last I2C msg to be sent, i.e. STOP to be generated
|
||||
* @result: result of the transfer
|
||||
*/
|
||||
struct priv_msg {
|
||||
u8 addr;
|
||||
u32 count;
|
||||
u8 *buf;
|
||||
bool stop;
|
||||
int result;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ls_i2c_dev - private data of the controller
|
||||
* @adap: I2C adapter for this controller
|
||||
* @dev: device for this controller
|
||||
* @base: virtual memory area
|
||||
* @complete: completion of I2C message
|
||||
* @speed: Standard or Fast are supported
|
||||
* @msg: I2C transfer information
|
||||
*/
|
||||
struct ls_i2c_dev {
|
||||
struct i2c_adapter adap;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct completion complete;
|
||||
int speed;
|
||||
struct priv_msg msg;
|
||||
};
|
||||
|
||||
static inline void i2c_set_bits(void __iomem *reg, u32 mask)
|
||||
{
|
||||
writel(readl(reg) | mask, reg);
|
||||
}
|
||||
|
||||
static inline void i2c_clr_bits(void __iomem *reg, u32 mask)
|
||||
{
|
||||
writel(readl(reg) & ~mask, reg);
|
||||
}
|
||||
|
||||
static void ls_i2c_disable_irq(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
void __iomem *reg = i2c_dev->base + I2C_CR2;
|
||||
|
||||
i2c_clr_bits(reg, I2C_CR2_IRQ_MASK);
|
||||
}
|
||||
|
||||
#define INPUT_REF_CLK 120000000
|
||||
|
||||
static int ls_i2c_hw_config(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 val;
|
||||
u32 ccr = 0;
|
||||
|
||||
/* reference clock determination the cnfigure val(0x3f) */
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR2, 0x3f);
|
||||
i2c_set_bits(i2c_dev->base + I2C_TRISE, 0x3f);
|
||||
|
||||
if (i2c_dev->speed == LS_I2C_SPEED_STANDARD) {
|
||||
val = DIV_ROUND_UP(INPUT_REF_CLK, 100000 * 2);
|
||||
} else {
|
||||
val = DIV_ROUND_UP(INPUT_REF_CLK, 400000 * 3);
|
||||
|
||||
/* Select Fast mode */
|
||||
ccr |= I2C_CCR_FS;
|
||||
}
|
||||
ccr |= val & 0xfff;
|
||||
writel(ccr, i2c_dev->base + I2C_CCR);
|
||||
|
||||
/* Enable I2C */
|
||||
writel(I2C_CR1_PE, i2c_dev->base + I2C_CR1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_i2c_wait_free_bus(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(i2c_dev->base + I2C_SR2,
|
||||
status,
|
||||
!(status & I2C_SR2_BUSY),
|
||||
10, 1000);
|
||||
if (ret) {
|
||||
dev_dbg(i2c_dev->dev, "bus not free\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ls_i2c_write_byte(struct ls_i2c_dev *i2c_dev, u8 byte)
|
||||
{
|
||||
writel(byte, i2c_dev->base + I2C_DR);
|
||||
}
|
||||
|
||||
static void ls_i2c_read_msg(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
|
||||
*msg->buf++ = readl(i2c_dev->base + I2C_DR);
|
||||
msg->count--;
|
||||
}
|
||||
|
||||
static void ls_i2c_terminate_xfer(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
|
||||
ls_i2c_disable_irq(i2c_dev);
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, (1 << (8 + msg->stop)));
|
||||
complete(&i2c_dev->complete);
|
||||
}
|
||||
|
||||
static void ls_i2c_handle_write(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
void __iomem *reg = i2c_dev->base + I2C_CR2;
|
||||
|
||||
if (msg->count) {
|
||||
ls_i2c_write_byte(i2c_dev, *msg->buf++);
|
||||
msg->count--;
|
||||
if (!msg->count)
|
||||
i2c_clr_bits(reg, I2C_CR2_ITBUFEN);
|
||||
} else {
|
||||
ls_i2c_terminate_xfer(i2c_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls_i2c_handle_read(struct ls_i2c_dev *i2c_dev, int flag)
|
||||
{
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
void __iomem *reg = i2c_dev->base + I2C_CR2;
|
||||
int i;
|
||||
|
||||
switch (msg->count) {
|
||||
case 1:
|
||||
/* only transmit 1 bytes condition */
|
||||
ls_i2c_disable_irq(i2c_dev);
|
||||
ls_i2c_read_msg(i2c_dev);
|
||||
complete(&i2c_dev->complete);
|
||||
break;
|
||||
case 2:
|
||||
if (flag != 1) {
|
||||
/* ensure only transmit 2 bytes condition */
|
||||
i2c_clr_bits(reg, I2C_CR2_ITBUFEN);
|
||||
break;
|
||||
}
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, (1 << (8 + msg->stop)));
|
||||
|
||||
ls_i2c_disable_irq(i2c_dev);
|
||||
|
||||
for (i = 2; i > 0; i--)
|
||||
ls_i2c_read_msg(i2c_dev);
|
||||
|
||||
i2c_clr_bits(i2c_dev->base + I2C_CR1, I2C_CR1_POS);
|
||||
complete(&i2c_dev->complete);
|
||||
break;
|
||||
case 3:
|
||||
if (readl(i2c_dev->base + I2C_CR2) & I2C_CR2_ITBUFEN) {
|
||||
i2c_clr_bits(reg, I2C_CR2_ITBUFEN);
|
||||
break;
|
||||
}
|
||||
|
||||
reg = i2c_dev->base + I2C_CR1;
|
||||
i2c_clr_bits(reg, I2C_CR1_ACK);
|
||||
fallthrough;
|
||||
default:
|
||||
ls_i2c_read_msg(i2c_dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ls_i2c_handle_rx_addr()
|
||||
* master receiver
|
||||
* @i2c_dev: Controller's private data
|
||||
*/
|
||||
static void ls_i2c_handle_rx_addr(struct ls_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
|
||||
switch (msg->count) {
|
||||
case 0:
|
||||
ls_i2c_terminate_xfer(i2c_dev);
|
||||
break;
|
||||
case 1:
|
||||
i2c_clr_bits(i2c_dev->base + I2C_CR1,
|
||||
(I2C_CR1_ACK | I2C_CR1_POS));
|
||||
/* start or stop */
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, (1 << (8 + msg->stop)));
|
||||
break;
|
||||
case 2:
|
||||
i2c_clr_bits(i2c_dev->base + I2C_CR1, I2C_CR1_ACK);
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, I2C_CR1_POS);
|
||||
break;
|
||||
|
||||
default:
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, I2C_CR1_ACK);
|
||||
i2c_clr_bits(i2c_dev->base + I2C_CR1, I2C_CR1_POS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ls_i2c_isr_error(u32 status, void *data)
|
||||
{
|
||||
struct ls_i2c_dev *i2c_dev = data;
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
|
||||
/* Arbitration lost */
|
||||
if (status & I2C_SR1_ARLO) {
|
||||
i2c_clr_bits(i2c_dev->base + I2C_SR1, I2C_SR1_ARLO);
|
||||
msg->result = -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acknowledge failure:
|
||||
* In master transmitter mode a Stop must be generated by software
|
||||
*/
|
||||
if (status & I2C_SR1_AF) {
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, I2C_CR1_STOP);
|
||||
i2c_clr_bits(i2c_dev->base + I2C_SR1, I2C_SR1_AF);
|
||||
msg->result = -EIO;
|
||||
}
|
||||
|
||||
/* Bus error */
|
||||
if (status & I2C_SR1_BERR) {
|
||||
i2c_clr_bits(i2c_dev->base + I2C_SR1, I2C_SR1_BERR);
|
||||
msg->result = -EIO;
|
||||
}
|
||||
|
||||
ls_i2c_disable_irq(i2c_dev);
|
||||
complete(&i2c_dev->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ls_i2c_isr_event() - Interrupt routine for I2C bus event
|
||||
* @irq: interrupt number
|
||||
* @data: Controller's private data
|
||||
*/
|
||||
static irqreturn_t ls_i2c_isr_event(int irq, void *data)
|
||||
{
|
||||
struct ls_i2c_dev *i2c_dev = data;
|
||||
struct priv_msg *msg = &i2c_dev->msg;
|
||||
u32 status, ien, event, cr2;
|
||||
u32 possible_status = I2C_SR1_ITEVTEN_MASK;
|
||||
int i;
|
||||
|
||||
status = readl(i2c_dev->base + I2C_SR1);
|
||||
if (status & (I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_BERR))
|
||||
return ls_i2c_isr_error(status, data);
|
||||
|
||||
cr2 = readl(i2c_dev->base + I2C_CR2);
|
||||
ien = cr2 & I2C_CR2_IRQ_MASK;
|
||||
|
||||
/* Update possible_status if buffer interrupt is enabled */
|
||||
if (ien & I2C_CR2_ITBUFEN)
|
||||
possible_status |= I2C_SR1_ITBUFEN_MASK;
|
||||
event = status & possible_status;
|
||||
|
||||
if (!event) {
|
||||
dev_dbg(i2c_dev->dev,
|
||||
"spurious evt irq (status=0x%08x, ien=0x%08x)\n",
|
||||
status, ien);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Start condition generated */
|
||||
if (event & I2C_SR1_SB)
|
||||
ls_i2c_write_byte(i2c_dev, msg->addr);
|
||||
|
||||
/* I2C Address sent */
|
||||
if (event & I2C_SR1_ADDR) {
|
||||
if (msg->addr & I2C_M_RD)
|
||||
ls_i2c_handle_rx_addr(i2c_dev);
|
||||
/* Clear ADDR flag */
|
||||
readl(i2c_dev->base + I2C_SR2);
|
||||
/* Enable buffer interrupts for RX/TX not empty events */
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR2, I2C_CR2_ITBUFEN);
|
||||
}
|
||||
|
||||
if (msg->addr & I2C_M_RD) {
|
||||
/* RX */
|
||||
if (event & 0x40)
|
||||
ls_i2c_handle_read(i2c_dev, 0);
|
||||
if (event & 0x4)
|
||||
ls_i2c_handle_read(i2c_dev, 1);
|
||||
} else {
|
||||
/* TX */
|
||||
if (event & 0x84)
|
||||
for (i = 0; i < 1 + !!((event & 0x84) == 0x84); i++)
|
||||
ls_i2c_handle_write(i2c_dev);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ls_i2c_xfer_msg(struct ls_i2c_dev *i2c_dev,
|
||||
struct i2c_msg *msg, bool is_stop)
|
||||
{
|
||||
struct priv_msg *priv_msg = &i2c_dev->msg;
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
priv_msg->addr = i2c_8bit_addr_from_msg(msg);
|
||||
priv_msg->buf = msg->buf;
|
||||
priv_msg->count = msg->len;
|
||||
priv_msg->stop = is_stop;
|
||||
priv_msg->result = 0;
|
||||
|
||||
reinit_completion(&i2c_dev->complete);
|
||||
|
||||
/* Enable events and errors interrupts */
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR2,
|
||||
I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
|
||||
|
||||
timeout = wait_for_completion_timeout(&i2c_dev->complete,
|
||||
i2c_dev->adap.timeout);
|
||||
ret = priv_msg->result;
|
||||
|
||||
if (!timeout)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ls_i2c_xfer() - Transfer combined I2C message
|
||||
* @i2c_adap: Adapter pointer to the controller
|
||||
* @msgs: Pointer to data to be written.
|
||||
* @num: Number of messages to be executed
|
||||
*/
|
||||
static int ls_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
|
||||
int num)
|
||||
{
|
||||
struct ls_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
ret = ls_i2c_wait_free_bus(i2c_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* START generation */
|
||||
i2c_set_bits(i2c_dev->base + I2C_CR1, I2C_CR1_START);
|
||||
|
||||
for (i = 0; i < num && !ret; i++)
|
||||
ret = ls_i2c_xfer_msg(i2c_dev, &msgs[i], i == num - 1);
|
||||
|
||||
return (ret < 0) ? ret : num;
|
||||
}
|
||||
|
||||
static u32 ls_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm ls_i2c_algo = {
|
||||
.master_xfer = ls_i2c_xfer,
|
||||
.functionality = ls_i2c_func,
|
||||
};
|
||||
|
||||
static int ls_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct ls_i2c_dev *i2c_dev;
|
||||
struct resource *res;
|
||||
u32 irq_event, clk_rate;
|
||||
struct i2c_adapter *adap;
|
||||
int ret;
|
||||
int name_buffer_len;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no mem resource?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
irq_event = platform_get_irq(pdev, 0);
|
||||
if (irq_event <= 0) {
|
||||
dev_err(&pdev->dev, "IRQ event missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
|
||||
if (!i2c_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(i2c_dev->base)) {
|
||||
ret = PTR_ERR(i2c_dev->base);
|
||||
goto free_i2c_mem;
|
||||
}
|
||||
|
||||
i2c_dev->speed = LS_I2C_SPEED_STANDARD;
|
||||
ret = of_property_read_u32(np, "i2c-speed", &clk_rate);
|
||||
if (!ret && clk_rate >= 400000)
|
||||
i2c_dev->speed = LS_I2C_SPEED_FAST;
|
||||
|
||||
init_completion(&i2c_dev->complete);
|
||||
|
||||
i2c_dev->dev = &pdev->dev;
|
||||
|
||||
ret = request_irq(irq_event, ls_i2c_isr_event, IRQF_SHARED,
|
||||
pdev->name, i2c_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq event %i\n",
|
||||
irq_event);
|
||||
goto free_i2c_ioremap;
|
||||
}
|
||||
|
||||
ls_i2c_hw_config(i2c_dev);
|
||||
|
||||
adap = &i2c_dev->adap;
|
||||
i2c_set_adapdata(adap, i2c_dev);
|
||||
adap->nr = pdev->id;
|
||||
name_buffer_len = strlen(pdev->name);
|
||||
name_buffer_len = (name_buffer_len >= sizeof(adap->name)) ? sizeof(adap->name) - 1 : name_buffer_len;
|
||||
memset(adap->name, 0, name_buffer_len + 1);
|
||||
strncpy(adap->name, pdev->name, name_buffer_len);
|
||||
adap->name[name_buffer_len] = 0;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->retries = 5;
|
||||
adap->algo = &ls_i2c_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
adap->timeout = 2 * HZ;
|
||||
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
goto free_i2c_ioremap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, i2c_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
free_i2c_ioremap:
|
||||
devm_iounmap(&pdev->dev, i2c_dev->base);
|
||||
free_i2c_mem:
|
||||
kfree(i2c_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ls_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ls_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
i2c_del_adapter(&i2c_dev->adap);
|
||||
iounmap(i2c_dev->base);
|
||||
kfree(i2c_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ls_i2c_match[] = {
|
||||
{.compatible = "loongson,lsfs-i2c"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls_i2c_match);
|
||||
|
||||
static struct platform_driver ls_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "lsfs-i2c",
|
||||
.of_match_table = ls_i2c_match,
|
||||
},
|
||||
.probe = ls_i2c_probe,
|
||||
.remove = ls_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ls_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Loongson Technology Corporation Limited");
|
||||
MODULE_DESCRIPTION("Loongson fast speed I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1647,4 +1647,10 @@ config XILINX_AMS
|
||||
The driver can also be built as a module. If so, the module will be called
|
||||
xilinx-ams.
|
||||
|
||||
config LS_2K300_ADC
|
||||
tristate "ls2k300 driver"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
loongson 2k300 ADC driver
|
||||
endmenu
|
||||
|
||||
@@ -145,3 +145,4 @@ obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
|
||||
xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
|
||||
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
|
||||
obj-$(CONFIG_LS_2K300_ADC) += loongson-2k300-adc.o loongson-2k300-adc-core.o
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include "loongson-2k300-adc-core.h"
|
||||
|
||||
#define ADC_DATA_MASK 0x0FFF
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : adc_init
|
||||
* Description : Initializes the ADCx peripheral according to the specified parameters
|
||||
* in the ADC_InitStruct.
|
||||
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
|
||||
* - ADC_InitStruct: pointer to an adc_init_info structure that
|
||||
* contains the configuration information for the specified
|
||||
* ADC peripheral.
|
||||
* Output : None
|
||||
* Return : None
|
||||
******************************************************************************/
|
||||
void adc_init(adc_reg_map* ADCx, adc_init_info* ADC_InitStruct)
|
||||
{
|
||||
unsigned int temp_val_1 = 0;
|
||||
unsigned int temp_val_2 = 0;
|
||||
|
||||
/*---------------------------- ADCx CR1 Configuration -----------------*/
|
||||
/* Get the ADCx CR1 value */
|
||||
temp_val_1 = ADCx->CR1;
|
||||
/* Clear DUALMOD and SCAN bits */
|
||||
temp_val_1 &= CR1_CLEAR_MASK;
|
||||
/* Configure ADCx: Dual mode and scan conversion mode */
|
||||
/* Set DUALMOD bits according to ADC_Mode value */
|
||||
/* Set SCAN bit according to ADC_ScanConvMode value */
|
||||
temp_val_1 |= (unsigned int)(ADC_InitStruct->ADC_Mode | ((unsigned int)ADC_InitStruct->ADC_ScanConvMode << 8) |
|
||||
((unsigned int)((ADC_InitStruct->ADC_ClkDivider)&0x3f) << 24) |
|
||||
((unsigned int)ADC_InitStruct->ADC_DiffMod << 20) |
|
||||
((unsigned int)ADC_InitStruct->ADC_OutPhaseSel << 30)); //yg
|
||||
temp_val_1 |= 1 << CR1_EOC_IE_OFFSET;
|
||||
temp_val_1 ^= 1 << CR1_EOC_IE_OFFSET;
|
||||
temp_val_1 |= ADC_InitStruct->ADC_Int_EOC << CR1_EOC_IE_OFFSET;
|
||||
temp_val_1 |= 1 << CR1_J_EOC_IE_OFFSET;
|
||||
temp_val_1 ^= 1 << CR1_J_EOC_IE_OFFSET;
|
||||
temp_val_1 |= ADC_InitStruct->ADC_Int_JEOC << CR1_J_EOC_IE_OFFSET;
|
||||
/* Write to ADCx CR1 */
|
||||
ADCx->CR1 = temp_val_1;
|
||||
|
||||
/*---------------------------- ADCx CR2 Configuration -----------------*/
|
||||
/* Get the ADCx CR2 value */
|
||||
temp_val_1 = ADCx->CR2;
|
||||
/* Clear CONT, ALIGN and EXTSEL bits */
|
||||
temp_val_1 &= CR2_CLEAR_MASK;
|
||||
/* Configure ADCx: external trigger event and continuous conversion mode */
|
||||
/* Set ALIGN bit according to ADC_DataAlign value */
|
||||
/* Set EXTSEL bits according to ADC_ExternalTrigConv value */
|
||||
/* Set CONT bit according to ADC_ContinuousConvMode value */
|
||||
temp_val_1 |= (unsigned int)(ADC_InitStruct->ADC_DataAlign | ADC_InitStruct->ADC_ExternalTrigConv |
|
||||
((unsigned int)ADC_InitStruct->ADC_ContinuousConvMode << 1) |
|
||||
((unsigned int)ADC_InitStruct->ADC_JTrigMod << 24) | //yg
|
||||
((unsigned int)(((ADC_InitStruct->ADC_ClkDivider)>>6)&0xf) << 26) |
|
||||
((unsigned int)ADC_InitStruct->ADC_ADCEdge << 30) |
|
||||
((unsigned int)ADC_InitStruct->ADC_ClkMask << 31)) ; //yg
|
||||
/* Write to ADCx CR2 */
|
||||
ADCx->CR2 = temp_val_1;
|
||||
|
||||
/*---------------------------- ADCx SQR1 Configuration -----------------*/
|
||||
/* Get the ADCx SQR1 value */
|
||||
temp_val_1 = ADCx->SQR1;
|
||||
/* Clear L bits */
|
||||
temp_val_1 &= SQR1_CLEAR_MASK;
|
||||
/* Configure ADCx: regular channel sequence length */
|
||||
/* Set L bits according to ADC_NbrOfChannel value */
|
||||
temp_val_2 |= (ADC_InitStruct->ADC_NbrOfChannel - 1);
|
||||
temp_val_1 |= ((unsigned int)temp_val_2 << 20);
|
||||
/* Write to ADCx SQR1 */
|
||||
ADCx->SQR1 = temp_val_1;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : adc_struct_init
|
||||
* Description : Fills each ADC_InitStruct member with its default value.
|
||||
* Input : ADC_InitStruct : pointer to an adc_init_info structure
|
||||
* which will be initialized.
|
||||
* Output : None
|
||||
* Return : None
|
||||
*******************************************************************************/
|
||||
void adc_struct_init(adc_init_info* ADC_InitStruct)
|
||||
{
|
||||
/* Reset ADC init structure parameters values */
|
||||
/* Initialize the ADC_Mode member */
|
||||
ADC_InitStruct->ADC_Mode = ADC_Mode_Independent;
|
||||
/* initialize the ADC_ScanConvMode member */
|
||||
ADC_InitStruct->ADC_ScanConvMode = DISABLE;
|
||||
/* Initialize the ADC_ContinuousConvMode member */
|
||||
ADC_InitStruct->ADC_ContinuousConvMode = DISABLE;
|
||||
/* Initialize the ADC_ExternalTrigConv member */
|
||||
ADC_InitStruct->ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
|
||||
/* Initialize the ADC_DataAlign member */
|
||||
ADC_InitStruct->ADC_DataAlign = ADC_DataAlign_Right;
|
||||
/* Initialize the ADC_NbrOfChannel member */
|
||||
ADC_InitStruct->ADC_NbrOfChannel = 1;
|
||||
/* Initialize the ADC_ClkDivider member */
|
||||
ADC_InitStruct->ADC_ClkDivider = 0xff;
|
||||
/* Initialize the ADC_JTrigMod member */
|
||||
ADC_InitStruct->ADC_JTrigMod = 0;
|
||||
/* Initialize the ADC_ADCEdge member */
|
||||
ADC_InitStruct->ADC_ADCEdge = 0;
|
||||
/* Initialize the ADC_DIFFMOD member */
|
||||
ADC_InitStruct->ADC_DiffMod = 0;
|
||||
/* Initialize the ADC_OutPhaseSel member */
|
||||
ADC_InitStruct->ADC_OutPhaseSel = 0;
|
||||
/* Initialize the ADC_ClkMask member */
|
||||
ADC_InitStruct->ADC_ClkMask = 0;
|
||||
/* Initialize CR1 EOC disable */
|
||||
ADC_InitStruct->ADC_Int_EOC = DISABLE;
|
||||
/* Initialize CR1 EOC disable */
|
||||
ADC_InitStruct->ADC_Int_JEOC = DISABLE;
|
||||
}
|
||||
|
||||
void ADC_RegularChannelConfig(adc_reg_map* ADCx, unsigned char ADC_Channel, unsigned char Rank, unsigned char ADC_SampleTime)
|
||||
{
|
||||
unsigned int temp_val_1 = 0, temp_val_2 = 0;
|
||||
|
||||
/* if ADC_Channel_10 ... ADC_Channel_17 is selected */
|
||||
if (ADC_Channel > ADC_Channel_9) {
|
||||
/* Get the old register value */
|
||||
temp_val_1 = ADCx->SMPR1;
|
||||
/* Calculate the mask to clear */
|
||||
temp_val_2 = SMPR1_SMP_MASK << (3 * (ADC_Channel - 10));
|
||||
/* Clear the old discontinuous mode channel count */
|
||||
temp_val_1 &= ~temp_val_2;
|
||||
/* Calculate the mask to set */
|
||||
temp_val_2 = (unsigned int)ADC_SampleTime << (3 * (ADC_Channel - 10));
|
||||
/* Set the discontinuous mode channel count */
|
||||
temp_val_1 |= temp_val_2;
|
||||
/* Store the new register value */
|
||||
ADCx->SMPR1 = temp_val_1;
|
||||
} else { /* ADC_Channel include in ADC_Channel_[0..9] */
|
||||
/* Get the old register value */
|
||||
temp_val_1 = ADCx->SMPR2;
|
||||
/* Calculate the mask to clear */
|
||||
temp_val_2 = SMPR2_SMP_MASK << (3 * ADC_Channel);
|
||||
/* Clear the old discontinuous mode channel count */
|
||||
temp_val_1 &= ~temp_val_2;
|
||||
/* Calculate the mask to set */
|
||||
temp_val_2 = (unsigned int)ADC_SampleTime << (3 * ADC_Channel);
|
||||
/* Set the discontinuous mode channel count */
|
||||
temp_val_1 |= temp_val_2;
|
||||
/* Store the new register value */
|
||||
ADCx->SMPR2 = temp_val_1;
|
||||
}
|
||||
/* For Rank 1 to 6 */
|
||||
if (Rank < 7) {
|
||||
/* Get the old register value */
|
||||
temp_val_1 = ADCx->SQR3;
|
||||
/* Calculate the mask to clear */
|
||||
temp_val_2 = SQR3_SQ_MASK << (5 * (Rank - 1));
|
||||
/* Clear the old SQx bits for the selected rank */
|
||||
temp_val_1 &= ~temp_val_2;
|
||||
/* Calculate the mask to set */
|
||||
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 1));
|
||||
/* Set the SQx bits for the selected rank */
|
||||
temp_val_1 |= temp_val_2;
|
||||
/* Store the new register value */
|
||||
ADCx->SQR3 = temp_val_1;
|
||||
} else if (Rank < 13) { /* For Rank 7 to 12 */
|
||||
/* Get the old register value */
|
||||
temp_val_1 = ADCx->SQR2;
|
||||
/* Calculate the mask to clear */
|
||||
temp_val_2 = SQR2_SQ_MASK << (5 * (Rank - 7));
|
||||
/* Clear the old SQx bits for the selected rank */
|
||||
temp_val_1 &= ~temp_val_2;
|
||||
/* Calculate the mask to set */
|
||||
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 7));
|
||||
/* Set the SQx bits for the selected rank */
|
||||
temp_val_1 |= temp_val_2;
|
||||
/* Store the new register value */
|
||||
ADCx->SQR2 = temp_val_1;
|
||||
} else { /* For Rank 13 to 16 */
|
||||
/* Get the old register value */
|
||||
temp_val_1 = ADCx->SQR1;
|
||||
/* Calculate the mask to clear */
|
||||
temp_val_2 = SQR1_SQ_MASK << (5 * (Rank - 13));
|
||||
/* Clear the old SQx bits for the selected rank */
|
||||
temp_val_1 &= ~temp_val_2;
|
||||
/* Calculate the mask to set */
|
||||
temp_val_2 = (unsigned int)ADC_Channel << (5 * (Rank - 13));
|
||||
/* Set the SQx bits for the selected rank */
|
||||
temp_val_1 |= temp_val_2;
|
||||
/* Store the new register value */
|
||||
ADCx->SQR1 = temp_val_1;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : adc_dev_enable
|
||||
* Description : Enables or disables the specified ADC peripheral.
|
||||
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
|
||||
* - state: new state of the ADCx peripheral. This parameter
|
||||
* can be: ENABLE or DISABLE.
|
||||
* Output : None
|
||||
* Return : None
|
||||
*******************************************************************************/
|
||||
void adc_dev_enable(adc_reg_map* ADCx, FunctionalState state)
|
||||
{
|
||||
unsigned int temp;
|
||||
|
||||
if (state == ENABLE) {
|
||||
temp = ADCx->CR2;
|
||||
temp |= 1 << CR2_ADON_OFFSET;
|
||||
temp ^= 1 << CR2_ADON_OFFSET;
|
||||
temp |= (state << CR2_ADON_OFFSET);
|
||||
ADCx->CR2 = temp;
|
||||
|
||||
// 触发复位校准和AD校准
|
||||
ADCx->CR2 = temp;
|
||||
temp = ADCx->CR2;
|
||||
temp |= (state << 2) | (state << 3);
|
||||
ADCx->CR2 = temp;
|
||||
#if 0
|
||||
while (1) {
|
||||
temp = ADCx->CR2;
|
||||
temp &= 1 << 2;
|
||||
if (!temp)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
ndelay(1000000);
|
||||
#endif
|
||||
} else {
|
||||
temp = ADCx->CR2;
|
||||
temp |= 1 << CR2_ADON_OFFSET;
|
||||
temp ^= 1 << CR2_ADON_OFFSET;
|
||||
ADCx->CR2 = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : adc_software_start_conv_trigger
|
||||
* Description : Enables or disables the selected ADC software start conversion .
|
||||
* Input : - ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
|
||||
* - state: new state of the selected ADC software start conversion.
|
||||
* This parameter can be: ENABLE or DISABLE.
|
||||
* Output : None
|
||||
* Return : None
|
||||
*******************************************************************************/
|
||||
void adc_software_start_conv_trigger(adc_reg_map* ADCx, FunctionalState state, unsigned char J_channel)
|
||||
{
|
||||
unsigned int temp;
|
||||
unsigned char EOC_offset;
|
||||
unsigned char start_offset;
|
||||
unsigned char exttrig_offset;
|
||||
|
||||
EOC_offset = J_channel ? SR_J_EOC_OFFSET : SR_EOC_OFFSET;
|
||||
start_offset = J_channel ? CR2_EXTTRIG_SW_J_START_OFFSET : CR2_EXTTRIG_SW_START_OFFSET;
|
||||
exttrig_offset = J_channel ? CR2_J_EXTTRIG_OFFSET : CR2_EXTTRIG_OFFSET;
|
||||
|
||||
if (state == ENABLE) {
|
||||
temp = ADCx->CR2;
|
||||
temp |= 1 << exttrig_offset;
|
||||
ADCx->CR2 = temp;
|
||||
// 先把 EOC 清除
|
||||
temp = ADCx->SR;
|
||||
temp |= 1 << EOC_offset;
|
||||
temp ^= 1 << EOC_offset;
|
||||
ADCx->SR = temp;
|
||||
ADCx->CR2 |= 1 << start_offset;
|
||||
} else {
|
||||
/* Disable the selected ADC conversion on external event and stop the selected ADC conversion */
|
||||
temp = ADCx->CR2;
|
||||
temp |= 1 << start_offset;
|
||||
temp ^= 1 << start_offset;
|
||||
ADCx->CR2 = temp;
|
||||
}
|
||||
}
|
||||
|
||||
void adc_eoc_check_conv_end(adc_reg_map* ADCx)
|
||||
{
|
||||
int max_loop;
|
||||
unsigned int temp;
|
||||
|
||||
max_loop = 0;
|
||||
while (1) {
|
||||
temp = ADCx->SR;
|
||||
temp &= 1 << 1;
|
||||
ndelay(10);
|
||||
++max_loop;
|
||||
if (temp == 2)
|
||||
break;
|
||||
if (max_loop == 10000) {
|
||||
pr_info("adc_eoc_check_conv_end eoc wait timeout\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
#ifndef __LOONGSON_2K300_ADC_CORE_H__
|
||||
#define __LOONGSON_2K300_ADC_CORE_H__
|
||||
|
||||
typedef enum FunctionalState {
|
||||
DISABLE = 0,
|
||||
ENABLE,
|
||||
} FunctionalState;
|
||||
|
||||
// ADC register offsets
|
||||
#define ADC_SR 0x00
|
||||
#define ADC_CR1 0x04
|
||||
#define ADC_CR2 0x08
|
||||
#define ADC_SMPR1 0x0C
|
||||
#define ADC_SMPR2 0x10
|
||||
#define ADC_JOFR1 0x14
|
||||
#define ADC_JOFR2 0x18
|
||||
#define ADC_JOFR3 0x1C
|
||||
#define ADC_JOFR4 0x20
|
||||
#define ADC_HTR 0x24
|
||||
#define ADC_LTR 0x28
|
||||
#define ADC_SQR1 0x2C
|
||||
#define ADC_SQR2 0x30
|
||||
#define ADC_SQR3 0x34
|
||||
#define ADC_JSQR 0x38
|
||||
#define ADC_JDR1 0x3C
|
||||
#define ADC_JDR2 0x40
|
||||
#define ADC_JDR3 0x44
|
||||
#define ADC_JDR4 0x48
|
||||
#define ADC_DR 0x4C
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile unsigned int SR;
|
||||
volatile unsigned int CR1;
|
||||
volatile unsigned int CR2;
|
||||
volatile unsigned int SMPR1;
|
||||
volatile unsigned int SMPR2;
|
||||
volatile unsigned int JOFR1;
|
||||
volatile unsigned int JOFR2;
|
||||
volatile unsigned int JOFR3;
|
||||
volatile unsigned int JOFR4;
|
||||
volatile unsigned int HTR;
|
||||
volatile unsigned int LTR;
|
||||
volatile unsigned int SQR1;
|
||||
volatile unsigned int SQR2;
|
||||
volatile unsigned int SQR3;
|
||||
volatile unsigned int JSQR;
|
||||
volatile unsigned int JDR1;
|
||||
volatile unsigned int JDR2;
|
||||
volatile unsigned int JDR3;
|
||||
volatile unsigned int JDR4;
|
||||
volatile unsigned int DR;
|
||||
} adc_reg_map;
|
||||
|
||||
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
|
||||
* File Name : stm32f10x_adc.h
|
||||
* Author : MCD Application Team
|
||||
* Version : V2.0.3
|
||||
* Date : 09/22/2008
|
||||
* Description : This file contains all the functions prototypes for the
|
||||
* ADC firmware library.
|
||||
********************************************************************************
|
||||
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
||||
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
|
||||
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
|
||||
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
|
||||
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
||||
*******************************************************************************/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
|
||||
#define ADC_RCG (12)
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* ADC DISCNUM mask */
|
||||
// #define CR1_DISCNUM_Reset ((unsigned int)0xFFFF1FFF)
|
||||
|
||||
// /* ADC DISCEN mask */
|
||||
// #define CR1_DISCEN_Set ((unsigned int)0x00000800)
|
||||
// #define CR1_DISCEN_Reset ((unsigned int)0xFFFFF7FF)
|
||||
|
||||
// /* ADC JAUTO mask */
|
||||
// #define CR1_JAUTO_Set ((unsigned int)0x00000400)
|
||||
// #define CR1_JAUTO_Reset ((unsigned int)0xFFFFFBFF)
|
||||
|
||||
// /* ADC JDISCEN mask */
|
||||
// #define CR1_JDISCEN_Set ((unsigned int)0x00001000)
|
||||
// #define CR1_JDISCEN_Reset ((unsigned int)0xFFFFEFFF)
|
||||
|
||||
// /* ADC AWDCH mask */
|
||||
// #define CR1_AWDCH_Reset ((unsigned int)0xFFFFFFE0)
|
||||
|
||||
// /* ADC Analog watchdog enable mode mask */
|
||||
// #define CR1_AWDMode_Reset ((unsigned int)0xFF3FFDFF)
|
||||
|
||||
// /* CR1 register Mask */
|
||||
#define CR1_CLEAR_MASK ((unsigned int)0xFFF0FEFF)
|
||||
|
||||
//
|
||||
#define SR_EOC_OFFSET 0x1
|
||||
#define SR_J_EOC_OFFSET 0x2
|
||||
|
||||
//
|
||||
#define CR1_EOC_IE_OFFSET 0x5
|
||||
#define CR1_J_EOC_IE_OFFSET 0x7
|
||||
|
||||
/* ADC ADON mask */
|
||||
#define CR2_ADON_OFFSET 0x0
|
||||
|
||||
/* ADC reset */
|
||||
// #define CR2_ADC_Reset ((unsigned int)0x80000000)
|
||||
|
||||
// /* ADC DMA mask */
|
||||
// #define CR2_DMA_Set ((unsigned int)0x00000100)
|
||||
// #define CR2_DMA_Reset ((unsigned int)0xFFFFFEFF)
|
||||
|
||||
// /* ADC RSTCAL mask */
|
||||
// #define CR2_RSTCAL_Set ((unsigned int)0x00000008)
|
||||
|
||||
// /* ADC CAL mask */
|
||||
// #define CR2_CAL_Set ((unsigned int)0x00000004)
|
||||
|
||||
// /* ADC SWSTART mask */
|
||||
// #define CR2_SWSTART_Set ((unsigned int)0x00400000)
|
||||
|
||||
// /* ADC EXTTRIG mask */
|
||||
// #define CR2_EXTTRIG_Set ((unsigned int)0x00100000)
|
||||
// #define CR2_EXTTRIG_Reset ((unsigned int)0xFFEFFFFF)
|
||||
|
||||
/* ADC Software start mask */
|
||||
#define CR2_EXTTRIG_SW_START_OFFSET 22
|
||||
#define CR2_EXTTRIG_SW_J_START_OFFSET 21
|
||||
#define CR2_EXTTRIG_OFFSET 20
|
||||
#define CR2_J_EXTTRIG_OFFSET 15
|
||||
// #define CR2_EXTTRIG_SWSTART_Set ((unsigned int)0x00500000)
|
||||
// #define CR2_EXTTRIG_SWSTART_Reset ((unsigned int)0xFFAFFFFF)
|
||||
|
||||
/* ADC JEXTSEL mask */
|
||||
// #define CR2_JEXTSEL_Reset ((unsigned int)0xFFFF8FFF)
|
||||
|
||||
// /* ADC JEXTTRIG mask */
|
||||
// #define CR2_JEXTTRIG_Set ((unsigned int)0x00008000)
|
||||
// #define CR2_JEXTTRIG_Reset ((unsigned int)0xFFFF7FFF)
|
||||
|
||||
// /* ADC JSWSTART mask */
|
||||
// #define CR2_JSWSTART_Set ((unsigned int)0x00200000)
|
||||
|
||||
// /* ADC injected software start mask */
|
||||
// #define CR2_JEXTTRIG_JSWSTART_Set ((unsigned int)0x00208000)
|
||||
// #define CR2_JEXTTRIG_JSWSTART_Reset ((unsigned int)0xFFDF7FFF)
|
||||
|
||||
// /* ADC TSPD mask */
|
||||
// #define CR2_TSVREFE_Set ((unsigned int)0x00800000)
|
||||
// #define CR2_TSVREFE_Reset ((unsigned int)0xFF7FFFFF)
|
||||
|
||||
// /* CR2 register Mask */
|
||||
#define CR2_CLEAR_MASK ((unsigned int)0xFFF1F7FD)
|
||||
|
||||
// /* ADC SQx mask */
|
||||
#define SQR3_SQ_MASK 0x1F
|
||||
#define SQR2_SQ_MASK 0x1F
|
||||
#define SQR1_SQ_MASK 0x1F
|
||||
|
||||
// /* SQR1 register Mask */
|
||||
#define SQR1_CLEAR_MASK ((unsigned int)0xFF0FFFFF)
|
||||
|
||||
// /* ADC JSQx mask */
|
||||
// #define JSQR_JSQ_Set ((unsigned int)0x0000001F)
|
||||
|
||||
// /* ADC JL mask */
|
||||
// #define JSQR_JL_Set ((unsigned int)0x00300000)
|
||||
// #define JSQR_JL_Reset ((unsigned int)0xFFCFFFFF)
|
||||
|
||||
// /* ADC SMPx mask */
|
||||
#define SMPR1_SMP_MASK 0x7
|
||||
#define SMPR2_SMP_MASK 0x7
|
||||
|
||||
// /* ADC JDRx registers offset */
|
||||
// #define JDR_Offset ((unsigned char)0x28)
|
||||
|
||||
// #define DISABLE 0
|
||||
// #define ENABLE 1
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
// #include "../i2c/ls2k0300_map.h"
|
||||
|
||||
/* Exported types ------------------------------------------------------------*/
|
||||
/* ADC Init structure definition */
|
||||
typedef struct
|
||||
{
|
||||
unsigned int ADC_Mode;
|
||||
int ADC_ScanConvMode; //CR1 scan
|
||||
int ADC_ContinuousConvMode;//CR2 cont
|
||||
unsigned int ADC_ExternalTrigConv;//CR2 extsel
|
||||
unsigned int ADC_DataAlign;//CR2 align
|
||||
unsigned char ADC_NbrOfChannel;//SQR1 l
|
||||
unsigned short ADC_ClkDivider;//CR1 clkdiv
|
||||
unsigned char ADC_JTrigMod;//CR2 jtrigmod
|
||||
unsigned char ADC_ADCEdge;//CR2 adcedge
|
||||
unsigned char ADC_DiffMod;//CR1 diffmod
|
||||
unsigned char ADC_OutPhaseSel;//CR1 ops
|
||||
unsigned char ADC_ClkMask;//CR2 clkmask
|
||||
unsigned char ADC_Int_EOC; // CR1 EOC enable ?
|
||||
unsigned char ADC_Int_JEOC; // CR1 JEOC enable ?
|
||||
}adc_init_info;
|
||||
|
||||
/* Exported constants --------------------------------------------------------*/
|
||||
|
||||
/* ADC dual mode -------------------------------------------------------------*/
|
||||
#define ADC_Mode_Independent ((unsigned int)0x00000000)
|
||||
#define ADC_Mode_RegInjecSimult ((unsigned int)0x00010000)
|
||||
#define ADC_Mode_RegSimult_AlterTrig ((unsigned int)0x00020000)
|
||||
#define ADC_Mode_InjecSimult_FastInterl ((unsigned int)0x00030000)
|
||||
#define ADC_Mode_InjecSimult_SlowInterl ((unsigned int)0x00040000)
|
||||
#define ADC_Mode_InjecSimult ((unsigned int)0x00050000)
|
||||
#define ADC_Mode_RegSimult ((unsigned int)0x00060000)
|
||||
#define ADC_Mode_FastInterl ((unsigned int)0x00070000)
|
||||
#define ADC_Mode_SlowInterl ((unsigned int)0x00080000)
|
||||
#define ADC_Mode_AlterTrig ((unsigned int)0x00090000)
|
||||
|
||||
#define IS_ADC_MODE(MODE) (((MODE) == ADC_Mode_Independent) || \
|
||||
((MODE) == ADC_Mode_RegInjecSimult) || \
|
||||
((MODE) == ADC_Mode_RegSimult_AlterTrig) || \
|
||||
((MODE) == ADC_Mode_InjecSimult_FastInterl) || \
|
||||
((MODE) == ADC_Mode_InjecSimult_SlowInterl) || \
|
||||
((MODE) == ADC_Mode_InjecSimult) || \
|
||||
((MODE) == ADC_Mode_RegSimult) || \
|
||||
((MODE) == ADC_Mode_FastInterl) || \
|
||||
((MODE) == ADC_Mode_SlowInterl) || \
|
||||
((MODE) == ADC_Mode_AlterTrig))
|
||||
|
||||
/* ADC extrenal trigger sources for regular channels conversion --------------*/
|
||||
/* for ADC1 and ADC2 */
|
||||
#define ADC_ExternalTrigConv_T1_CC1 ((unsigned int)0x00000000)
|
||||
#define ADC_ExternalTrigConv_T1_CC2 ((unsigned int)0x00020000)
|
||||
#define ADC_ExternalTrigConv_T2_CC2 ((unsigned int)0x00060000)
|
||||
#define ADC_ExternalTrigConv_T3_TRGO ((unsigned int)0x00080000)
|
||||
#define ADC_ExternalTrigConv_T4_CC4 ((unsigned int)0x000A0000)
|
||||
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((unsigned int)0x000C0000)
|
||||
/* for ADC1, ADC2 and ADC3 */
|
||||
#define ADC_ExternalTrigConv_T1_CC3 ((unsigned int)0x00040000)
|
||||
#define ADC_ExternalTrigConv_None ((unsigned int)0x000E0000)
|
||||
/* for ADC3 */
|
||||
#define ADC_ExternalTrigConv_T3_CC1 ((unsigned int)0x00000000)
|
||||
#define ADC_ExternalTrigConv_T2_CC3 ((unsigned int)0x00020000)
|
||||
#define ADC_ExternalTrigConv_T8_CC1 ((unsigned int)0x00060000)
|
||||
#define ADC_ExternalTrigConv_T8_TRGO ((unsigned int)0x00080000)
|
||||
#define ADC_ExternalTrigConv_T5_CC1 ((unsigned int)0x000A0000)
|
||||
#define ADC_ExternalTrigConv_T5_CC3 ((unsigned int)0x000C0000)
|
||||
|
||||
#define IS_ADC_EXT_TRIG(REGTRIG) (((REGTRIG) == ADC_ExternalTrigConv_T1_CC1) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T1_CC2) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T1_CC3) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T2_CC2) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T3_TRGO) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T4_CC4) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_None) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T3_CC1) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T2_CC3) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T8_CC1) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T8_TRGO) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T5_CC1) || \
|
||||
((REGTRIG) == ADC_ExternalTrigConv_T5_CC3))
|
||||
|
||||
/* ADC data align ------------------------------------------------------------*/
|
||||
#define ADC_DataAlign_Right ((unsigned int)0x00000000)
|
||||
#define ADC_DataAlign_Left ((unsigned int)0x00000800)
|
||||
|
||||
#define IS_ADC_DATA_ALIGN(ALIGN) (((ALIGN) == ADC_DataAlign_Right) || \
|
||||
((ALIGN) == ADC_DataAlign_Left))
|
||||
|
||||
/* ADC channels --------------------------------------------------------------*/
|
||||
#define ADC_Channel_0 ((unsigned char)0x00)
|
||||
#define ADC_Channel_1 ((unsigned char)0x01)
|
||||
#define ADC_Channel_2 ((unsigned char)0x02)
|
||||
#define ADC_Channel_3 ((unsigned char)0x03)
|
||||
#define ADC_Channel_4 ((unsigned char)0x08)
|
||||
#define ADC_Channel_5 ((unsigned char)0x09)
|
||||
#define ADC_Channel_6 ((unsigned char)0x0a)
|
||||
#define ADC_Channel_7 ((unsigned char)0x0b)
|
||||
#define ADC_Channel_8 ((unsigned char)0x18)
|
||||
#define ADC_Channel_9 ((unsigned char)0x19)
|
||||
#define ADC_Channel_10 ((unsigned char)0x1A)
|
||||
#define ADC_Channel_11 ((unsigned char)0x1B)
|
||||
#define ADC_Channel_12 ((unsigned char)0x1C)
|
||||
#define ADC_Channel_13 ((unsigned char)0x1D)
|
||||
#define ADC_Channel_14 ((unsigned char)0x1E)
|
||||
#define ADC_Channel_15 ((unsigned char)0x1F)
|
||||
#define ADC_Channel_16 ((unsigned char)0x10)
|
||||
#define ADC_Channel_17 ((unsigned char)0x11)
|
||||
|
||||
#define IS_ADC_CHANNEL(CHANNEL) (((CHANNEL) == ADC_Channel_0) || ((CHANNEL) == ADC_Channel_1) || \
|
||||
((CHANNEL) == ADC_Channel_2) || ((CHANNEL) == ADC_Channel_3) || \
|
||||
((CHANNEL) == ADC_Channel_4) || ((CHANNEL) == ADC_Channel_5) || \
|
||||
((CHANNEL) == ADC_Channel_6) || ((CHANNEL) == ADC_Channel_7) || \
|
||||
((CHANNEL) == ADC_Channel_8) || ((CHANNEL) == ADC_Channel_9) || \
|
||||
((CHANNEL) == ADC_Channel_10) || ((CHANNEL) == ADC_Channel_11) || \
|
||||
((CHANNEL) == ADC_Channel_12) || ((CHANNEL) == ADC_Channel_13) || \
|
||||
((CHANNEL) == ADC_Channel_14) || ((CHANNEL) == ADC_Channel_15) || \
|
||||
((CHANNEL) == ADC_Channel_16) || ((CHANNEL) == ADC_Channel_17))
|
||||
|
||||
/* ADC sampling times --------------------------------------------------------*/
|
||||
#define ADC_SampleTime_1Cycles ((unsigned char)0x00)
|
||||
#define ADC_SampleTime_2Cycles ((unsigned char)0x01)
|
||||
#define ADC_SampleTime_4Cycles ((unsigned char)0x02)
|
||||
#define ADC_SampleTime_8Cycles ((unsigned char)0x03)
|
||||
#define ADC_SampleTime_16Cycles ((unsigned char)0x04)
|
||||
#define ADC_SampleTime_32Cycles ((unsigned char)0x05)
|
||||
#define ADC_SampleTime_64Cycles ((unsigned char)0x06)
|
||||
#define ADC_SampleTime_128Cycles ((unsigned char)0x07)
|
||||
|
||||
#define IS_ADC_SAMPLE_TIME(TIME) (((TIME) == ADC_SampleTime_1Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_2Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_4Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_8Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_16Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_32Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_64Cycles5) || \
|
||||
((TIME) == ADC_SampleTime_128Cycles5))
|
||||
|
||||
/* ADC extrenal trigger sources for injected channels conversion -------------*/
|
||||
/* For ADC1 and ADC2 */
|
||||
#define ADC_ExternalTrigInjecConv_T2_TRGO ((unsigned int)0x00002000)
|
||||
#define ADC_ExternalTrigInjecConv_T2_CC1 ((unsigned int)0x00003000)
|
||||
#define ADC_ExternalTrigInjecConv_T3_CC4 ((unsigned int)0x00004000)
|
||||
#define ADC_ExternalTrigInjecConv_T4_TRGO ((unsigned int)0x00005000)
|
||||
#define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((unsigned int)0x00006000)
|
||||
/* For ADC1, ADC2 and ADC3 */
|
||||
#define ADC_ExternalTrigInjecConv_T1_TRGO ((unsigned int)0x00000000)
|
||||
#define ADC_ExternalTrigInjecConv_T1_CC4 ((unsigned int)0x00001000)
|
||||
#define ADC_ExternalTrigInjecConv_None ((unsigned int)0x00007000)
|
||||
/* For ADC3 */
|
||||
#define ADC_ExternalTrigInjecConv_T4_CC3 ((unsigned int)0x00002000)
|
||||
#define ADC_ExternalTrigInjecConv_T8_CC2 ((unsigned int)0x00003000)
|
||||
#define ADC_ExternalTrigInjecConv_T8_CC4 ((unsigned int)0x00004000)
|
||||
#define ADC_ExternalTrigInjecConv_T5_TRGO ((unsigned int)0x00005000)
|
||||
#define ADC_ExternalTrigInjecConv_T5_CC4 ((unsigned int)0x00006000)
|
||||
|
||||
#define IS_ADC_EXT_INJEC_TRIG(INJTRIG) (((INJTRIG) == ADC_ExternalTrigInjecConv_T1_TRGO) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T1_CC4) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T2_TRGO) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T2_CC1) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T3_CC4) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T4_TRGO) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_None) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T4_CC3) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC2) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T8_CC4) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T5_TRGO) || \
|
||||
((INJTRIG) == ADC_ExternalTrigInjecConv_T5_CC4))
|
||||
|
||||
/* ADC injected channel selection --------------------------------------------*/
|
||||
#define ADC_InjectedChannel_1 ((unsigned char)0x14)
|
||||
#define ADC_InjectedChannel_2 ((unsigned char)0x18)
|
||||
#define ADC_InjectedChannel_3 ((unsigned char)0x1C)
|
||||
#define ADC_InjectedChannel_4 ((unsigned char)0x20)
|
||||
|
||||
#define IS_ADC_INJECTED_CHANNEL(CHANNEL) (((CHANNEL) == ADC_InjectedChannel_1) || \
|
||||
((CHANNEL) == ADC_InjectedChannel_2) || \
|
||||
((CHANNEL) == ADC_InjectedChannel_3) || \
|
||||
((CHANNEL) == ADC_InjectedChannel_4))
|
||||
|
||||
/* ADC analog watchdog selection ---------------------------------------------*/
|
||||
#define ADC_AnalogWatchdog_SingleRegEnable ((unsigned int)0x00800200)
|
||||
#define ADC_AnalogWatchdog_SingleInjecEnable ((unsigned int)0x00400200)
|
||||
#define ADC_AnalogWatchdog_SingleRegOrInjecEnable ((unsigned int)0x00C00200)
|
||||
#define ADC_AnalogWatchdog_AllRegEnable ((unsigned int)0x00800000)
|
||||
#define ADC_AnalogWatchdog_AllInjecEnable ((unsigned int)0x00400000)
|
||||
#define ADC_AnalogWatchdog_AllRegAllInjecEnable ((unsigned int)0x00C00000)
|
||||
#define ADC_AnalogWatchdog_None ((unsigned int)0x00000000)
|
||||
|
||||
#define IS_ADC_ANALOG_WATCHDOG(WATCHDOG) (((WATCHDOG) == ADC_AnalogWatchdog_SingleRegEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_SingleInjecEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_SingleRegOrInjecEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_AllRegEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_AllInjecEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_AllRegAllInjecEnable) || \
|
||||
((WATCHDOG) == ADC_AnalogWatchdog_None))
|
||||
|
||||
/* ADC interrupts definition -------------------------------------------------*/
|
||||
#define ADC_IT_EOC ((unsigned short)0x0220)
|
||||
#define ADC_IT_AWD ((unsigned short)0x0140)
|
||||
#define ADC_IT_JEOC ((unsigned short)0x0480)
|
||||
|
||||
#define IS_ADC_IT(IT) ((((IT) & (unsigned short)0xF81F) == 0x00) && ((IT) != 0x00))
|
||||
#define IS_ADC_GET_IT(IT) (((IT) == ADC_IT_EOC) || ((IT) == ADC_IT_AWD) || \
|
||||
((IT) == ADC_IT_JEOC))
|
||||
|
||||
/* ADC flags definition ------------------------------------------------------*/
|
||||
#define ADC_FLAG_AWD ((unsigned char)0x01)
|
||||
#define ADC_FLAG_EOC ((unsigned char)0x02)
|
||||
#define ADC_FLAG_JEOC ((unsigned char)0x04)
|
||||
#define ADC_FLAG_JSTRT ((unsigned char)0x08)
|
||||
#define ADC_FLAG_STRT ((unsigned char)0x10)
|
||||
|
||||
#define IS_ADC_CLEAR_FLAG(FLAG) ((((FLAG) & (unsigned char)0xE0) == 0x00) && ((FLAG) != 0x00))
|
||||
#define IS_ADC_GET_FLAG(FLAG) (((FLAG) == ADC_FLAG_AWD) || ((FLAG) == ADC_FLAG_EOC) || \
|
||||
((FLAG) == ADC_FLAG_JEOC) || ((FLAG)== ADC_FLAG_JSTRT) || \
|
||||
((FLAG) == ADC_FLAG_STRT))
|
||||
|
||||
/* ADC thresholds ------------------------------------------------------------*/
|
||||
#define IS_ADC_THRESHOLD(THRESHOLD) ((THRESHOLD) <= 0xFFF)
|
||||
|
||||
/* ADC injected offset -------------------------------------------------------*/
|
||||
#define IS_ADC_OFFSET(OFFSET) ((OFFSET) <= 0xFFF)
|
||||
|
||||
/* ADC injected length -------------------------------------------------------*/
|
||||
#define IS_ADC_INJECTED_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x4))
|
||||
|
||||
/* ADC injected rank ---------------------------------------------------------*/
|
||||
#define IS_ADC_INJECTED_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x4))
|
||||
|
||||
/* ADC regular length --------------------------------------------------------*/
|
||||
#define IS_ADC_REGULAR_LENGTH(LENGTH) (((LENGTH) >= 0x1) && ((LENGTH) <= 0x10))
|
||||
|
||||
/* ADC regular rank ----------------------------------------------------------*/
|
||||
#define IS_ADC_REGULAR_RANK(RANK) (((RANK) >= 0x1) && ((RANK) <= 0x10))
|
||||
|
||||
/* ADC regular discontinuous mode number -------------------------------------*/
|
||||
#define IS_ADC_REGULAR_DISC_NUMBER(NUMBER) (((NUMBER) >= 0x1) && ((NUMBER) <= 0x8))
|
||||
|
||||
//Loongson Feature
|
||||
#define RCH0 ADC_Channel_0
|
||||
#define RCH1 ADC_Channel_1
|
||||
#define RCH2 ADC_Channel_2
|
||||
#define RCH3 ADC_Channel_3
|
||||
#define RCH4 ADC_Channel_4
|
||||
#define RCH5 ADC_Channel_5
|
||||
#define RCH6 ADC_Channel_6
|
||||
#define RCH7 ADC_Channel_7
|
||||
|
||||
//Loongson Feature
|
||||
#define JCH0 ADC_Channel_0
|
||||
#define JCH1 ADC_Channel_1
|
||||
#define JCH2 ADC_Channel_2
|
||||
#define JCH3 ADC_Channel_3
|
||||
|
||||
void adc_init(adc_reg_map* ADCx, adc_init_info* ADC_InitStruct);
|
||||
void adc_struct_init(adc_init_info* ADC_InitStruct);
|
||||
void ADC_RegularChannelConfig(adc_reg_map* ADCx, unsigned char ADC_Channel, unsigned char Rank, unsigned char ADC_SampleTime);
|
||||
void adc_dev_enable(adc_reg_map* ADCx, FunctionalState state);
|
||||
void adc_software_start_conv_trigger(adc_reg_map* ADCx, FunctionalState state, unsigned char J_channel);
|
||||
void adc_eoc_check_conv_end(adc_reg_map* ADCx);
|
||||
|
||||
#endif /* __LOONGSON_2K300_ADC_CORE_H__ */
|
||||
@@ -0,0 +1,185 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include "loongson-2k300-adc-core.h"
|
||||
|
||||
#define ADC_DATA_MASK 0x0FFF
|
||||
|
||||
#define ADC_MAX_VOLTAGE 1800
|
||||
#define ADC_SCALE 4096
|
||||
|
||||
struct ls2k300_adc_data {
|
||||
void __iomem *base;
|
||||
spinlock_t read_raw_lock; // 添加读取的自旋锁 扫描模式,单次读取时使用
|
||||
};
|
||||
|
||||
// 这里是有节点可以读取的关键
|
||||
#define LS2K300_CHANNEL(num) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = ADC_DR, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 12, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ls2k300_adc_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
LS2K300_CHANNEL(0),
|
||||
LS2K300_CHANNEL(1),
|
||||
LS2K300_CHANNEL(2),
|
||||
LS2K300_CHANNEL(3),
|
||||
LS2K300_CHANNEL(4),
|
||||
LS2K300_CHANNEL(5),
|
||||
LS2K300_CHANNEL(6),
|
||||
LS2K300_CHANNEL(7),
|
||||
// 注入通道
|
||||
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 0, .address = ADC_JDR1 },
|
||||
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 1, .address = ADC_JDR2 },
|
||||
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 2, .address = ADC_JDR3 },
|
||||
// { .type = IIO_VOLTAGE, .differential = 1, .channel = 3, .address = ADC_JDR4 },
|
||||
};
|
||||
|
||||
// sysfs 下的节点读取时的具体调用函数
|
||||
static int ls2k300_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ls2k300_adc_data *data = iio_priv(indio_dev);
|
||||
unsigned int reg_offset;
|
||||
unsigned long flags;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
// 针对 in_voltagex_raw 节点
|
||||
if (chan->differential) {
|
||||
reg_offset = chan->address;
|
||||
} else {
|
||||
reg_offset = chan->address;
|
||||
}
|
||||
|
||||
// 由于规则通道组只有一个通道,所以这里需要用锁来限定一段时间只能读取一个通道的值。
|
||||
spin_lock_irqsave(&data->read_raw_lock, flags);
|
||||
|
||||
ADC_RegularChannelConfig(data->base, chan->channel, 1, ADC_SampleTime_64Cycles);
|
||||
adc_software_start_conv_trigger(data->base, ENABLE, 0);
|
||||
adc_eoc_check_conv_end(data->base);
|
||||
*val = ioread16(data->base + reg_offset) & ADC_DATA_MASK;
|
||||
|
||||
spin_unlock_irqrestore(&data->read_raw_lock, flags);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
// 针对 in_voltagex_scale 节点
|
||||
*val = ADC_MAX_VOLTAGE;
|
||||
*val2 = ADC_SCALE;
|
||||
// 返回的值就是 val1 / val2 的那个小数值
|
||||
// scale * raw 就是对应的电压值,单位mV
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ls2k300_adc_info = {
|
||||
.read_raw = ls2k300_adc_read_raw,
|
||||
};
|
||||
|
||||
static int ls2k300_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
adc_init_info adc_init_struct;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ls2k300_adc_data *data;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "devm_iio_device_alloc failed! probe failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->base)) {
|
||||
dev_err(&pdev->dev, "devm_ioremap_resource failed! probe failed! (base: %.llx)\n", (unsigned long long)data->base);
|
||||
return PTR_ERR(data->base);
|
||||
}
|
||||
|
||||
// 配置 ADC CR1 和 CR2 等寄存器,具体配置根据需要调整
|
||||
// 不使用 DMA, 扫描模式, 单次,规则序列转化数为1, 也就是每次都等待读取完毕。
|
||||
// EOC中断和JEOC中断没开
|
||||
adc_struct_init(&adc_init_struct);
|
||||
adc_init_struct.ADC_Mode = ADC_Mode_Independent;
|
||||
adc_init_struct.ADC_ScanConvMode = ENABLE;
|
||||
adc_init_struct.ADC_ContinuousConvMode = DISABLE;
|
||||
adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||
adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
adc_init_struct.ADC_NbrOfChannel = 1; //规则通道数量 为 1,这是一个一个的触发 扫描模式的单次扫描
|
||||
adc_init_struct.ADC_ClkDivider = 1; //Loongson Feature
|
||||
adc_init_struct.ADC_JTrigMod = 0; //Loongson Feature
|
||||
adc_init_struct.ADC_ADCEdge = 0; //Loongson Feature
|
||||
adc_init_struct.ADC_DiffMod = 0; //Loongson Feature
|
||||
adc_init(data->base, &adc_init_struct);
|
||||
|
||||
adc_dev_enable(data->base, ENABLE);
|
||||
|
||||
// iio 框架信息注册
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &ls2k300_adc_info;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ls2k300_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ls2k300_adc_channels);
|
||||
|
||||
spin_lock_init(&data->read_raw_lock); // 初始化自旋锁
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "iio_device_register failed! probe failed! (ret: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
dev_info(&pdev->dev, "ADC Device registered successfully\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls2k300_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
iio_device_unregister(indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ls2k300_adc_of_match[] = {
|
||||
{ .compatible = "ls2k300-adc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls2k300_adc_of_match);
|
||||
|
||||
static struct platform_driver ls2k300_adc_driver = {
|
||||
.driver = {
|
||||
.name = "ls2k300_adc",
|
||||
.of_match_table = ls2k300_adc_of_match,
|
||||
},
|
||||
.probe = ls2k300_adc_probe,
|
||||
.remove = ls2k300_adc_remove,
|
||||
};
|
||||
module_platform_driver(ls2k300_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("oujintao qujintao@loongson.cn");
|
||||
MODULE_DESCRIPTION("2k300 ADC IIO driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,386 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* =====================================================================================
|
||||
*
|
||||
* Filename: irq.c
|
||||
*
|
||||
* Description: irq handle
|
||||
*
|
||||
* Version: 1.0
|
||||
* Revision: none
|
||||
* Compiler: gcc
|
||||
*
|
||||
* Company: Loongson Corp.
|
||||
*
|
||||
* =====================================================================================
|
||||
*/
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define LS2K_INTISR_REG(i) (priv->regbase + 0x20 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTEN_REG(i) (priv->regbase + 0x24 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTENSET_REG(i) (priv->regbase + 0x28 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTENCLR_REG(i) (priv->regbase + 0x2c + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTPOL_REG(i) (priv->regbase + 0x30 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTEDGE_REG(i) (priv->regbase + 0x34 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_IRQ_ROUTE_REG(i) (priv->regbase + 0x00 + (((i) > 31) ? 0x40 : 0) + ((i) % 32))
|
||||
#define LS2K_COREISR_REG0(i) (priv->isrreg + (i) * 0x100)
|
||||
#define LS2K_COREISR_REG1(i) (priv->isrreg + 8 + (i) * 0x100)
|
||||
/* 2k1000 */
|
||||
#define LS2K_BOUNCE_REG(i) (priv->regbase + 0x38 + (((i) > 31) ? 0x40 : 0))
|
||||
#define LS2K_INTAUTO_REG(i) (priv->regbase + 0x3c + (((i) > 31) ? 0x40 : 0))
|
||||
|
||||
struct iointc {
|
||||
struct irq_domain *domain;
|
||||
int cascade;
|
||||
void *regbase;
|
||||
void *isrreg;
|
||||
raw_spinlock_t lock;
|
||||
};
|
||||
|
||||
static void mask_ls2k_iointc_irq(struct irq_data *data)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)data->domain->host_data;
|
||||
unsigned long hwirq = data->hwirq;
|
||||
void *reg = LS2K_INTENCLR_REG(hwirq);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
writel(1<<(hwirq%32), (void *)reg);
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void unmask_ls2k_iointc_irq(struct irq_data *data)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)data->domain->host_data;
|
||||
unsigned long hwirq = data->hwirq;
|
||||
void *reg = LS2K_INTENSET_REG(hwirq);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
writel(1<<(hwirq % 32), (void *)reg);
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int ls2k_iointc_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)d->domain->host_data;
|
||||
unsigned long hwirq = d->hwirq;
|
||||
unsigned long flags;
|
||||
unsigned int pol_val, edge_val;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
pol_val = readl((void *)LS2K_INTPOL_REG(hwirq));
|
||||
edge_val = readl((void *)LS2K_INTEDGE_REG(hwirq));
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
edge_val |= (1<<(hwirq%32));
|
||||
pol_val &= ~(1<<(hwirq%32));
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
edge_val |= (1<<(hwirq%32));
|
||||
pol_val |= (1<<(hwirq%32));
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
edge_val &= ~(1<<(hwirq%32));
|
||||
pol_val |= (1<<(hwirq%32));
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
edge_val &= ~(1<<(hwirq%32));
|
||||
pol_val &= ~(1<<(hwirq%32));
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
pr_warn("Not support both_edge interrupt,\
|
||||
but requester can simulate it\n");
|
||||
break;
|
||||
case IRQ_TYPE_NONE:
|
||||
default:
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(pol_val, (void *)LS2K_INTPOL_REG(hwirq));
|
||||
writel(edge_val, (void *)LS2K_INTEDGE_REG(hwirq));
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls2k_iointc_irq_ack(struct irq_data *d)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)d->domain->host_data;
|
||||
unsigned long hwirq = d->hwirq;
|
||||
void *reg;
|
||||
unsigned long flags;
|
||||
unsigned int reg_val;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
reg = LS2K_INTEN_REG(hwirq);
|
||||
reg_val = readl((void *)reg);
|
||||
|
||||
reg = LS2K_INTENCLR_REG(hwirq);
|
||||
writel(1 << (hwirq%32), (void *)reg);
|
||||
|
||||
if (reg_val & (1 << (hwirq%32)))
|
||||
reg = LS2K_INTENSET_REG(hwirq);
|
||||
else
|
||||
reg = LS2K_INTENCLR_REG(hwirq);
|
||||
writel(1 << (hwirq%32), (void *)reg);
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int ls2k_iointc_irq_domain_xlate(struct irq_domain *d, struct device_node *node,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
unsigned long *out_hwirq, unsigned int *out_type)
|
||||
{
|
||||
if (WARN_ON(intsize < 1))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = intspec[0];
|
||||
if (intsize > 1)
|
||||
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
|
||||
else
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls2k500_iointc_set_irq_route(struct iointc *priv, int irq, struct cpumask *core_mask, int line)
|
||||
{
|
||||
|
||||
if (irq > 63) {
|
||||
printk(KERN_ERR "%s: interrupt number error\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
writeb((1 << (line + 4)), (void *)LS2K_IRQ_ROUTE_REG(irq));
|
||||
}
|
||||
|
||||
static int ls2k500_iointc_set_affinity(struct irq_data *d, const struct cpumask *affinity,
|
||||
bool force)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)d->domain->host_data;
|
||||
unsigned long hwirq = d->hwirq;
|
||||
cpumask_t tmask;
|
||||
unsigned long *mask;
|
||||
unsigned long flags;
|
||||
unsigned long irqline_parent = irq_get_irq_data(priv->cascade)->hwirq;
|
||||
|
||||
cpumask_and(&tmask, affinity, cpu_online_mask);
|
||||
cpumask_copy(d->common->affinity, &tmask);
|
||||
|
||||
mask = cpumask_bits(&tmask);
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
ls2k500_iointc_set_irq_route(priv, hwirq, &tmask, irqline_parent - 2 /* 2 soft IP */);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
irq_data_update_effective_affinity(d, &tmask);
|
||||
|
||||
return IRQ_SET_MASK_OK_NOCOPY;
|
||||
}
|
||||
|
||||
static struct irq_chip ls2k500_iointc = {
|
||||
.name = "2k-iointc",
|
||||
.irq_mask = mask_ls2k_iointc_irq,
|
||||
.irq_unmask = unmask_ls2k_iointc_irq,
|
||||
.irq_set_affinity = ls2k500_iointc_set_affinity,
|
||||
.irq_ack = ls2k_iointc_irq_ack,
|
||||
.irq_set_type = ls2k_iointc_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static void ls2k500_iointc_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct iointc *priv = irq_desc_get_handler_data(desc);
|
||||
unsigned long pending;
|
||||
u32 reg_val;
|
||||
int irq;
|
||||
unsigned int virq;
|
||||
|
||||
pending = readl((void *)LS2K_COREISR_REG0(0));
|
||||
reg_val = readl((void *)LS2K_COREISR_REG1(0));
|
||||
pending |= (unsigned long)reg_val << 32;
|
||||
|
||||
if (!pending)
|
||||
spurious_interrupt();
|
||||
|
||||
while (pending) {
|
||||
irq = fls64(pending) - 1;
|
||||
virq = irq_linear_revmap(priv->domain, irq);
|
||||
generic_handle_irq(virq);
|
||||
pending &= ~BIT(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int ls2k500_iointc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ls2k500_iointc, handle_level_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops ls2k500_iointc_irq_domain_ops = {
|
||||
.map = ls2k500_iointc_map,
|
||||
.xlate = ls2k_iointc_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static int ls2k500_iointc_irq_of_init(struct device_node *of_node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
u32 cascade;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct iointc *priv = kzalloc(sizeof(struct iointc), GFP_KERNEL);
|
||||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
priv->cascade = cascade = of_irq_get_byname(of_node, "cascade");
|
||||
priv->regbase = of_iomap(of_node, 0);
|
||||
priv->isrreg = of_iomap(of_node, 1);
|
||||
fwnode = of_node_to_fwnode(of_node);
|
||||
|
||||
priv->domain = irq_domain_create_linear(fwnode, 64,
|
||||
&ls2k500_iointc_irq_domain_ops, priv);
|
||||
|
||||
irq_set_chained_handler_and_data(cascade,
|
||||
ls2k500_iointc_irq_handler,
|
||||
priv);
|
||||
if (!priv->domain)
|
||||
panic("Failed to add irqdomain for ls2k io intc");
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_DECLARE(ls2k500_iointc, "loongson,2k500-iointc", ls2k500_iointc_irq_of_init);
|
||||
IRQCHIP_DECLARE(ls2k500_icu, "loongson,2k500-icu", ls2k500_iointc_irq_of_init);
|
||||
|
||||
static void ls2k1000_iointc_set_irq_route(struct iointc *priv, int irq, struct cpumask *core_mask, int line)
|
||||
{
|
||||
void *auto_reg = LS2K_INTAUTO_REG(irq);
|
||||
void *bounce_reg = LS2K_BOUNCE_REG(irq);
|
||||
unsigned long *mask;
|
||||
unsigned int weight;
|
||||
unsigned int reg_val;
|
||||
|
||||
if (irq > 63) {
|
||||
printk(KERN_ERR "%s: interrupt number error\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
mask = cpumask_bits(core_mask);
|
||||
writeb((1 << (line + 4)) | (*mask & 0xf), (void *)LS2K_IRQ_ROUTE_REG(irq));
|
||||
|
||||
reg_val = readl((void *)auto_reg);
|
||||
reg_val &= ~(1 << (irq % 32)); /* auto always set to 0 */
|
||||
writel(reg_val, (void *)auto_reg);
|
||||
|
||||
reg_val = readl((void *)bounce_reg);
|
||||
|
||||
weight = cpumask_weight(core_mask);
|
||||
if (weight > 1)
|
||||
reg_val |= (1 << (irq % 32));
|
||||
else
|
||||
reg_val &= ~(1 << (irq % 32));
|
||||
writel(reg_val, (void *)bounce_reg);
|
||||
}
|
||||
|
||||
static int ls2k1000_iointc_set_affinity(struct irq_data *d, const struct cpumask *affinity,
|
||||
bool force)
|
||||
{
|
||||
struct iointc *priv = (struct iointc *)d->domain->host_data;
|
||||
unsigned long hwirq = d->hwirq;
|
||||
cpumask_t tmask;
|
||||
unsigned long *mask;
|
||||
unsigned long flags;
|
||||
unsigned long irqline_parent = irq_get_irq_data(priv->cascade)->hwirq;
|
||||
|
||||
cpumask_and(&tmask, affinity, cpu_online_mask);
|
||||
cpumask_copy(d->common->affinity, &tmask);
|
||||
|
||||
mask = cpumask_bits(&tmask);
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
ls2k1000_iointc_set_irq_route(priv, hwirq, &tmask, irqline_parent - 2 /* 2 soft IP */);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
irq_data_update_effective_affinity(d, &tmask);
|
||||
|
||||
return IRQ_SET_MASK_OK_NOCOPY;
|
||||
}
|
||||
|
||||
static struct irq_chip ls2k1000_iointc = {
|
||||
.name = "2k-iointc",
|
||||
.irq_mask = mask_ls2k_iointc_irq,
|
||||
.irq_unmask = unmask_ls2k_iointc_irq,
|
||||
.irq_set_affinity = ls2k1000_iointc_set_affinity,
|
||||
.irq_ack = ls2k_iointc_irq_ack,
|
||||
.irq_set_type = ls2k_iointc_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static void ls2k1000_iointc_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct iointc *priv = irq_desc_get_handler_data(desc);
|
||||
unsigned long pending;
|
||||
int irq, virq;
|
||||
int core = cpu_logical_map(smp_processor_id());
|
||||
|
||||
pending = readl((void *)LS2K_COREISR_REG1(core));
|
||||
pending <<= 32;
|
||||
pending |= readl((void *)LS2K_COREISR_REG0(core));
|
||||
|
||||
if (!pending)
|
||||
spurious_interrupt();
|
||||
|
||||
while (pending) {
|
||||
irq = fls64(pending) - 1;
|
||||
virq = irq_linear_revmap(priv->domain, irq);
|
||||
generic_handle_irq(virq);
|
||||
pending &= ~BIT(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int ls2k1000_iointc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ls2k1000_iointc, handle_level_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops ls2k1000_iointc_irq_domain_ops = {
|
||||
.map = ls2k1000_iointc_map,
|
||||
.xlate = ls2k_iointc_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static int ls2k1000_iointc_irq_of_init(struct device_node *of_node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
u32 cascade;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct iointc *priv = kzalloc(sizeof(struct iointc), GFP_KERNEL);
|
||||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
priv->cascade = cascade = of_irq_get_byname(of_node, "cascade");
|
||||
priv->regbase = of_iomap(of_node, 0);
|
||||
priv->isrreg = of_iomap(of_node, 1);
|
||||
fwnode = of_node_to_fwnode(of_node);
|
||||
|
||||
priv->domain = irq_domain_create_linear(fwnode, 64,
|
||||
&ls2k1000_iointc_irq_domain_ops, priv);
|
||||
|
||||
irq_set_chained_handler_and_data(cascade,
|
||||
ls2k1000_iointc_irq_handler,
|
||||
priv);
|
||||
if (!priv->domain)
|
||||
panic("Failed to add irqdomain for ls2k io intc");
|
||||
|
||||
irq_set_default_host(priv->domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_DECLARE(ls2k1000_iointc, "loongson,2k1000-iointc", ls2k1000_iointc_irq_of_init);
|
||||
IRQCHIP_DECLARE(ls2k1000_icu, "loongson,2k1000-icu", ls2k1000_iointc_irq_of_init);
|
||||
@@ -1101,3 +1101,13 @@ config MMC_LITEX
|
||||
module will be called litex_mmc.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_LS2K
|
||||
tristate "loongson 2k SD/MMC Card Interface support"
|
||||
depends on LOONGARCH && LOONGSON2_APB_DMA
|
||||
help
|
||||
This selects a driver for the MCI interface found in
|
||||
loongson 2k CPU.If you have a board based on it and a MMC/SD
|
||||
slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@@ -104,6 +104,7 @@ cqhci-y += cqhci-core.o
|
||||
cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o
|
||||
obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o
|
||||
obj-$(CONFIG_MMC_LITEX) += litex_mmc.o
|
||||
obj-$(CONFIG_MMC_LS2K) += ls2kmci.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
CFLAGS-cb710-mmc += -DDEBUG
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* linux/drivers/mmc/ls2k_mci.h - Loongson ls2k MCI driver
|
||||
*
|
||||
* Copyright (C) loongson, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FCR_SDIO_H_
|
||||
#define FCR_SDIO_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define _REG(x) ((volatile u32 *)(x))
|
||||
#define _REG2(b, o) ((volatile u32 *)((b)+(o)))
|
||||
|
||||
/*
|
||||
* sdio_controler
|
||||
*/
|
||||
#ifndef CONFIG_LOONGARCH
|
||||
#define LS2K1000_SDIO_REG_BASE 0x1fe0c000
|
||||
#define LS2K1000_GPIO_INTEN_BASE TO_UNCACHE(0x1fe10530)
|
||||
#else
|
||||
#define LS2K1000_SDIO_REG_BASE 0x1fe2c000
|
||||
#define LS2K1000_GPIO_INTEN_BASE TO_UNCACHE(0x1fe00530)
|
||||
#endif
|
||||
|
||||
#define LS2K0500_SDIO0_REG_BASE 0x1ff64000
|
||||
#define LS2K0500_SDIO0_GPIO_INTEN_BASE TO_UNCACHE(0x1fe104e4) /* GPIO32~63 */
|
||||
#define LS2K0500_SDIO1_REG_BASE 0x1ff66000
|
||||
#define LS2K0500_SDIO1_GPIO_INTEN_BASE TO_UNCACHE(0x1fe104e8) /* GPIO64~95 */
|
||||
|
||||
#define SDIO_DEV_ADDR (host->base+0x40)
|
||||
#define WDMA_OFFSET 0x400
|
||||
#define RDMA_OFFSET 0x800
|
||||
|
||||
#define SDICON 0x00
|
||||
#define SDIPRE 0x04
|
||||
#define SDICMDARG 0x08
|
||||
#define SDICMDCON 0x0c
|
||||
#define SDICMDSTA 0x10
|
||||
#define SDIRSP0 0x14
|
||||
#define SDIRSP1 0x18
|
||||
#define SDIRSP2 0x1C
|
||||
#define SDIRSP3 0x20
|
||||
#define SDIDTIMER 0x24
|
||||
#define SDIBSIZE 0x28
|
||||
#define SDIDATCON 0x2C
|
||||
#define SDIDATCNT 0x30
|
||||
#define SDIDSTA 0x34
|
||||
#define SDIFSTA 0x38
|
||||
#define SDIINTMSK 0x3C
|
||||
#define SDIWRDAT 0x40
|
||||
#define SDISTAADD0 0x44
|
||||
#define SDISTAADD1 0x48
|
||||
#define SDISTAADD2 0x4c
|
||||
#define SDISTAADD3 0x50
|
||||
#define SDISTAADD4 0x54
|
||||
#define SDISTAADD5 0x58
|
||||
#define SDISTAADD6 0x5c
|
||||
#define SDISTAADD7 0x60
|
||||
#define SDIINTEN 0x64
|
||||
|
||||
#define DAT_4_WIRE 1
|
||||
#define ERASE_START_ADDR 0x5000
|
||||
#define ERASE_End_ADDR 0x5000
|
||||
|
||||
#define SDICMDSTAT (0x10)
|
||||
#define SDITIMER (0x24)
|
||||
#define SDIDCON (0x2C)
|
||||
#define SDIDCNT (0x30)
|
||||
|
||||
#define SDIDATA (0x3C)
|
||||
#define SDIIMSK (0x40)
|
||||
|
||||
#define SDICON_RESET (1<<8)
|
||||
#define SDICON_MMCCLOCK (1<<5)
|
||||
#define SDICON_BYTEORDER (1<<4)
|
||||
#define SDICON_SDIOIRQ (1<<3)
|
||||
#define SDICON_RWAITEN (1<<2)
|
||||
#define SDICON_FIFORESET (1<<1)
|
||||
#define SDICON_CLOCKTYPE (1<<0)
|
||||
|
||||
#define SDIPRE_REVCLOCK (1<<31)
|
||||
|
||||
#define SDICMDCON_CMD6DATA (1<<18)
|
||||
#define SDICMDCON_ABORT (1<<12)
|
||||
#define SDICMDCON_WITHDATA (1<<11)
|
||||
#define SDICMDCON_LONGRSP (1<<10)
|
||||
#define SDICMDCON_WAITRSP (1<<9)
|
||||
#define SDICMDCON_CMDSTART (1<<8)
|
||||
#define SDICMDCON_SENDERHOST (1<<6)
|
||||
#define SDICMDCON_INDEX (0x3f)
|
||||
|
||||
#define SDICMDSTAT_CRCFAIL (1<<12)
|
||||
#define SDICMDSTAT_CMDSENT (1<<11)
|
||||
#define SDICMDSTAT_CMDTIMEOUT (1<<10)
|
||||
#define SDICMDSTAT_RSPFIN (1<<9)
|
||||
#define SDICMDSTAT_XFERING (1<<8)
|
||||
#define SDICMDSTAT_INDEX (0xff)
|
||||
|
||||
#define SDIDCON_8BIT_BUS (1<<26)
|
||||
#define SDIDCON_IRQPERIOD (1<<21)
|
||||
#define SDIDCON_TXAFTERRESP (1<<20)
|
||||
#define SDIDCON_RXAFTERCMD (1<<19)
|
||||
#define SDIDCON_BUSYAFTERCMD (1<<18)
|
||||
#define SDIDCON_BLOCKMODE (1<<17)
|
||||
#define SDIDCON_4BIT_BUS (1<<16)
|
||||
#define MISC_CTRL_SDIO_DMAEN (1<<15)
|
||||
#define SDIDCON_STOP (1<<14)
|
||||
#define SDIDCON_DATMODE (3<<12)
|
||||
#define SDIDCON_BLKNUM (0x7ff)
|
||||
|
||||
/* constants for SDIDCON_DATMODE */
|
||||
#define SDIDCON_XFER_READY (0<<12)
|
||||
#define SDIDCON_XFER_CHKSTART (1<<12)
|
||||
#define SDIDCON_XFER_RXSTART (2<<12)
|
||||
#define SDIDCON_XFER_TXSTART (3<<12)
|
||||
|
||||
#define SDIDCON_BLKNUM_MASK (0xFFF)
|
||||
#define SDIDCNT_BLKNUM_SHIFT (12)
|
||||
|
||||
#define SDIDSTA_RDYWAITREQ (1<<10)
|
||||
#define SDIDSTA_SDIOIRQDETECT (1<<9)
|
||||
#define SDIDSTA_FIFOFAIL (1<<8)
|
||||
#define SDIDSTA_CRCFAIL (1<<7)
|
||||
#define SDIDSTA_RXCRCFAIL (1<<6)
|
||||
#define SDIDSTA_DATATIMEOUT (1<<5)
|
||||
#define SDIDSTA_XFERFINISH (1<<4)
|
||||
#define SDIDSTA_BUSYFINISH (1<<3)
|
||||
#define SDIDSTA_SBITERR (1<<2)
|
||||
#define SDIDSTA_TXDATAON (1<<1)
|
||||
#define SDIDSTA_RXDATAON (1<<0)
|
||||
|
||||
#define SDIFSTA_TFDET (1<<13)
|
||||
#define SDIFSTA_RFDET (1<<12)
|
||||
#define SDIFSTA_TFFULL (1<<11)
|
||||
#define SDIFSTA_TFEMPTY (1<<10)
|
||||
#define SDIFSTA_RFLAST (1<<9)
|
||||
#define SDIFSTA_RFFULL (1<<8)
|
||||
#define SDIFSTA_RFEMPTY (1<<7)
|
||||
#define SDIFSTA_COUNTMASK (0x7f)
|
||||
|
||||
#define SDIIMSK_RESPONSEND (1<<14)
|
||||
#define SDIIMSK_READWAIT (1<<13)
|
||||
#define SDIIMSK_SDIOIRQ (1<<12)
|
||||
#define SDIIMSK_FIFOFAIL (1<<11)
|
||||
|
||||
|
||||
#define SDIIMSK_RESPONSECRC (1<<8)
|
||||
#define SDIIMSK_CMDTIMEOUT (1<<7)
|
||||
#define SDIIMSK_CMDSENT (1<<6)
|
||||
#define SDIIMSK_PROGERR (1<<4)
|
||||
#define SDIIMSK_TXCRCFAIL (1<<3)
|
||||
#define SDIIMSK_RXCRCFAIL (1<<2)
|
||||
#define SDIIMSK_DATATIMEOUT (1<<1)
|
||||
#define SDIIMSK_DATAFINISH (1<<0)
|
||||
|
||||
#define SDIIMSK_BUSYFINISH (1<<6)
|
||||
#define SDIIMSK_SBITERR (1<<5)
|
||||
#define SDIIMSK_TXFIFOHALF (1<<4)
|
||||
#define SDIIMSK_TXFIFOEMPTY (1<<3)
|
||||
#define SDIIMSK_RXFIFOLAST (1<<2)
|
||||
#define SDIIMSK_RXFIFOFULL (1<<1)
|
||||
#define SDIIMSK_RXFIFOHALF (1<<0)
|
||||
|
||||
#define SDIINT_CLEARALL 0x1ff
|
||||
#define SDIINT_ENALL 0x1ff
|
||||
|
||||
#define nr_strtol strtoul
|
||||
|
||||
enum ls2k_mci_waitfor {
|
||||
COMPLETION_NONE,
|
||||
COMPLETION_FINALIZE,
|
||||
COMPLETION_CMDSENT,
|
||||
COMPLETION_RSPFIN,
|
||||
COMPLETION_XFERFINISH,
|
||||
COMPLETION_XFERFINISH_RSPFIN,
|
||||
};
|
||||
|
||||
typedef struct ls2k_dma_desc{
|
||||
volatile u32 order_addr;
|
||||
volatile u32 saddr;
|
||||
volatile u32 daddr;
|
||||
volatile u32 length;
|
||||
volatile u32 step_length;
|
||||
volatile u32 step_times;
|
||||
volatile u32 cmd;
|
||||
volatile u32 dummy;
|
||||
volatile u32 order_addr_hi;
|
||||
volatile u32 saddr_hi;
|
||||
} ls2k_dma_desc;
|
||||
|
||||
struct ls2k_mci_host {
|
||||
struct platform_device *pdev;
|
||||
struct ls2k_mci_pdata *pdata;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *mem;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
dma_addr_t phys_base;
|
||||
void __iomem *wdma_order_reg;
|
||||
void __iomem *rdma_order_reg;
|
||||
int irq;
|
||||
int irq_cd;
|
||||
int dma;
|
||||
unsigned long dma_send_vir_mem;
|
||||
unsigned long dma_send_phy_mem;
|
||||
unsigned long dma_rec_vir_mem;
|
||||
unsigned long dma_rec_phy_mem;
|
||||
struct ls2k_dma_desc *sg_cpu;
|
||||
dma_addr_t sg_dma;
|
||||
|
||||
unsigned long clk_rate;
|
||||
unsigned long clk_div;
|
||||
unsigned long real_rate;
|
||||
u8 prescaler;
|
||||
|
||||
int is2440;
|
||||
unsigned sdiimsk;
|
||||
unsigned sdidata;
|
||||
int dodma;
|
||||
int dmatogo;
|
||||
|
||||
bool irq_disabled;
|
||||
bool irq_enabled;
|
||||
bool irq_state;
|
||||
int sdio_irqen;
|
||||
|
||||
struct mmc_request *mrq;
|
||||
int cmd_is_stop;
|
||||
|
||||
spinlock_t complete_lock;
|
||||
enum ls2k_mci_waitfor complete_what;
|
||||
|
||||
int dma_complete;
|
||||
|
||||
u32 pio_sgptr;
|
||||
u32 pio_bytes;
|
||||
u32 pio_count;
|
||||
u32 *pio_ptr;
|
||||
#define XFER_NONE 0
|
||||
#define XFER_READ 1
|
||||
#define XFER_WRITE 2
|
||||
u32 pio_active;
|
||||
|
||||
int bus_width;
|
||||
|
||||
char dbgmsg_cmd[301];
|
||||
char dbgmsg_dat[301];
|
||||
char *status;
|
||||
|
||||
unsigned int ccnt, dcnt;
|
||||
struct tasklet_struct pio_tasklet;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_state;
|
||||
struct dentry *debug_regs;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
struct dma_chan *chan;
|
||||
unsigned int app_cmd;
|
||||
};
|
||||
|
||||
struct ls2k_mci_pdata {
|
||||
unsigned int no_wprotect : 1;
|
||||
unsigned int no_detect : 1;
|
||||
unsigned int wprotect_invert : 1;
|
||||
unsigned int detect_invert : 1; /* set => detect active high. */
|
||||
unsigned int use_dma : 1;
|
||||
|
||||
unsigned int gpio_detect;
|
||||
unsigned int gpio_wprotect;
|
||||
#define LOONGSON_SDIO_EMMC_VER_1_0 0x100
|
||||
#define LOONGSON_SDIO_EMMC_VER_1_1 0x110
|
||||
#define LOONGSON_SDIO_EMMC_VER_1_2 0x120
|
||||
unsigned int version;
|
||||
unsigned long ocr_avail;
|
||||
void (*set_power)(unsigned char power_mode,
|
||||
unsigned short vdd);
|
||||
void (*irq_fixup)(struct ls2k_mci_host *host,
|
||||
struct mmc_command *cmd);
|
||||
};
|
||||
#endif
|
||||
@@ -230,6 +230,7 @@ source "drivers/net/can/sja1000/Kconfig"
|
||||
source "drivers/net/can/softing/Kconfig"
|
||||
source "drivers/net/can/spi/Kconfig"
|
||||
source "drivers/net/can/usb/Kconfig"
|
||||
source "drivers/net/can/ls_canfd/Kconfig"
|
||||
|
||||
endif #CAN_NETLINK
|
||||
|
||||
|
||||
@@ -33,5 +33,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
||||
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
||||
obj-$(CONFIG_CAN_LSCANFD) += ls_canfd/
|
||||
|
||||
subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
menuconfig CAN_LSCANFD
|
||||
tristate "Support LSCAN devices"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
if CAN_LSCANFD
|
||||
|
||||
config CAN_LSCANFD_PLATFORM
|
||||
tristate "Generic Platform Bus based LSCAN driver"
|
||||
help
|
||||
This driver adds support for the 2K chips connected to
|
||||
the "platform bus" (Linux abstraction for directly to the
|
||||
processor attached devices). Which can be found on various
|
||||
boards from Phytec (http://www.phytec.de) like the PCM027,
|
||||
PCM038. It also provides the OpenFirmware "platform bus" found
|
||||
on embedded systems with OpenFirmware bindings, e.g. if you
|
||||
have a PowerPC based system you may want to enable this option.
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# LS CAN FD ip core Linux driver Makefile
|
||||
|
||||
# Invoked make without parameters for native build for current running kernel
|
||||
# Set KERNEL_VERSION variable to compile for specific installed kernel version
|
||||
# Exact location of Linux kernel build tree can be chosen by KDIR variable
|
||||
# If you build for different CPU architecture than native and you do not
|
||||
# use GNUmakefile trick to set CROSS_COMPILE and ARCH then set these to
|
||||
# matching values else you do not screw only LS CAN FD build but even
|
||||
# base kernel build
|
||||
|
||||
quiet_cmd_build_lscanfd_dma = GEN $@
|
||||
cmd_build_lscanfd_dma = cp $(obj)/lscanfd_dma.o_shipped $@
|
||||
|
||||
quiet_cmd_build_lscanfd_platform = GEN $@
|
||||
cmd_build_lscanfd_platform = cp $(obj)/lscanfd_platform.o_shipped $@
|
||||
|
||||
$(obj)/lscanfd_dma.o: FORCE
|
||||
$(call if_changed,build_lscanfd_dma)
|
||||
|
||||
$(obj)/lscanfd_platform.o: FORCE
|
||||
$(call if_changed,build_lscanfd_platform)
|
||||
|
||||
obj-$(CONFIG_CAN_LSCANFD) := lscanfd_dma.o
|
||||
obj-$(CONFIG_CAN_LSCANFD_PLATFORM) += lscanfd_platform.o
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -167,6 +167,12 @@
|
||||
#define YT8531_LDO_VOL_3V3 0x0
|
||||
#define YT8531_LDO_VOL_1V8 0x2
|
||||
|
||||
#define YT8531_LED_GEN_CFG 0xa00b
|
||||
#define YT8531_LED0_CFG 0xa00c
|
||||
#define YT8531_LED1_CFG 0xa00d
|
||||
#define YT8531_LED2_CFG 0xa00e
|
||||
#define YT8531_BLINK_CFG 0xa00f
|
||||
|
||||
/* 1b0 disable 1.9ns rxc clock delay *default*
|
||||
* 1b1 enable 1.9ns rxc clock delay
|
||||
*/
|
||||
@@ -1681,6 +1687,30 @@ err_restore_page:
|
||||
return phy_restore_page(phydev, old_page, ret);
|
||||
}
|
||||
|
||||
static int yt8531_led_init(struct phy_device *phydev)
|
||||
{
|
||||
u32 val;
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = phydev->mdio.dev.of_node;
|
||||
if (!of_node)
|
||||
return 0;
|
||||
|
||||
if (!of_property_read_u32(of_node, "motorcomm,led0_cfg", &val)) {
|
||||
val &= 0xffff;
|
||||
ytphy_write_ext(phydev, YT8531_LED0_CFG, val);
|
||||
}
|
||||
if (!of_property_read_u32(of_node, "motorcomm,led1_cfg", &val)) {
|
||||
val &= 0xffff;
|
||||
ytphy_write_ext(phydev, YT8531_LED1_CFG, val);
|
||||
}
|
||||
if (!of_property_read_u32(of_node, "motorcomm,led2_cfg", &val)) {
|
||||
val &= 0xffff;
|
||||
ytphy_write_ext(phydev, YT8531_LED2_CFG, val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yt8531_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct device_node *node = phydev->mdio.dev.of_node;
|
||||
@@ -1708,6 +1738,8 @@ static int yt8531_config_init(struct phy_device *phydev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
yt8531_led_init(phydev);
|
||||
|
||||
ret = yt8531_set_ds(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -757,4 +757,13 @@ config PWM_XILINX
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-xilinx.
|
||||
|
||||
config PWM_LS
|
||||
tristate "Loongson PWM support"
|
||||
depends on LOONGSON_2K500 || LOONGSON_2K1000 || LOONGSON_2K300 || LOONGSON_2P500
|
||||
help
|
||||
Generic PWM framework driver for loongson3.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-ls.
|
||||
|
||||
endif
|
||||
|
||||
@@ -70,3 +70,4 @@ obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
|
||||
obj-$(CONFIG_PWM_VISCONTI) += pwm-visconti.o
|
||||
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
|
||||
obj-$(CONFIG_PWM_XILINX) += pwm-xilinx.o
|
||||
obj-$(CONFIG_PWM_LS) += pwm-ls.o
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
/* Loongson PWM registers */
|
||||
#define LOONGSON_PWM_REG_DUTY 0x4 /* Low Pulse Buffer Register */
|
||||
@@ -45,7 +46,7 @@
|
||||
#define LOONGSON_PWM_CTRL_DZONE BIT(10) /* Anti-dead Zone Enable Bit */
|
||||
|
||||
/* default input clk frequency for the ACPI case */
|
||||
#define LOONGSON_PWM_FREQ_DEFAULT 50000 /* Hz */
|
||||
#define LOONGSON_PWM_FREQ_DEFAULT 50000000 /* Hz */
|
||||
|
||||
struct pwm_loongson_suspend_store {
|
||||
u32 ctrl;
|
||||
@@ -86,10 +87,10 @@ static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *p
|
||||
|
||||
if (polarity == PWM_POLARITY_INVERSED)
|
||||
/* Duty cycle defines LOW period of PWM */
|
||||
val |= LOONGSON_PWM_CTRL_INVERT;
|
||||
val &= ~LOONGSON_PWM_CTRL_INVERT;
|
||||
else
|
||||
/* Duty cycle defines HIGH period of PWM */
|
||||
val &= ~LOONGSON_PWM_CTRL_INVERT;
|
||||
val |= LOONGSON_PWM_CTRL_INVERT;
|
||||
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
@@ -99,11 +100,31 @@ static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *p
|
||||
static void pwm_loongson_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
u32 val;
|
||||
u32 duty;
|
||||
u32 period;
|
||||
u32 period_1000ns;
|
||||
struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip);
|
||||
|
||||
duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY);
|
||||
period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD);
|
||||
|
||||
period_1000ns = mul_u64_u64_div_u64(1000, ddata->clk_rate, NSEC_PER_SEC);
|
||||
pwm_loongson_writel(ddata, 1, LOONGSON_PWM_REG_DUTY);
|
||||
pwm_loongson_writel(ddata, period_1000ns, LOONGSON_PWM_REG_PERIOD);
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
|
||||
val |= LOONGSON_PWM_CTRL_RST;
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
val ^= LOONGSON_PWM_CTRL_RST;
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
ndelay(1000);
|
||||
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
|
||||
val &= ~LOONGSON_PWM_CTRL_EN;
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
pwm_loongson_writel(ddata, duty, LOONGSON_PWM_REG_DUTY);
|
||||
pwm_loongson_writel(ddata, period, LOONGSON_PWM_REG_PERIOD);
|
||||
}
|
||||
|
||||
static int pwm_loongson_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
@@ -178,8 +199,8 @@ static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
/* duty & period have a max of 2^32, so we can't overflow */
|
||||
state->duty_cycle = DIV64_U64_ROUND_UP((u64)duty * NSEC_PER_SEC, ddata->clk_rate);
|
||||
state->period = DIV64_U64_ROUND_UP((u64)period * NSEC_PER_SEC, ddata->clk_rate);
|
||||
state->polarity = (ctrl & LOONGSON_PWM_CTRL_INVERT) ? PWM_POLARITY_INVERSED :
|
||||
PWM_POLARITY_NORMAL;
|
||||
state->polarity = (ctrl & LOONGSON_PWM_CTRL_INVERT) ? PWM_POLARITY_NORMAL :
|
||||
PWM_POLARITY_INVERSED;
|
||||
state->enabled = (ctrl & LOONGSON_PWM_CTRL_EN) ? true : false;
|
||||
|
||||
return 0;
|
||||
@@ -196,6 +217,8 @@ static int pwm_loongson_probe(struct platform_device *pdev)
|
||||
struct pwm_chip *chip;
|
||||
struct pwm_loongson_ddata *ddata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 of_clk_freq = 0;
|
||||
|
||||
chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata));
|
||||
if (IS_ERR(chip))
|
||||
@@ -210,6 +233,7 @@ static int pwm_loongson_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(ddata->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(ddata->clk),
|
||||
"failed to get pwm clock\n");
|
||||
ddata->clk_rate = LOONGSON_PWM_FREQ_DEFAULT;
|
||||
if (ddata->clk) {
|
||||
ret = devm_clk_rate_exclusive_get(dev, ddata->clk);
|
||||
if (ret)
|
||||
@@ -217,7 +241,10 @@ static int pwm_loongson_probe(struct platform_device *pdev)
|
||||
|
||||
ddata->clk_rate = clk_get_rate(ddata->clk);
|
||||
} else {
|
||||
ddata->clk_rate = LOONGSON_PWM_FREQ_DEFAULT;
|
||||
#ifdef CONFIG_OF
|
||||
if (!of_property_read_u32(np, "clock-frequency", &of_clk_freq))
|
||||
ddata->clk_rate = of_clk_freq;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Explicitly initialize the CTRL register */
|
||||
@@ -270,6 +297,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(pwm_loongson_pm_ops, pwm_loongson_suspend,
|
||||
|
||||
static const struct of_device_id pwm_loongson_of_ids[] = {
|
||||
{ .compatible = "loongson,ls7a-pwm" },
|
||||
{ .compatible = "loongson,ls2k-pwm" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pwm_loongson_of_ids);
|
||||
|
||||
@@ -102,6 +102,11 @@ static const struct loongson_rtc_config ls2k1000_rtc_config = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct loongson_rtc_config ls2k_gen_rtc_config = {
|
||||
.pm_offset = 0x0,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct regmap_config loongson_rtc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
@@ -323,8 +328,10 @@ static int loongson_rtc_probe(struct platform_device *pdev)
|
||||
"devm_rtc_allocate_device failed\n");
|
||||
|
||||
/* Get RTC alarm irq */
|
||||
/* 发现 2k300 不需要中断 开了中断就会导致无法读写 */
|
||||
/* 所以 pm_offset == 0 代表不需要中断 */
|
||||
alarm_irq = platform_get_irq(pdev, 0);
|
||||
if (alarm_irq > 0) {
|
||||
if (alarm_irq > 0 && priv->config->pm_offset) {
|
||||
ret = devm_request_irq(dev, alarm_irq, loongson_rtc_isr,
|
||||
0, "loongson-alarm", priv);
|
||||
if (ret < 0)
|
||||
@@ -372,6 +379,7 @@ static const struct of_device_id loongson_rtc_of_match[] = {
|
||||
{ .compatible = "loongson,ls1c-rtc", .data = &ls1c_rtc_config },
|
||||
{ .compatible = "loongson,ls7a-rtc", .data = &generic_rtc_config },
|
||||
{ .compatible = "loongson,ls2k1000-rtc", .data = &ls2k1000_rtc_config },
|
||||
{ .compatible = "loongson,ls2k-rtc", .data = &ls2k_gen_rtc_config },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, loongson_rtc_of_match);
|
||||
|
||||
@@ -1248,6 +1248,18 @@ config SPI_TLE62X0
|
||||
sysfs interface, with each line presented as a kind of GPIO
|
||||
exposing both switch control and diagnostic feedback.
|
||||
|
||||
config SPI_LS
|
||||
tristate "Loongson SPI Controller Support"
|
||||
depends on LOONGSON_2K1000 || LOONGSON_2K500 || LOONGSON_2K300 || LOONGSON_2P500
|
||||
help
|
||||
This is the driver for Loongson spi master controller.
|
||||
|
||||
config SPI_LS_IO
|
||||
tristate "Loongson SPI-IO Controller Support"
|
||||
depends on LOONGSON_2K300 || LOONGSON_2P500
|
||||
help
|
||||
This is the driver for Loongson spi-io controller.
|
||||
|
||||
#
|
||||
# Add new SPI protocol masters in alphabetical order above this line
|
||||
#
|
||||
|
||||
@@ -160,3 +160,5 @@ obj-$(CONFIG_SPI_AMD) += spi-amd.o
|
||||
# SPI slave protocol handlers
|
||||
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
|
||||
obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
|
||||
obj-$(CONFIG_SPI_LS) += spi-ls.o
|
||||
obj-$(CONFIG_SPI_LS_IO) += spi-ls-io.o
|
||||
|
||||
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* Loongson SPI driver
|
||||
*
|
||||
* Copyright (C) 2017 Juxin Gao <gaojuxin@loongson.cn>
|
||||
*
|
||||
* 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 <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
// #include <asm-generic/unaligned.h>
|
||||
// #include <linux/unaligned/le_byteshift.h>
|
||||
|
||||
/*SPI IO registers */
|
||||
#define REG_CR1 0x00
|
||||
#define REG_CR2 0x04
|
||||
#define REG_CR3 0x08
|
||||
#define REG_CR4 0x0c
|
||||
#define REG_IER 0x10
|
||||
#define REG_SR1 0x14
|
||||
#define REG_SR2 0x18
|
||||
#define REG_CFG1 0x20
|
||||
#define REG_CFG2 0x24
|
||||
#define REG_CFG3 0x28
|
||||
#define REG_CRC1 0x30
|
||||
#define REG_CRC2 0x34
|
||||
#define REG_DR 0x40
|
||||
#define REG_DMA_ISR 0x100
|
||||
#define REG_DMA_IFCR 0x104
|
||||
#define REG_DMA_CCR_RX 0x110
|
||||
#define REG_DMA_CNDTR_RX 0x114
|
||||
#define REG_DMA_CMAR_RX 0x11c
|
||||
#define REG_DMA_CCR_TX 0x120
|
||||
#define REG_DMA_CNDTR_TX 0x124
|
||||
#define REG_DMA_CMAR_TX 0x12c
|
||||
|
||||
#define SHIFTW(_x, _s) ((_x) << (_s))
|
||||
#define SHIFTR(_x, _s) ((_x) >> (_s))
|
||||
|
||||
/* SPI_CR1 bit fields */
|
||||
#define BIT_CR1_SPE BIT(0)
|
||||
#define BIT_CR1_CSTART BIT(1)
|
||||
#define BIT_CR1_AUTOSUS BIT(2)
|
||||
#define BIT_CR1_SSREV BIT(8)
|
||||
|
||||
/* SPI_CR2 bit fields */
|
||||
#define BIT_CR2_RXDMAEN BIT(7)
|
||||
#define BIT_CR2_TXDMAEN BIT(15)
|
||||
#define MASK_CR2_RXFTHLV 0x3
|
||||
#define MASK_CR2_TXFTHLV 0x300
|
||||
|
||||
/* SPI_IER bit fields */
|
||||
#define BIT_IER_RXAIE BIT(0)
|
||||
#define BIT_IER_TXAIE BIT(1)
|
||||
#define BIT_IER_DXAIE BIT(2)
|
||||
#define BIT_IER_RXEIE BIT(4)
|
||||
#define BIT_IER_TXEIE BIT(5)
|
||||
#define BIT_IER_SUSPIE BIT(7)
|
||||
#define BIT_IER_OVRIE BIT(8)
|
||||
#define BIT_IER_UDRIE BIT(9)
|
||||
#define BIT_IER_CRCEIE BIT(10)
|
||||
#define BIT_IER_MODFIE BIT(11)
|
||||
#define BIT_IER_EOTIE BIT(15)
|
||||
#define BIT_IER_ALL \
|
||||
(BIT_IER_RXAIE | BIT_IER_TXAIE | BIT_IER_DXAIE | BIT_IER_RXEIE | \
|
||||
BIT_IER_TXEIE | BIT_IER_SUSPIE | BIT_IER_OVRIE | BIT_IER_UDRIE | \
|
||||
BIT_IER_CRCEIE | BIT_IER_MODFIE | BIT_IER_EOTIE)
|
||||
|
||||
/* SPI_SR1 bit fields */
|
||||
#define BIT_SR1_RXA BIT(0)
|
||||
#define BIT_SR1_TXA BIT(1)
|
||||
#define BIT_SR1_DXA BIT(2)
|
||||
#define BIT_SR1_RXE BIT(4)
|
||||
#define BIT_SR1_TXE BIT(5)
|
||||
#define BIT_SR1_SUSP BIT(7)
|
||||
#define BIT_SR1_OVR BIT(8)
|
||||
#define BIT_SR1_UDR BIT(9)
|
||||
#define BIT_SR1_CRCE BIT(10)
|
||||
#define BIT_SR1_MODF BIT(11)
|
||||
#define BIT_SR1_EOT BIT(15)
|
||||
|
||||
/* SPI_SR2 bit fields */
|
||||
#define MASK_SR2_RXFLV 0x7 // << 0
|
||||
#define MASK_SR2_TXFLV 0x700 // << 8
|
||||
|
||||
/* SPI_CFG1 bit fields */
|
||||
#define BIT_CFG1_CPOL BIT(0)
|
||||
#define BIT_CFG1_CPHA BIT(1)
|
||||
#define BIT_CFG1_LSBFRST BIT(7)
|
||||
#define MASK_CFG1_DSIZE 0x1f00 // << 8
|
||||
|
||||
/* SPI_CFG2 bit fields */
|
||||
#define SHIFT_CFG2_BRINT 8
|
||||
#define MASK_CFG2_BRDEC 0xfc // << 2
|
||||
#define MASK_CFG2_BRINT 0xff00 // << 8
|
||||
|
||||
/* SPI_CFG3 bit fields */
|
||||
#define BIT_CFG3_MSTR BIT(0)
|
||||
#define BIT_CFG3_DIOSWP BIT(1)
|
||||
#define BIT_CFG3_DIE BIT(2)
|
||||
#define BIT_CFG3_DOE BIT(3)
|
||||
#define MASK_CFG3_SSMODE 0x300 // << 8
|
||||
|
||||
/* SPI_CRC1 bit fields */
|
||||
#define BIT_CRC1_CRCEN BIT(0)
|
||||
#define MASK_CRC1_CRCPOLY 0xfffffffe // << 1
|
||||
|
||||
/* SPI_CRC2 bit fields */
|
||||
#define BIT_CRC2_RCRCINI BIT(0)
|
||||
#define BIT_CRC2_TCRCINI BIT(1)
|
||||
#define MASK_CRC2_CRCSIZE 0x1f00 // << 8
|
||||
|
||||
#define ls_spiio_write_reg(spi, offset, data) \
|
||||
writel(data, spi->reg + offset)
|
||||
#define ls_spiio_read_reg(spi, offset) \
|
||||
readl(spi->reg + offset)
|
||||
|
||||
#define ls_spiio_write_reg_byte(spi, offset, data) \
|
||||
writeb(data, spi->reg + offset)
|
||||
#define ls_spiio_read_reg_byte(spi, offset) \
|
||||
readb(spi->reg + offset)
|
||||
|
||||
|
||||
struct ls_spiio_priv {
|
||||
void __iomem *reg;
|
||||
struct spi_controller *controller;
|
||||
u32 ref_clk;
|
||||
u32 clk;
|
||||
u8 dsize;
|
||||
struct gpio_desc **cs_gpios;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int ls_spiio_idle_sr1(struct ls_spiio_priv* priv, u32 bit)
|
||||
{
|
||||
int timeout = 1000;
|
||||
while(!(ls_spiio_read_reg(priv, REG_SR1) & bit)
|
||||
&& timeout--);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static int ls_spiio_active_cs(struct ls_spiio_priv* priv, int cs)
|
||||
{
|
||||
int active = 0;
|
||||
|
||||
if (!priv->cs_gpios)
|
||||
ls_spiio_write_reg(priv, REG_CR1, BIT_CR1_SSREV | BIT_CR1_SPE);
|
||||
else
|
||||
gpiod_set_raw_value(priv->cs_gpios[cs], active);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spiio_clear_cs(struct ls_spiio_priv* priv)
|
||||
{
|
||||
struct spi_controller* controller = priv->controller;
|
||||
int clear = 1;
|
||||
int i;
|
||||
|
||||
if (!priv->cs_gpios)
|
||||
ls_spiio_write_reg(priv, REG_CR1, BIT_CR1_SSREV);
|
||||
else
|
||||
for (i = 0; i < controller->num_chipselect; i++)
|
||||
gpiod_set_raw_value(priv->cs_gpios[i], clear);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spiio_free_cs_gpios(struct ls_spiio_priv* priv)
|
||||
{
|
||||
struct spi_controller* controller = priv->controller;
|
||||
struct device *dev = priv->dev;
|
||||
int i;
|
||||
|
||||
if (!priv->cs_gpios)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < controller->num_chipselect; i++)
|
||||
devm_gpiod_put(dev, priv->cs_gpios[i]);
|
||||
|
||||
devm_kfree(dev, priv->cs_gpios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spiio_init_cs_gpios(struct ls_spiio_priv* priv)
|
||||
{
|
||||
struct spi_controller* controller = priv->controller;
|
||||
struct device *dev = priv->dev;
|
||||
int clear = 1;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
priv->cs_gpios = devm_kcalloc(dev, controller->num_chipselect,
|
||||
sizeof(*priv->cs_gpios),
|
||||
GFP_KERNEL);
|
||||
if (!priv->cs_gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < controller->num_chipselect; i++) {
|
||||
priv->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(priv->cs_gpios[i]))
|
||||
{
|
||||
ret = PTR_ERR(priv->cs_gpios[i]);
|
||||
dev_err(dev, "Cannot Requst cs-gpio[%d]: %d\n",
|
||||
i, ret);
|
||||
goto free_cs_gpio_array;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev_info(dev, "GPIO[%d] as cs[%d]\n",
|
||||
desc_to_gpio(priv->cs_gpios[i]), i);
|
||||
gpiod_direction_output_raw(priv->cs_gpios[i], clear);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_cs_gpio_array:
|
||||
devm_kfree(dev, priv->cs_gpios);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ls_spiio_xfer(struct ls_spiio_priv* priv, u8* tx, u8* rx, int len)
|
||||
{
|
||||
int remain = len;
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
while (remain > 0)
|
||||
{
|
||||
|
||||
if (ls_spiio_idle_sr1(priv, BIT_SR1_TXA) < 0) {
|
||||
pr_debug("wait txa timeout!\n");
|
||||
return 0;
|
||||
}
|
||||
if (tx != NULL)
|
||||
{
|
||||
ls_spiio_write_reg_byte(priv, REG_DR, *tx);
|
||||
tx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ls_spiio_write_reg_byte(priv, REG_DR, 0x00);
|
||||
}
|
||||
|
||||
ls_spiio_write_reg(priv, REG_CR1,
|
||||
BIT_CR1_SPE | BIT_CR1_CSTART | BIT_CR1_AUTOSUS);
|
||||
if (ls_spiio_idle_sr1(priv, BIT_SR1_EOT) < 0) {
|
||||
pr_debug("wait eot timeout!\n");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ls_spiio_write_reg(priv, REG_SR1, BIT_SR1_EOT);
|
||||
}
|
||||
|
||||
if (ls_spiio_idle_sr1(priv, BIT_SR1_RXA) < 0) {
|
||||
pr_debug("wait rxa timeout!\n");
|
||||
return 0;
|
||||
}
|
||||
if (rx != NULL)
|
||||
{
|
||||
*rx = ls_spiio_read_reg_byte(priv, REG_DR);
|
||||
rx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ls_spiio_read_reg_byte(priv, REG_DR);
|
||||
}
|
||||
|
||||
remain--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ls_spiio_set_clk_timing(struct ls_spiio_priv* priv,
|
||||
unsigned long clk, u32 mode)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = 0;
|
||||
if (priv->clk != clk)
|
||||
{
|
||||
val= DIV_ROUND_UP(priv->ref_clk, clk);
|
||||
|
||||
if (val < 2)
|
||||
val = 2;
|
||||
|
||||
if (val > 255)
|
||||
val = 255;
|
||||
|
||||
pr_debug("refclk = %d, need = %ld, approximate = %d\n",
|
||||
priv->ref_clk, clk, priv->ref_clk / val);
|
||||
|
||||
priv->clk = clk;
|
||||
|
||||
ls_spiio_write_reg(priv, REG_CFG2, (val << 8));
|
||||
}
|
||||
|
||||
val = 0;
|
||||
if (mode & SPI_CPOL)
|
||||
val |= (1 << 0);
|
||||
if (mode & SPI_CPHA)
|
||||
val |= (1 << 1);
|
||||
if (mode & SPI_LSB_FIRST)
|
||||
val |= (1 << 7);
|
||||
val |= (priv->dsize << 8);
|
||||
|
||||
pr_debug("clk timing : 0x%x\n", val);
|
||||
ls_spiio_write_reg(priv, REG_CFG1, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spiio_init(struct ls_spiio_priv* priv)
|
||||
{
|
||||
/*pin set,controller mode and full duplex*/
|
||||
//MSTR:1 DIOSWP:0 DIE:1 DOE:1 SSMODE:0
|
||||
ls_spiio_write_reg(priv, REG_CFG3,
|
||||
BIT_CFG3_MSTR | BIT_CFG3_DIE | BIT_CFG3_DOE);
|
||||
/*set clk rate */
|
||||
//BRINT:0xff BRDEC:0
|
||||
ls_spiio_write_reg(priv, REG_CFG2,0xff00);
|
||||
/*set transfer format*/
|
||||
//CPOL:0 CPHA:0 LSBFRST:0 DSIZE:7
|
||||
ls_spiio_write_reg(priv, REG_CFG1, 0x700);
|
||||
// DR Enable
|
||||
ls_spiio_write_reg(priv, REG_CR1, BIT_CR1_SPE);
|
||||
|
||||
priv->dsize = 7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct ls_spiio_priv* priv;
|
||||
|
||||
priv = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
ls_spiio_set_clk_timing(priv, spi->max_speed_hz, spi->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct ls_spiio_priv* priv = spi_controller_get_devdata(spi->controller);
|
||||
struct spi_transfer *t = NULL;
|
||||
m->actual_length = 0;
|
||||
m->status = 0;
|
||||
|
||||
ls_spiio_active_cs(priv, spi->chip_select[0]);
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
/*setup spi clock*/
|
||||
ls_spiio_set_clk_timing(priv, t->speed_hz, spi->mode);
|
||||
m->actual_length += ls_spiio_xfer(priv,
|
||||
(u8*)t->tx_buf, (u8*)t->rx_buf, t->len);
|
||||
}
|
||||
ls_spiio_clear_cs(priv);
|
||||
|
||||
m->complete(m->context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drv_ls_spiio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ls_spiio_priv* priv;
|
||||
struct spi_controller* controller;
|
||||
u32 num_chipselect;
|
||||
int ret;
|
||||
|
||||
controller = spi_alloc_master(&pdev->dev, sizeof(*priv));
|
||||
|
||||
if (controller == NULL) {
|
||||
dev_dbg(&pdev->dev, "controller allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (pdev->id != -1)
|
||||
controller->bus_num = pdev->id;
|
||||
|
||||
controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||
controller->setup = __spi_setup;
|
||||
controller->transfer = __spi_transfer;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "num-chipselects",
|
||||
&num_chipselect))
|
||||
num_chipselect = 1;
|
||||
controller->num_chipselect = num_chipselect ? num_chipselect : 1;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
controller->dev.of_node = of_node_get(pdev->dev.of_node);
|
||||
#endif
|
||||
dev_set_drvdata(&pdev->dev, controller);
|
||||
|
||||
priv = spi_controller_get_devdata(controller);
|
||||
priv->controller = controller;
|
||||
priv->dev = dev;
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "cs-gpios", NULL))
|
||||
{
|
||||
ret = ls_spiio_init_cs_gpios(priv);
|
||||
if (ret)
|
||||
goto free_controller;
|
||||
}
|
||||
|
||||
priv->reg = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (priv->reg == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot map IO\n");
|
||||
ret = -ENXIO;
|
||||
goto try_free_cs_gpios;
|
||||
}
|
||||
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&priv->ref_clk))
|
||||
{
|
||||
dev_err(&pdev->dev, "Cannot get clock\n");
|
||||
goto try_free_cs_gpios;
|
||||
}
|
||||
|
||||
ls_spiio_init(priv);
|
||||
|
||||
ret = spi_register_controller(controller);
|
||||
if (ret)
|
||||
{
|
||||
dev_err(&pdev->dev, "spi register fail\n");
|
||||
goto unmap_io;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
unmap_io:
|
||||
devm_iounmap(&pdev->dev, priv->reg);
|
||||
try_free_cs_gpios:
|
||||
if (of_find_property(pdev->dev.of_node, "cs-gpios", NULL))
|
||||
ls_spiio_free_cs_gpios(priv);
|
||||
free_controller:
|
||||
spi_controller_put(controller);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id drvid_ls_spiio[] = {
|
||||
{ .compatible = "loongson,ls-spi-io", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, drvid_ls_spiio);
|
||||
#endif
|
||||
static struct platform_driver ls_spiio_driver = {
|
||||
.probe = drv_ls_spiio_probe,
|
||||
.driver = {
|
||||
.name = "ls-spi-io",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(drvid_ls_spiio),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init __drv_spi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&ls_spiio_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit __drv_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ls_spiio_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(__drv_spi_init);
|
||||
module_exit(__drv_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Niuyize <niuyize@loongson.cn>");
|
||||
MODULE_DESCRIPTION("Loongson SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Loongson SPI driver
|
||||
*
|
||||
* Copyright (C) 2017 Juxin Gao <gaojuxin@loongson.cn>
|
||||
*
|
||||
* 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 <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/*define spi register */
|
||||
#define SPCR 0x00
|
||||
#define SPSR 0x01
|
||||
#define FIFO 0x02
|
||||
#define SPER 0x03
|
||||
#define PARA 0x04
|
||||
#define SPCS 0x04
|
||||
#define SFCS 0x05
|
||||
#define TIMI 0x06
|
||||
|
||||
#define PARA_MEM_EN 0x01
|
||||
#define SPSR_SPIF 0x80
|
||||
#define SPSR_WCOL 0x40
|
||||
#define SPCR_SPE 0x40
|
||||
|
||||
extern unsigned long bus_clock;
|
||||
struct ls_spi {
|
||||
struct work_struct work;
|
||||
spinlock_t lock;
|
||||
|
||||
struct list_head msg_queue;
|
||||
struct spi_controller *controller;
|
||||
void __iomem *base;
|
||||
int cs_active;
|
||||
unsigned long clk;
|
||||
unsigned int hz;
|
||||
unsigned char spcr, sper, spsr;
|
||||
unsigned char para, sfcs, timi;
|
||||
struct workqueue_struct *wq;
|
||||
unsigned int mode;
|
||||
|
||||
unsigned int gpio_cs_mode;
|
||||
struct gpio_desc **cs_gpios;
|
||||
} *ls_spi_dev;
|
||||
|
||||
static inline int set_cs(struct ls_spi *ls_spi, struct spi_device *spi, int val);
|
||||
|
||||
static void ls_spi_write_reg(struct ls_spi *spi,
|
||||
unsigned char reg, unsigned char data)
|
||||
{
|
||||
writeb(data, spi->base +reg);
|
||||
}
|
||||
|
||||
static char ls_spi_read_reg(struct ls_spi *spi,
|
||||
unsigned char reg)
|
||||
{
|
||||
return readb(spi->base + reg);
|
||||
}
|
||||
|
||||
static int get_ls_spi_clk(struct device *dev, unsigned long* clk)
|
||||
{
|
||||
struct clk *clk_unit;
|
||||
if (!dev || !clk)
|
||||
return -1;
|
||||
|
||||
clk_unit = devm_clk_get(dev, "sclk");
|
||||
if (IS_ERR(clk_unit))
|
||||
return -1;
|
||||
|
||||
if (clk_prepare_enable(clk_unit))
|
||||
return -1;
|
||||
|
||||
clk[0] = clk_get_rate(clk_unit);
|
||||
if (!clk[0]) {
|
||||
clk_disable_unprepare(clk_unit);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spi_update_state(struct ls_spi *ls_spi,struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
unsigned int hz;
|
||||
unsigned int div, div_tmp;
|
||||
unsigned int bit;
|
||||
unsigned long clk;
|
||||
unsigned char val;
|
||||
const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};
|
||||
|
||||
hz = t ? t->speed_hz : spi->max_speed_hz;
|
||||
|
||||
if (!hz)
|
||||
hz = spi->max_speed_hz;
|
||||
|
||||
clk = ls_spi->clk ? ls_spi->clk : 100000000;
|
||||
|
||||
if ((hz && ls_spi->hz != hz) || ((spi->mode ^ ls_spi->mode) & (SPI_CPOL | SPI_CPHA))) {
|
||||
div = DIV_ROUND_UP(clk, hz);
|
||||
|
||||
if (div < 2)
|
||||
div = 2;
|
||||
|
||||
if (div > 4096)
|
||||
div = 4096;
|
||||
|
||||
bit = fls(div) - 1;
|
||||
if ((1<<bit) == div)
|
||||
bit--;
|
||||
div_tmp = rdiv[bit];
|
||||
|
||||
dev_dbg(&spi->dev, "clk = %ld hz = %d div_tmp = %d bit = %d\n",
|
||||
clk, hz, div_tmp, bit);
|
||||
|
||||
ls_spi->hz = hz;
|
||||
ls_spi->spcr = div_tmp & 3;
|
||||
ls_spi->sper = (div_tmp >> 2) & 3;
|
||||
|
||||
val = ls_spi_read_reg(ls_spi, SPCR);
|
||||
val &= ~0xc;
|
||||
if (spi->mode & SPI_CPOL)
|
||||
val |= 8;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
val |= 4;
|
||||
ls_spi_write_reg(ls_spi, SPCR, (val & ~3) | ls_spi->spcr);
|
||||
val = ls_spi_read_reg(ls_spi, SPER);
|
||||
ls_spi_write_reg(ls_spi, SPER, (val & ~3) | ls_spi->sper);
|
||||
ls_spi->mode &= SPI_NO_CS;
|
||||
ls_spi->mode |= spi->mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
|
||||
ls_spi = spi_controller_get_devdata(spi->controller);
|
||||
if (spi->bits_per_word %8)
|
||||
return -EINVAL;
|
||||
|
||||
if(spi->chip_select[0] >= spi->controller->num_chipselect)
|
||||
return -EINVAL;
|
||||
|
||||
ls_spi_update_state(ls_spi, spi, NULL);
|
||||
|
||||
set_cs(ls_spi, spi, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spi_write_read_8bit( struct spi_device *spi,
|
||||
const u8 **tx_buf, u8 **rx_buf, unsigned int num)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
ls_spi = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
if (tx_buf && *tx_buf){
|
||||
ls_spi_write_reg(ls_spi, FIFO, *((*tx_buf)++));
|
||||
while((ls_spi_read_reg(ls_spi, SPSR) & 0x1) == 1);
|
||||
}else{
|
||||
ls_spi_write_reg(ls_spi, FIFO, 0);
|
||||
while((ls_spi_read_reg(ls_spi, SPSR) & 0x1) == 1);
|
||||
}
|
||||
|
||||
if (rx_buf && *rx_buf) {
|
||||
*(*rx_buf)++ = ls_spi_read_reg(ls_spi, FIFO);
|
||||
}else{
|
||||
ls_spi_read_reg(ls_spi, FIFO);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int ls_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
unsigned int count;
|
||||
const u8 *tx = xfer->tx_buf;
|
||||
u8 *rx = xfer->rx_buf;
|
||||
|
||||
ls_spi = spi_controller_get_devdata(spi->controller);
|
||||
count = xfer->len;
|
||||
|
||||
do {
|
||||
if (ls_spi_write_read_8bit(spi, &tx, &rx, count) < 0)
|
||||
goto out;
|
||||
count--;
|
||||
} while (count);
|
||||
|
||||
out:
|
||||
return xfer->len - count;
|
||||
|
||||
}
|
||||
|
||||
static inline void set_mul_cs(struct ls_spi *ls_spi, struct spi_device *spi, int val)
|
||||
{
|
||||
if (ls_spi->gpio_cs_mode) {
|
||||
gpiod_direction_output(ls_spi->cs_gpios[spi->chip_select[0]], val);
|
||||
}
|
||||
else {
|
||||
int cs = ls_spi_read_reg(ls_spi, SFCS) & ~(0x11 << spi->chip_select[0]);
|
||||
ls_spi_write_reg(ls_spi, SFCS, (val ? (0x11 << spi->chip_select[0]):(0x1 << spi->chip_select[0])) | cs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int set_cs(struct ls_spi *ls_spi, struct spi_device *spi, int val)
|
||||
{
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
val = !val;
|
||||
if (ls_spi->mode & SPI_NO_CS) {
|
||||
// 虽然新增了 gpio_cs_mode 但是 如果 只是单个 cs,一直使能(指不能控制的),那么这里设置不设置都一样
|
||||
ls_spi_write_reg(ls_spi, SPCS, val);
|
||||
} else {
|
||||
set_mul_cs(ls_spi, spi, val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls_spi_work(struct work_struct *work)
|
||||
{
|
||||
struct ls_spi *ls_spi =
|
||||
container_of(work, struct ls_spi, work);
|
||||
int param;
|
||||
|
||||
spin_lock(&ls_spi->lock);
|
||||
param = ls_spi_read_reg(ls_spi, PARA);
|
||||
ls_spi_write_reg(ls_spi, PARA, param&~1);
|
||||
while (!list_empty(&ls_spi->msg_queue)) {
|
||||
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
|
||||
m = container_of(ls_spi->msg_queue.next, struct spi_message, queue);
|
||||
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock(&ls_spi->lock);
|
||||
|
||||
spi = m->spi;
|
||||
|
||||
/*in here set cs*/
|
||||
set_cs(ls_spi, spi, 0);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
|
||||
/*setup spi clock*/
|
||||
ls_spi_update_state(ls_spi, spi, t);
|
||||
|
||||
if (t->len)
|
||||
m->actual_length +=
|
||||
ls_spi_write_read(spi, t);
|
||||
}
|
||||
|
||||
set_cs(ls_spi, spi, 1);
|
||||
m->complete(m->context);
|
||||
|
||||
spin_lock(&ls_spi->lock);
|
||||
}
|
||||
|
||||
ls_spi_write_reg(ls_spi, PARA, param);
|
||||
spin_unlock(&ls_spi->lock);
|
||||
}
|
||||
|
||||
static int ls_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = 0;
|
||||
if (list_empty(&m->transfers) || !m->complete)
|
||||
return -EINVAL;
|
||||
|
||||
ls_spi = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
|
||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"invalid transfer data buffers\n");
|
||||
goto msg_rejected;
|
||||
}
|
||||
|
||||
/*other things not check*/
|
||||
|
||||
}
|
||||
|
||||
spin_lock(&ls_spi->lock);
|
||||
list_add_tail(&m->queue, &ls_spi->msg_queue);
|
||||
queue_work(ls_spi->wq, &ls_spi->work);
|
||||
spin_unlock(&ls_spi->lock);
|
||||
|
||||
return 0;
|
||||
msg_rejected:
|
||||
|
||||
m->status = -EINVAL;
|
||||
if (m->complete)
|
||||
m->complete(m->context);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ls_spi_reginit(void)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = ls_spi_read_reg(ls_spi_dev, SPCR);
|
||||
val &= ~SPCR_SPE;
|
||||
ls_spi_write_reg(ls_spi_dev, SPCR, val);
|
||||
|
||||
ls_spi_write_reg(ls_spi_dev, SPSR, (SPSR_SPIF | SPSR_WCOL));
|
||||
|
||||
val = ls_spi_read_reg(ls_spi_dev, SPCR);
|
||||
val |= SPCR_SPE;
|
||||
ls_spi_write_reg(ls_spi_dev, SPCR, val);
|
||||
}
|
||||
|
||||
static int ls_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *controller;
|
||||
struct ls_spi *spi;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
unsigned int num_cs;
|
||||
controller = spi_alloc_master(&pdev->dev, sizeof(struct ls_spi));
|
||||
|
||||
if (controller == NULL) {
|
||||
dev_dbg(&pdev->dev, "controller allocation failed\n");
|
||||
return-ENOMEM;
|
||||
}
|
||||
|
||||
if (pdev->id != -1)
|
||||
controller->bus_num = pdev->id;
|
||||
|
||||
controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH ;
|
||||
controller->setup = ls_spi_setup;
|
||||
controller->transfer = ls_spi_transfer;
|
||||
#ifdef CONFIG_OF
|
||||
controller->dev.of_node = of_node_get(pdev->dev.of_node);
|
||||
#endif
|
||||
dev_set_drvdata(&pdev->dev, controller);
|
||||
|
||||
spi = spi_controller_get_devdata(controller);
|
||||
|
||||
ls_spi_dev = spi;
|
||||
|
||||
spi->wq = create_singlethread_workqueue(pdev->name);
|
||||
|
||||
spi->controller = controller;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
|
||||
ret = -ENOENT;
|
||||
goto free_controller;
|
||||
}
|
||||
|
||||
spi->base = ioremap(res->start, (res->end - res->start)+1);
|
||||
if (spi->base == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot map IO\n");
|
||||
ret = -ENXIO;
|
||||
goto unmap_io;
|
||||
}
|
||||
|
||||
ls_spi_reginit();
|
||||
|
||||
spi->clk = 0;
|
||||
get_ls_spi_clk(&pdev->dev, &spi->clk);
|
||||
|
||||
spi->mode = 0;
|
||||
if (of_get_property(pdev->dev.of_node, "spi-nocs", NULL))
|
||||
spi->mode |= SPI_NO_CS;
|
||||
|
||||
spi->gpio_cs_mode = 0;
|
||||
if (of_get_property(pdev->dev.of_node, "gpio_cs", NULL))
|
||||
spi->gpio_cs_mode = 1;
|
||||
|
||||
controller->num_chipselect = 4;
|
||||
if (spi->gpio_cs_mode) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "num-chipselects", &num_cs);
|
||||
if (ret || !num_cs) {
|
||||
// 看成没有cs的玩法 基本不会来这里
|
||||
controller->num_chipselect = 0;
|
||||
spi->gpio_cs_mode = 0;
|
||||
spi->mode |= SPI_NO_CS;
|
||||
if (ret)
|
||||
dev_dbg(&pdev->dev, "warning: not found num-chipselects and let it as 0\n");
|
||||
} else {
|
||||
int i;
|
||||
spi->mode = 0; // 按照 gpio_cs 的模式来弄 这里的话是为了无视 dts 里面的 spi-nocs 声明
|
||||
controller->num_chipselect = num_cs;
|
||||
spi->cs_gpios = devm_kcalloc(&pdev->dev, num_cs,
|
||||
sizeof(*spi->cs_gpios),
|
||||
GFP_KERNEL);
|
||||
if (!spi->cs_gpios)
|
||||
goto unmap_io;
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
spi->cs_gpios[i] = devm_gpiod_get_index(&pdev->dev, "cs", i,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(spi->cs_gpios[i])) {
|
||||
devm_kfree(&pdev->dev, spi->cs_gpios);
|
||||
spi->cs_gpios = NULL;
|
||||
goto unmap_io;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INIT_WORK(&spi->work, ls_spi_work);
|
||||
|
||||
spin_lock_init(&spi->lock);
|
||||
INIT_LIST_HEAD(&spi->msg_queue);
|
||||
|
||||
ret = spi_register_controller(controller);
|
||||
if (ret < 0)
|
||||
goto unmap_io;
|
||||
|
||||
return ret;
|
||||
|
||||
unmap_io:
|
||||
iounmap(spi->base);
|
||||
free_controller:
|
||||
kfree(controller);
|
||||
spi_controller_put(controller);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ls_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
struct spi_controller *controller;
|
||||
|
||||
controller = dev_get_drvdata(dev);
|
||||
ls_spi = spi_controller_get_devdata(controller);
|
||||
|
||||
ls_spi->spcr = ls_spi_read_reg(ls_spi, SPCR);
|
||||
ls_spi->sper = ls_spi_read_reg(ls_spi, SPER);
|
||||
ls_spi->spsr = ls_spi_read_reg(ls_spi, SPSR);
|
||||
ls_spi->para = ls_spi_read_reg(ls_spi, PARA);
|
||||
ls_spi->sfcs = ls_spi_read_reg(ls_spi, SFCS);
|
||||
ls_spi->timi = ls_spi_read_reg(ls_spi, TIMI);
|
||||
|
||||
#ifdef CONFIG_MACH_LOONGSON64
|
||||
if (!(ls_spi->para & PARA_MEM_EN))
|
||||
ls_spi_write_reg(ls_spi, PARA, ls_spi->para | PARA_MEM_EN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_spi_resume(struct device *dev)
|
||||
{
|
||||
struct ls_spi *ls_spi;
|
||||
struct spi_controller *controller;
|
||||
|
||||
controller = dev_get_drvdata(dev);
|
||||
ls_spi = spi_controller_get_devdata(controller);
|
||||
|
||||
ls_spi_write_reg(ls_spi, SPCR, ls_spi->spcr);
|
||||
ls_spi_write_reg(ls_spi, SPER, ls_spi->sper);
|
||||
ls_spi_write_reg(ls_spi, SPSR, ls_spi->spsr);
|
||||
ls_spi_write_reg(ls_spi, PARA, ls_spi->para);
|
||||
ls_spi_write_reg(ls_spi, SFCS, ls_spi->sfcs);
|
||||
ls_spi_write_reg(ls_spi, TIMI, ls_spi->timi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ls_spi_dev_pm_ops = {
|
||||
.suspend = ls_spi_suspend,
|
||||
.resume = ls_spi_resume,
|
||||
};
|
||||
|
||||
#define LS_DEV_PM_OPS (&ls_spi_dev_pm_ops)
|
||||
#else
|
||||
#define LS_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id ls_spi_id_table[] = {
|
||||
{ .compatible = "loongson,ls-spi", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls_spi_id_table);
|
||||
#endif
|
||||
static struct platform_driver ls_spi_driver = {
|
||||
.probe = ls_spi_probe,
|
||||
.driver = {
|
||||
.name = "ls-spi",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.pm = LS_DEV_PM_OPS,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(ls_spi_id_table),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static struct resource ls_spi_resources[] = {
|
||||
[0] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device ls_spi_device = {
|
||||
.name = "ls-spi",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(ls_spi_resources),
|
||||
.resource = ls_spi_resources,
|
||||
};
|
||||
|
||||
|
||||
static int ls_spi_pci_register(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int ret;
|
||||
unsigned char v8;
|
||||
|
||||
pr_debug("ls_spi_pci_register BEGIN\n");
|
||||
/* Enable device in PCI config */
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "ls-pci (%s): Cannot enable PCI device\n",
|
||||
pci_name(pdev));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* request the mem regions */
|
||||
ret = pci_request_region(pdev, 0, "ls-spi io");
|
||||
if (ret < 0) {
|
||||
printk( KERN_ERR "ls-spi (%s): cannot request region 0.\n",
|
||||
pci_name(pdev));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ls_spi_resources[0].start = pci_resource_start (pdev, 0);
|
||||
ls_spi_resources[0].end = pci_resource_end(pdev, 0);
|
||||
/* need api from pci irq */
|
||||
ret = pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &v8);
|
||||
|
||||
if (ret == PCIBIOS_SUCCESSFUL) {
|
||||
|
||||
ls_spi_resources[1].start = v8;
|
||||
ls_spi_resources[1].end = v8;
|
||||
platform_device_register(&ls_spi_device);
|
||||
}
|
||||
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ls_spi_pci_unregister(struct pci_dev *pdev)
|
||||
{
|
||||
pci_release_region(pdev, 0);
|
||||
}
|
||||
|
||||
static struct pci_device_id ls_spi_devices[] = {
|
||||
{PCI_DEVICE(0x14, 0x7a0b)},
|
||||
{0, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static struct pci_driver ls_spi_pci_driver = {
|
||||
.name = "ls-spi-pci",
|
||||
.id_table = ls_spi_devices,
|
||||
.probe = ls_spi_pci_register,
|
||||
.remove = ls_spi_pci_unregister,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
static int __init ls_spi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&ls_spi_driver);
|
||||
#ifdef CONFIG_PCI
|
||||
if(!ret)
|
||||
ret = pci_register_driver(&ls_spi_pci_driver);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ls_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ls_spi_driver);
|
||||
#ifdef CONFIG_PCI
|
||||
pci_unregister_driver(&ls_spi_pci_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
subsys_initcall(ls_spi_init);
|
||||
module_exit(ls_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Juxin Gao <gaojuxin@loongson.cn>");
|
||||
MODULE_DESCRIPTION("Loongson SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -30,6 +30,11 @@
|
||||
LOONGSON2_THSENS_INT_HIGH)
|
||||
#define LOONGSON2_THSENS_OUT_MASK 0xFF
|
||||
|
||||
enum cpu_id {
|
||||
ID_2K300 = 1,
|
||||
ID_OTHER,
|
||||
};
|
||||
|
||||
/*
|
||||
* This flag is used to indicate the temperature reading
|
||||
* method of the Loongson-2K2000
|
||||
@@ -37,6 +42,7 @@
|
||||
#define LS2K2000_THSENS_OUT_FLAG BIT(0)
|
||||
|
||||
struct loongson2_thermal_chip_data {
|
||||
enum cpu_id cpu_type;
|
||||
unsigned int thermal_sensor_sel;
|
||||
unsigned int flags;
|
||||
};
|
||||
@@ -71,13 +77,52 @@ static int loongson2_thermal_set(struct loongson2_thermal_data *data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int warning_2k300_old_fuse_tip;
|
||||
|
||||
static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
int val;
|
||||
int temp_val;
|
||||
int old_fuse_2k300;
|
||||
struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
|
||||
const struct loongson2_thermal_chip_data* chip_data;
|
||||
enum cpu_id cur_id;
|
||||
|
||||
if (!data)
|
||||
cur_id = ID_OTHER;
|
||||
else {
|
||||
chip_data = data->chip_data;
|
||||
if (!chip_data)
|
||||
cur_id = ID_OTHER;
|
||||
else
|
||||
cur_id = chip_data->cpu_type;
|
||||
}
|
||||
|
||||
val = readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG);
|
||||
*temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO;
|
||||
if (cur_id == ID_2K300) {
|
||||
temp_val = ((val & 0x7ff) * 569) - 394700;
|
||||
/*
|
||||
* fix old 2k300 use old fuse that will let kernel crash critical temperature
|
||||
*/
|
||||
old_fuse_2k300 = 0;
|
||||
val = temp_val;
|
||||
if (temp_val < -55000) {
|
||||
pr_info("look temp_val is %d\n", temp_val);
|
||||
old_fuse_2k300 = 1;
|
||||
temp_val = -55000;
|
||||
}
|
||||
else if (temp_val > 125000) {
|
||||
pr_info("look temp_val is %d\n", temp_val);
|
||||
old_fuse_2k300 = 1;
|
||||
temp_val = 125000;
|
||||
}
|
||||
if (!warning_2k300_old_fuse_tip && old_fuse_2k300) {
|
||||
pr_err("%s this 2k300 use old fuse so thermal not right!(%d)\n", __func__, val);
|
||||
warning_2k300_old_fuse_tip = 1;
|
||||
}
|
||||
*temp = temp_val;
|
||||
} else
|
||||
*temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -175,11 +220,18 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = {
|
||||
.cpu_type = ID_OTHER,
|
||||
.thermal_sensor_sel = 0,
|
||||
};
|
||||
|
||||
static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k300_data = {
|
||||
.cpu_type = ID_2K300,
|
||||
.thermal_sensor_sel = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = {
|
||||
.cpu_type = ID_OTHER,
|
||||
.thermal_sensor_sel = 0,
|
||||
.flags = LS2K2000_THSENS_OUT_FLAG,
|
||||
};
|
||||
@@ -193,6 +245,10 @@ static const struct of_device_id of_loongson2_thermal_match[] = {
|
||||
.compatible = "loongson,ls2k2000-thermal",
|
||||
.data = &loongson2_thermal_ls2k2000_data,
|
||||
},
|
||||
{
|
||||
.compatible = "loongson,ls2k300-thermal",
|
||||
.data = &loongson2_thermal_ls2k300_data,
|
||||
},
|
||||
{ /* end */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_loongson2_thermal_match);
|
||||
|
||||
@@ -1883,6 +1883,25 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
|
||||
return up->dma->rx_dma(up);
|
||||
}
|
||||
|
||||
static int serial8250_handle_irq_rx_status_error_ignore(unsigned char status)
|
||||
{
|
||||
unsigned char temp;
|
||||
int i = 0;
|
||||
|
||||
// 0x18 打断中断 有帧错误表示位 接收FIFO为空
|
||||
unsigned char error_value_set[] = {(UART_LSR_BI | UART_LSR_FE), UART_LSR_BI};
|
||||
unsigned char error_mask_set[] = {(UART_LSR_BI | UART_LSR_FE | UART_LSR_DR), (UART_LSR_BI | UART_LSR_DR)};
|
||||
|
||||
for (i = 0; i < sizeof(error_value_set); ++i) {
|
||||
temp = status;
|
||||
temp &= error_mask_set[i];
|
||||
if (temp == error_value_set[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This handles the interrupt from one port.
|
||||
*/
|
||||
@@ -1920,8 +1939,10 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
|
||||
d = irq_get_irq_data(port->irq);
|
||||
if (d && irqd_is_wakeup_set(d))
|
||||
pm_wakeup_event(tport->tty->dev, 0);
|
||||
if (!up->dma || handle_rx_dma(up, iir))
|
||||
status = serial8250_rx_chars(up, status);
|
||||
if (serial8250_handle_irq_rx_status_error_ignore(status)) {
|
||||
if (!up->dma || handle_rx_dma(up, iir))
|
||||
status = serial8250_rx_chars(up, status);
|
||||
}
|
||||
}
|
||||
serial8250_modem_status(up);
|
||||
if ((status & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) {
|
||||
|
||||
@@ -241,7 +241,7 @@ static bool usb_generic_driver_match(struct usb_device *udev)
|
||||
|
||||
int usb_generic_driver_probe(struct usb_device *udev)
|
||||
{
|
||||
int err, c;
|
||||
int err, c, try;
|
||||
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
@@ -251,7 +251,13 @@ int usb_generic_driver_probe(struct usb_device *udev)
|
||||
else {
|
||||
c = usb_choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
for (try = 0; try < 5; try++) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (!err)
|
||||
break;
|
||||
dev_warn(&udev->dev, "try to set config #%d again\n", c);
|
||||
udelay(100);
|
||||
}
|
||||
if (err && err != -ENODEV) {
|
||||
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
||||
c, err);
|
||||
|
||||
@@ -505,6 +505,12 @@ struct dwc2_core_params {
|
||||
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
|
||||
|
||||
bool change_speed_quirk;
|
||||
|
||||
#define DWC2_LOONGSON_FIX_NONE 0
|
||||
#define DWC2_LOONGSON_FIX_DMA 1
|
||||
#define DWC2_LOONGSON_FIX_DELAY 2
|
||||
#define DWC2_LOONGSON_FIX_DELAY_FULL 3
|
||||
u32 loongson_fix;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+13
-1
@@ -605,6 +605,13 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
|
||||
dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
|
||||
|
||||
// EP0 & CONTROL & BULK must delay for a while
|
||||
if(hsotg->params.loongson_fix == DWC2_LOONGSON_FIX_DELAY &&
|
||||
chan->ep_num == 0 &&
|
||||
(chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
|
||||
chan->ep_type == USB_ENDPOINT_XFER_BULK) )
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
|
||||
@@ -662,7 +669,6 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
|
||||
hcintmsk = 0xffffffff;
|
||||
hcintmsk &= ~HCINTMSK_RESERVED14_31;
|
||||
dwc2_writel(hsotg, hcintmsk, HCINT(hc_num));
|
||||
|
||||
/* Enable channel interrupts required for this transfer */
|
||||
dwc2_hc_enable_ints(hsotg, chan);
|
||||
|
||||
@@ -699,6 +705,11 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
|
||||
chan->max_packet);
|
||||
}
|
||||
|
||||
// EP0 must delay for a while
|
||||
if(hsotg->params.loongson_fix == DWC2_LOONGSON_FIX_DELAY &&
|
||||
chan->ep_num == 0)
|
||||
udelay(500);
|
||||
|
||||
/* Program the HCSPLT register for SPLITs */
|
||||
if (chan->do_split) {
|
||||
if (dbg_hc(chan))
|
||||
@@ -3707,6 +3718,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
hprt0 &= ~HPRT0_RST;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
hsotg->lx_state = DWC2_L0; /* Now back to On state */
|
||||
msleep(50);
|
||||
break;
|
||||
|
||||
case USB_PORT_FEAT_INDICATOR:
|
||||
|
||||
@@ -66,6 +66,13 @@ static void dwc2_set_loongson_params(struct dwc2_hsotg *hsotg)
|
||||
|
||||
p->phy_utmi_width = 8;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_PARTIAL;
|
||||
|
||||
device_property_read_u32(hsotg->dev, "loongson,dwc2-fix",
|
||||
&p->loongson_fix);
|
||||
dev_info(hsotg->dev, "Loongson Fix Param: %d\n", p->loongson_fix);
|
||||
|
||||
if (p->loongson_fix == DWC2_LOONGSON_FIX_DMA)
|
||||
p->dma_desc_enable = true;
|
||||
}
|
||||
|
||||
static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg)
|
||||
@@ -347,6 +354,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
.data = dwc2_set_stm32mp15_hsotg_params },
|
||||
{ .compatible = "intel,socfpga-agilex-hsotg",
|
||||
.data = dwc2_set_socfpga_agilex_params },
|
||||
{ .compatible = "loongson,loongson2-dwc2",
|
||||
.data = dwc2_set_loongson_params },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
|
||||
@@ -70,4 +70,9 @@ config LOGO_SUPERH_CLUT224
|
||||
depends on SUPERH
|
||||
default y
|
||||
|
||||
config LOGO_LOONGSON_CLUT224
|
||||
bool "224-color Loongson Linux logo"
|
||||
depends on MACH_LOONGSON64 || MACH_LOONGSON32
|
||||
default y
|
||||
|
||||
endif # LOGO
|
||||
|
||||
@@ -13,6 +13,7 @@ obj-$(CONFIG_LOGO_SUN_CLUT224) += logo_sun_clut224.o
|
||||
obj-$(CONFIG_LOGO_SUPERH_MONO) += logo_superh_mono.o
|
||||
obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o
|
||||
obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o
|
||||
obj-$(CONFIG_LOGO_LOONGSON_CLUT224) += logo_loongson_clut224.o
|
||||
|
||||
obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ const struct linux_logo * __ref fb_find_logo(int depth)
|
||||
logo = &logo_superh_mono;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
if (depth >= 4) {
|
||||
#ifdef CONFIG_LOGO_LINUX_VGA16
|
||||
/* Generic Linux logo */
|
||||
@@ -69,7 +69,7 @@ const struct linux_logo * __ref fb_find_logo(int depth)
|
||||
logo = &logo_superh_vga16;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
if (depth >= 8) {
|
||||
#ifdef CONFIG_LOGO_LINUX_CLUT224
|
||||
/* Generic Linux logo */
|
||||
@@ -99,6 +99,10 @@ const struct linux_logo * __ref fb_find_logo(int depth)
|
||||
#ifdef CONFIG_LOGO_SUPERH_CLUT224
|
||||
/* SuperH Linux logo */
|
||||
logo = &logo_superh_clut224;
|
||||
#endif
|
||||
#ifdef CONFIG_LOGO_LOONGSON_CLUT224
|
||||
/* Loongson Linux logo */
|
||||
logo = &logo_loongson_clut224;
|
||||
#endif
|
||||
}
|
||||
return logo;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1925,6 +1925,13 @@ config LOONGSON1_WDT
|
||||
help
|
||||
Hardware driver for the Loongson1 SoC Watchdog Timer.
|
||||
|
||||
config LOONGSON2_WDT
|
||||
tristate "Loongson2 SoC hardware watchdog"
|
||||
depends on MACH_LOONGSON64
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Hardware driver for the Loongson2 SoC Watchdog Timer.
|
||||
|
||||
config RALINK_WDT
|
||||
tristate "Ralink SoC watchdog"
|
||||
select WATCHDOG_CORE
|
||||
|
||||
@@ -175,6 +175,7 @@ obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
|
||||
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
|
||||
obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
|
||||
obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o
|
||||
obj-$(CONFIG_LOONGSON2_WDT) += loongson2_wdt.o
|
||||
obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o
|
||||
obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
|
||||
obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <loongson-2k.h>
|
||||
|
||||
#define DEFAULT_HEARTBEAT 30
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0444);
|
||||
|
||||
static unsigned int heartbeat;
|
||||
module_param(heartbeat, uint, 0444);
|
||||
|
||||
struct ls2x_wdt_drvdata {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned long clk_rate;
|
||||
struct watchdog_device wdt;
|
||||
};
|
||||
|
||||
static int ls2x_wdt_ping(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
writel(0x1, drvdata->base + WD_CR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
|
||||
unsigned int max_hw_heartbeat = wdt_dev->max_hw_heartbeat_ms / 1000;
|
||||
unsigned int counts;
|
||||
|
||||
wdt_dev->timeout = timeout;
|
||||
|
||||
counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat);
|
||||
writel(counts, drvdata->base + WD_TIMER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_wdt_start(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
|
||||
u8 wd_en = 1;
|
||||
|
||||
writel((1 << wd_en), drvdata->base + RST_CTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_wdt_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
|
||||
|
||||
writel(0, drvdata->base + RST_CTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ls2x_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "Loongson2 Watchdog",
|
||||
};
|
||||
|
||||
static const struct watchdog_ops ls2x_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = ls2x_wdt_start,
|
||||
.stop = ls2x_wdt_stop,
|
||||
.ping = ls2x_wdt_ping,
|
||||
.set_timeout = ls2x_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static int ls2x_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata;
|
||||
struct watchdog_device *ls2x_wdt;
|
||||
unsigned long clk_rate;
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
drvdata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(drvdata->base))
|
||||
return PTR_ERR(drvdata->base);
|
||||
|
||||
drvdata->clk = devm_clk_get(&pdev->dev, "wdt-clk");
|
||||
if (IS_ERR(drvdata->clk))
|
||||
return PTR_ERR(drvdata->clk);
|
||||
|
||||
err = clk_prepare_enable(drvdata->clk);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "clk enable failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(drvdata->clk);
|
||||
if (!clk_rate) {
|
||||
err = -EINVAL;
|
||||
goto err0;
|
||||
}
|
||||
drvdata->clk_rate = clk_rate;
|
||||
|
||||
ls2x_wdt = &drvdata->wdt;
|
||||
ls2x_wdt->info = &ls2x_wdt_info;
|
||||
ls2x_wdt->ops = &ls2x_wdt_ops;
|
||||
ls2x_wdt->timeout = DEFAULT_HEARTBEAT;
|
||||
ls2x_wdt->min_timeout = 1;
|
||||
ls2x_wdt->max_hw_heartbeat_ms = U32_MAX / clk_rate * 1000;
|
||||
ls2x_wdt->parent = &pdev->dev;
|
||||
|
||||
watchdog_init_timeout(ls2x_wdt, heartbeat, &pdev->dev);
|
||||
watchdog_set_nowayout(ls2x_wdt, nowayout);
|
||||
watchdog_set_drvdata(ls2x_wdt, drvdata);
|
||||
|
||||
err = watchdog_register_device(&drvdata->wdt);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register watchdog device\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
dev_info(&pdev->dev, "Loongson2 Watchdog driver registered\n");
|
||||
|
||||
return 0;
|
||||
err0:
|
||||
pr_info("lsdbg----> ls2x_wdt_probe err....");
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ls2x_wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ls2x_wdt_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
watchdog_unregister_device(&drvdata->wdt);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ls2x_wdt_of_match[] = {
|
||||
{.compatible = "loongson,ls2x-wdt",},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, ls2x_wdt_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ls2x_wdt_driver = {
|
||||
.probe = ls2x_wdt_probe,
|
||||
.remove = ls2x_wdt_remove,
|
||||
.driver = {
|
||||
.name = "ls2x-wdt",
|
||||
.of_match_table = of_match_ptr(ls2x_wdt_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ls2x_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Xiaochuang Mao <maoxiaochuan@loongson.cn>");
|
||||
MODULE_DESCRIPTION("Loongson2 Watchdog Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef __DT_BINDINGS_CLOCK_LOONGSON2K1000_H
|
||||
#define __DT_BINDINGS_CLOCK_LOONGSON2K1000_H
|
||||
|
||||
#define CLK_REF 0
|
||||
#define CLK_NODE 1
|
||||
#define CLK_CPU 2
|
||||
#define CLK_DDR 3
|
||||
#define CLK_GPU 4
|
||||
#define CLK_HDA 5
|
||||
#define CLK_DC 6
|
||||
#define CLK_PIX0 7
|
||||
#define CLK_PIX1 8
|
||||
#define CLK_GMAC 9
|
||||
#define CLK_SATA 10
|
||||
#define CLK_USB 11
|
||||
#define CLK_APB 12
|
||||
#define CLK_UART CLK_APB
|
||||
#define CLK_I2C CLK_APB
|
||||
#define CLK_NAND CLK_APB
|
||||
#define CLK_WDT CLK_APB
|
||||
#define CLK_SDIO CLK_APB
|
||||
#define CLK_CAN CLK_APB
|
||||
#define CLK_PWM CLK_APB
|
||||
|
||||
#define CLK_SPI 13
|
||||
#define CLK_I2S_MCLK 14
|
||||
|
||||
#define CLK_CNT 15
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef __DT_BINDINGS_CLOCK_LOONGSON2K300_H
|
||||
#define __DT_BINDINGS_CLOCK_LOONGSON2K300_H
|
||||
|
||||
#define CLK_REF 0
|
||||
|
||||
#define CLK_NODE 1
|
||||
#define CLK_CPU 2
|
||||
#define CLK_SCACHE 3
|
||||
#define CLK_IODMA 4
|
||||
#define CLK_GMAC 5
|
||||
#define CLK_I2S 6
|
||||
|
||||
#define CLK_DDR_P 7
|
||||
#define CLK_DDR 8
|
||||
#define CLK_NET 9
|
||||
#define CLK_DEVS 10
|
||||
#define CLK_USB 11
|
||||
#define CLK_APB 12
|
||||
#define CLK_BOOT 13
|
||||
#define CLK_SDIO 14
|
||||
|
||||
#define CLK_PIX_P 15
|
||||
#define CLK_PIX 16
|
||||
#define CLK_GMACBP 17
|
||||
|
||||
#define CLK_UART CLK_APB
|
||||
#define CLK_CAN CLK_APB
|
||||
#define CLK_I2C CLK_APB
|
||||
#define CLK_SPI CLK_APB
|
||||
#define CLK_AC97 CLK_APB
|
||||
#define CLK_NAND CLK_APB
|
||||
#define CLK_PWM CLK_APB
|
||||
#define CLK_HPET CLK_APB
|
||||
#define CLK_RTC CLK_APB
|
||||
|
||||
#define CLK_CNT 18
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef __DT_BINDINGS_CLOCK_LOONGSON2K500_H
|
||||
#define __DT_BINDINGS_CLOCK_LOONGSON2K500_H
|
||||
|
||||
#define CLK_REF 0
|
||||
|
||||
#define CLK_NODE 1
|
||||
#define CLK_CPU 2
|
||||
#define CLK_SCACHE 3
|
||||
#define CLK_IODMA 4
|
||||
|
||||
#define CLK_DDR_P 5
|
||||
#define CLK_DDR 6
|
||||
#define CLK_HDA 7
|
||||
#define CLK_NET 8
|
||||
#define CLK_PRINT CLK_NET
|
||||
#define CLK_PRINT_LSU 9
|
||||
|
||||
#define CLK_PIX0 10
|
||||
#define CLK_DC0 CLK_PIX0
|
||||
|
||||
#define CLK_PIX1 11
|
||||
#define CLK_DC1 CLK_PIX1
|
||||
|
||||
#define CLK_SOC 12
|
||||
#define CLK_GPU 13
|
||||
#define CLK_GMAC 14
|
||||
#define CLK_SB 15
|
||||
#define CLK_BOOT 16
|
||||
#define CLK_BOOT_CONF CLK_BOOT
|
||||
#define CLK_BOOT_SPI CLK_BOOT
|
||||
#define CLK_BOOT_LPC CLK_BOOT
|
||||
#define CLK_BOOT_LIO CLK_BOOT
|
||||
#define CLK_SATA 17
|
||||
#define CLK_USB 18
|
||||
#define CLK_APB 19
|
||||
#define CLK_UART CLK_APB
|
||||
#define CLK_CAN CLK_APB
|
||||
#define CLK_I2C CLK_APB
|
||||
#define CLK_PS2 CLK_APB
|
||||
#define CLK_SPI CLK_APB
|
||||
#define CLK_AC97 CLK_APB
|
||||
#define CLK_NAND CLK_APB
|
||||
#define CLK_PWM CLK_APB
|
||||
#define CLK_DPM CLK_APB
|
||||
#define CLK_SDIO CLK_APB
|
||||
#define CLK_HPET CLK_APB
|
||||
#define CLK_ACPI CLK_APB
|
||||
#define CLK_RTC CLK_APB
|
||||
|
||||
#define CLK_CNT 20
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __DT_DWC2_LOONGSON_FIX_H
|
||||
#define __DT_DWC2_LOONGSON_FIX_H
|
||||
|
||||
#define DWC2_LOONGSON_FIX_NONE 0
|
||||
#define DWC2_LOONGSON_FIX_DMA 1
|
||||
#define DWC2_LOONGSON_FIX_DELAY 2
|
||||
#define DWC2_LOONGSON_FIX_DELAY_FULL 3
|
||||
|
||||
#endif
|
||||
@@ -42,6 +42,7 @@ extern const struct linux_logo logo_superh_mono;
|
||||
extern const struct linux_logo logo_superh_vga16;
|
||||
extern const struct linux_logo logo_superh_clut224;
|
||||
extern const struct linux_logo logo_spe_clut224;
|
||||
extern const struct linux_logo logo_loongson_clut224;
|
||||
|
||||
extern const struct linux_logo *fb_find_logo(int depth);
|
||||
#ifdef CONFIG_FB_LOGO_EXTRA
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
#set -x
|
||||
|
||||
function setup_mips_env()
|
||||
{
|
||||
echo "====>setup env for MIPS..."
|
||||
# CC_PREFIX=/opt/mips64el-linux-gcc-8.x/host
|
||||
# export PATH=$CC_PREFIX/bin:$PATH
|
||||
# export LD_LIBRARY_PATH=$CC_PREFIX/lib:$LD_LIBRARY_PATH
|
||||
# export LD_LIBRARY_PATH=$CC_PREFIX/mips64el-buildroot-linux-gnu/lib64:$LD_LIBRARY_PATH
|
||||
|
||||
CC_PREFIX=/opt/mips-loongson-gcc8-linux-gnu-2021-06-04
|
||||
export PATH=$CC_PREFIX/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$CC_PREFIX/lib:$LD_LIBRARY_PATH
|
||||
|
||||
export ARCH=mips
|
||||
export CROSS_COMPILE=mips-linux-gnu-
|
||||
# export CROSS_COMPILE=mips64el-linux-
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setup_loongarch_env()
|
||||
{
|
||||
echo "====>setup env for LoongArch..."
|
||||
CC_PREFIX=/opt/loongarch64-linux-gnu-gcc13.3
|
||||
|
||||
export PATH=$PATH:$CC_PREFIX/bin
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:CC_PREFIX/lib
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CC_PREFIX/loongarch64-linux-gnu/lib64
|
||||
|
||||
export ARCH=loongarch
|
||||
export CROSS_COMPILE=loongarch64-linux-gnu-
|
||||
}
|
||||
|
||||
|
||||
if [ $# -eq 1 ] ; then
|
||||
if [ "$1" == "mips" ]; then
|
||||
setup_mips_env
|
||||
else
|
||||
setup_loongarch_env
|
||||
fi
|
||||
else
|
||||
setup_loongarch_env
|
||||
fi
|
||||
|
||||
#set +x
|
||||
+1
-1
@@ -98,7 +98,7 @@ source "sound/soc/google/Kconfig"
|
||||
source "sound/soc/hisilicon/Kconfig"
|
||||
source "sound/soc/jz4740/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/loongson/Kconfig"
|
||||
source "sound/soc/loongson-gd/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
source "sound/soc/mediatek/Kconfig"
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += google/
|
||||
obj-$(CONFIG_SND_SOC) += hisilicon/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += loongson/
|
||||
obj-$(CONFIG_SND_SOC) += loongson-gd/
|
||||
obj-$(CONFIG_SND_SOC) += img/
|
||||
obj-$(CONFIG_SND_SOC) += intel/
|
||||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
|
||||
@@ -547,6 +547,10 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
|
||||
ES8328_ADCCONTROL4_ADCWL_MASK,
|
||||
wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
|
||||
|
||||
#ifdef CONFIG_LOONGARCH
|
||||
// alsamixer 中暂时没找到调整该配置选项,临时处理
|
||||
snd_soc_component_write(component, ES8328_ADCPOWER, 0x00);
|
||||
#endif
|
||||
return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
|
||||
}
|
||||
|
||||
@@ -803,7 +807,20 @@ static int es8328_component_probe(struct snd_soc_component *component)
|
||||
dev_err(component->dev, "unable to prepare codec clk\n");
|
||||
goto clk_fail;
|
||||
}
|
||||
#ifdef CONFIG_LOONGARCH
|
||||
// RESET CODEC
|
||||
snd_soc_component_write(component, ES8328_CHIPPOWER,
|
||||
ES8328_CHIPPOWER_ADCDIG_OFF |
|
||||
ES8328_CHIPPOWER_DACDIG_OFF |
|
||||
ES8328_CHIPPOWER_ADCSTM_RESET |
|
||||
ES8328_CHIPPOWER_DACSTM_RESET);
|
||||
|
||||
snd_soc_component_write(component, ES8328_DACCONTROL21,
|
||||
ES8328_DACCONTROL21_LD2MO);
|
||||
|
||||
// PAI: waiting for codec ready in slave mode
|
||||
mdelay(1000);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
clk_fail:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
menuconfig SND_SOC_LOONGSON
|
||||
tristate "ASoC support for loongson"
|
||||
|
||||
if SND_SOC_LOONGSON
|
||||
|
||||
config SND_LS
|
||||
tristate "SoC Audio for ls-i2s"
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the I2S interface. You will also need to select the Audio
|
||||
interfaces to support below.
|
||||
|
||||
config SND_LS_PCM
|
||||
tristate "SoC Audio for ls-pcm used descriptor"
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the PCM interface. Select this by configuring the dma of the descriptor.
|
||||
|
||||
config SND_LS_PCM_GENERIC_DMA
|
||||
tristate "SoC Audio for ls-pcm used generic dma"
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the PCM interface. Select this by configuring the generic dma.
|
||||
|
||||
config SND_LS_DAILINK
|
||||
tristate "SoC Audio for Sound-Card/Dai-Link"
|
||||
depends on SND_LS
|
||||
default y
|
||||
|
||||
endif #SND_SOC_LOONGSON
|
||||
@@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_SND_LS) += ls-i2s.o
|
||||
obj-$(CONFIG_SND_LS_PCM) += ls-pcm.o
|
||||
obj-$(CONFIG_SND_LS_PCM_GENERIC_DMA) += ls-pcm-generic.o
|
||||
obj-$(CONFIG_SND_LS_DAILINK) += ls-sound.o
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user