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:
maoxiaochuan
2025-02-13 16:03:32 +08:00
parent f70d65c829
commit 7ffd18ab9d
107 changed files with 17571 additions and 43 deletions
+1
View File
@@ -177,3 +177,4 @@ sphinx_*/
# Rust analyzer configuration
/rust-project.json
*uImage
+12 -1
View File
@@ -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
+6 -1
View File
@@ -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
+897
View File
@@ -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>; //有数据时闪烁
};
};
};
+228
View File
@@ -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";
};
+234
View File
@@ -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
+278 -1
View File
@@ -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/////
//////////////////////////////////////
+23
View File
@@ -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
View File
@@ -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 $@
+1
View File
@@ -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"
+1
View File
@@ -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/
+35
View File
@@ -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
+7
View File
@@ -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
+118
View File
@@ -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);
}
+94
View File
@@ -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);
}
+479
View File
@@ -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);
+840
View File
@@ -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);
+767
View File
@@ -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);
+41
View File
@@ -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;
}
+15
View File
@@ -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 */
+8
View File
@@ -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"
+1
View File
@@ -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/
+703
View File
@@ -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");
+8
View File
@@ -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
+2 -1
View File
@@ -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
+550
View File
@@ -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");
+2
View File
@@ -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
+1
View File
@@ -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);
+345 -17
View File
@@ -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;
}
+12
View File
@@ -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)
+9 -2
View File
@@ -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)) {
+5
View File
@@ -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,
};
+101 -3
View File
@@ -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 = &reg_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);
}
+5
View File
@@ -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
+6
View File
@@ -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] */
+62
View File
@@ -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",
+7
View File
@@ -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
+1
View File
@@ -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
+561
View File
@@ -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");
+6
View File
@@ -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
+1
View File
@@ -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
+291
View File
@@ -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;
}
}
}
+449
View File
@@ -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__ */
+185
View File
@@ -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");
+386
View File
@@ -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);
+10
View File
@@ -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.
+1
View File
@@ -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
+290
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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
+18
View File
@@ -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
+25
View File
@@ -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.
+32
View File
@@ -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;
+9
View File
@@ -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
+1
View File
@@ -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
+34 -6
View File
@@ -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);
+9 -1
View File
@@ -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);
+12
View File
@@ -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
#
+2
View File
@@ -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
+500
View File
@@ -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");
+616
View File
@@ -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");
+57 -1
View File
@@ -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);
+23 -2
View File
@@ -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)) {
+8 -2
View File
@@ -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);
+6
View File
@@ -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
View File
@@ -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:
+9
View File
@@ -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);
+5
View File
@@ -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
+1
View File
@@ -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
+6 -2
View File
@@ -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
+7
View File
@@ -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
+1
View File
@@ -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
+180
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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/
+17
View File
@@ -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:
+30
View File
@@ -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
+4
View File
@@ -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