diff options
author | Tom Rini <trini@konsulko.com> | 2021-01-25 14:38:40 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2021-01-25 14:38:40 -0500 |
commit | c99be953e787cfb2414de67390427e00b6812240 (patch) | |
tree | 9f1253193077505d028e2b0000cefdbede1110cb | |
parent | 4057b98ff2f3fd112f05024cad5ccf970fa9bed4 (diff) | |
parent | 9f03585e8dd5554f131bbe507ccebbc30354f493 (diff) |
Merge tag 'mips-pull-2021-01-24' of https://gitlab.denx.de/u-boot/custodians/u-boot-mips
- MIPS: add support for Mediatek MT7620 SoCs
61 files changed, 4477 insertions, 72 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e7a6dd9ce2d..1864fd8748d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -812,6 +812,29 @@ F: drivers/spi/ca_sflash.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +MIPS MEDIATEK +M: Weijie Gao <weijie.gao@mediatek.com> +R: GSS_MTK_Uboot_upstream <GSS_MTK_Uboot_upstream@mediatek.com> +S: Maintained +F: arch/mips/mach-mtmips/ +F: arch/mips/dts/mt7620.dtsi +F: arch/mips/dts/mt7620-u-boot.dtsi +F: include/configs/mt7620.h +F: include/dt-bindings/clock/mt7620-clk.h +F: include/dt-bindings/clock/mt7628-clk.h +F: include/dt-bindings/reset/mt7620-reset.h +F: include/dt-bindings/reset/mt7628-reset.h +F: drivers/clk/mtmips/ +F: drivers/pinctrl/mtmips/ +F: drivers/gpio/mt7620_gpio.c +F: drivers/net/mt7620-eth.c +F: drivers/phy/mt7620-usb-phy.c +F: drivers/reset/reset-mtmips.c +F: drivers/serial/serial_mt7620.c +F: drivers/spi/mt7620_spi.c +F: drivers/sysreset/sysreset_resetctl.c +F: drivers/watchdog/mt7620_wdt.c + MIPS MSCC M: Gregory CLEMENT <gregory.clement@bootlin.com> M: Lars Povlsen <lars.povlsen@microchip.com> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index e0f6b6c4b3c..77f563e98ed 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -97,7 +97,6 @@ config ARCH_MTMIPS select SUPPORTS_CPU_MIPS32_R1 select SUPPORTS_CPU_MIPS32_R2 select SUPPORTS_LITTLE_ENDIAN - select SYSRESET select SUPPORT_SPL config ARCH_JZ47XX diff --git a/arch/mips/cpu/cpu.c b/arch/mips/cpu/cpu.c index 7d5c9fd83ae..b304026a67c 100644 --- a/arch/mips/cpu/cpu.c +++ b/arch/mips/cpu/cpu.c @@ -12,7 +12,7 @@ #include <asm/mipsregs.h> #include <asm/reboot.h> -#ifndef CONFIG_SYSRESET +#if !CONFIG_IS_ENABLED(SYSRESET) void __weak _machine_restart(void) { fprintf(stderr, "*** reset failed ***\n"); diff --git a/arch/mips/dts/Makefile b/arch/mips/dts/Makefile index dc85901dcaa..7c429231340 100644 --- a/arch/mips/dts/Makefile +++ b/arch/mips/dts/Makefile @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -dtb-$(CONFIG_ARCH_MTMIPS) += \ - gardena-smart-gateway-mt7688.dtb \ - linkit-smart-7688.dtb dtb-$(CONFIG_TARGET_AP121) += ap121.dtb dtb-$(CONFIG_TARGET_AP143) += ap143.dtb dtb-$(CONFIG_TARGET_AP152) += ap152.dtb @@ -17,7 +14,11 @@ dtb-$(CONFIG_BOARD_COMTREND_CT5361) += comtrend,ct-5361.dtb dtb-$(CONFIG_BOARD_COMTREND_VR3032U) += comtrend,vr-3032u.dtb dtb-$(CONFIG_BOARD_COMTREND_WAP5813N) += comtrend,wap-5813n.dtb dtb-$(CONFIG_BOARD_HUAWEI_HG556A) += huawei,hg556a.dtb +dtb-$(CONFIG_BOARD_MT7620_RFB) += mediatek,mt7620-rfb.dtb +dtb-$(CONFIG_BOARD_MT7620_MT7530_RFB) += mediatek,mt7620-mt7530-rfb.dtb dtb-$(CONFIG_BOARD_MT7628_RFB) += mediatek,mt7628-rfb.dtb +dtb-$(CONFIG_BOARD_GARDENA_SMART_GATEWAY_MT7688) += gardena-smart-gateway-mt7688.dtb +dtb-$(CONFIG_BOARD_LINKIT_SMART_7688) += linkit-smart-7688.dtb dtb-$(CONFIG_TARGET_OCTEON_EBB7304) += mrvl,octeon-ebb7304.dtb dtb-$(CONFIG_BOARD_NETGEAR_CG3100D) += netgear,cg3100d.dtb dtb-$(CONFIG_BOARD_NETGEAR_DGND3700V2) += netgear,dgnd3700v2.dtb diff --git a/arch/mips/dts/mediatek,mt7620-mt7530-rfb.dts b/arch/mips/dts/mediatek,mt7620-mt7530-rfb.dts new file mode 100644 index 00000000000..8bc3b1673ac --- /dev/null +++ b/arch/mips/dts/mediatek,mt7620-mt7530-rfb.dts @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "mt7620.dtsi" + +/ { + compatible = "mediatek,mt7620-mt7530-rfb", "mediatek,mt7620-soc"; + model = "MediaTek MT7620-MT7530 RFB (MTKC712)"; + + aliases { + serial0 = &uartlite; + spi0 = &spi0; + }; + + chosen { + stdout-path = &uartlite; + }; +}; + +&uartlite { + status = "okay"; +}; + +&pinctrl { + state_default: pin_state { + pleds { + groups = "ephy led", "wled"; + function = "led"; + }; + + gpios { + groups = "pa", "uartf"; + function = "gpio"; + }; + }; + + gsw_pins: gsw_pins { + mdio { + groups = "mdio"; + function = "mdio"; + }; + + rgmii1 { + groups = "rgmii1"; + function = "rgmii1"; + }; + }; +}; + +&spi0 { + status = "okay"; + num-cs = <2>; + + spi-flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <25000000>; + reg = <0>; + }; +}; + +&gpio0 { + pa0_pull_low { + gpio-hog; + output-low; + gpios = <20 GPIO_ACTIVE_HIGH>; + }; + + pa1_pull_low { + gpio-hog; + output-low; + gpios = <21 GPIO_ACTIVE_HIGH>; + }; +}; + +ð { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&gsw_pins>; + + port5 { + phy-mode = "rgmii"; + phy-addr = <5>; + fixed-link { + full-duplex; + speed = <1000>; + mediatek,mt7530; + mediatek,mt7530-reset = <&gpio0 10 GPIO_ACTIVE_HIGH>; + }; + }; +}; diff --git a/arch/mips/dts/mediatek,mt7620-rfb.dts b/arch/mips/dts/mediatek,mt7620-rfb.dts new file mode 100644 index 00000000000..616903e5544 --- /dev/null +++ b/arch/mips/dts/mediatek,mt7620-rfb.dts @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "mt7620.dtsi" + +/ { + compatible = "mediatek,mt7620-rfb", "mediatek,mt7620-soc"; + model = "MediaTek MT7620 RFB (WS2120)"; + + aliases { + serial0 = &uartlite; + spi0 = &spi0; + }; + + chosen { + stdout-path = &uartlite; + }; +}; + +&uartlite { + status = "okay"; +}; + +&pinctrl { + state_default: pin_state { + pleds { + groups = "ephy led", "wled"; + function = "led"; + }; + + gpios { + groups = "uartf"; + function = "gpio"; + }; + }; + + gsw_pins: gsw_pins { + mdio { + groups = "mdio"; + function = "mdio"; + }; + + rgmii1 { + groups = "rgmii1"; + function = "rgmii1"; + }; + + rgmii2 { + groups = "rgmii2"; + function = "rgmii2"; + }; + }; +}; + +&spi0 { + status = "okay"; + num-cs = <2>; + + spi-flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <25000000>; + reg = <0>; + }; +}; + +ð { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&gsw_pins>; + + port4 { + phy-mode = "rgmii"; + phy-addr = <4>; + }; + + port5 { + phy-mode = "rgmii"; + phy-addr = <5>; + }; +}; + +&mmc { + bus-width = <4>; + cap-sd-highspeed; + + status = "okay"; +}; diff --git a/arch/mips/dts/mt7620-u-boot.dtsi b/arch/mips/dts/mt7620-u-boot.dtsi new file mode 100644 index 00000000000..ed8425719b6 --- /dev/null +++ b/arch/mips/dts/mt7620-u-boot.dtsi @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +&uartlite { + u-boot,dm-pre-reloc; +}; + +&uartfull { + u-boot,dm-pre-reloc; +}; diff --git a/arch/mips/dts/mt7620.dtsi b/arch/mips/dts/mt7620.dtsi new file mode 100644 index 00000000000..03a80b7a709 --- /dev/null +++ b/arch/mips/dts/mt7620.dtsi @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <dt-bindings/clock/mt7620-clk.h> +#include <dt-bindings/reset/mt7620-reset.h> + +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7620-soc"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "mti,mips24KEc"; + device_type = "cpu"; + reg = <0>; + }; + }; + + clk48m: clk48m@0 { + compatible = "fixed-clock"; + + clock-frequency = <48000000>; + + #clock-cells = <0>; + }; + + sysc: sysc@10000000 { + compatible = "mediatek,mt7620-sysc"; + reg = <0x10000000 0x100>; + }; + + clkctrl: clkctrl@10000030 { + compatible = "mediatek,mt7620-clk"; + mediatek,sysc = <&sysc>; + + #clock-cells = <1>; + }; + + rstctrl: rstctrl@10000034 { + compatible = "mediatek,mtmips-reset"; + reg = <0x10000034 0x4>; + #reset-cells = <1>; + }; + + reboot: resetctl-reboot { + compatible = "resetctl-reboot"; + + resets = <&rstctrl SYS_RST>; + reset-names = "sysreset"; + }; + + uartfull: uartfull@10000500 { + compatible = "mediatek,mt7620-uart"; + reg = <10000500 0x100>; + + pinctrl-names = "default"; + pinctrl-0 = <&uartf_gpio_pins>; + + clocks = <&clkctrl CLK_UARTF>; + + resets = <&rstctrl UARTF_RST>; + reset-names = "uartf"; + + clock-frequency = <40000000>; + + status = "disabled"; + }; + + uartlite: uartlite@10000c00 { + compatible = "mediatek,mt7620-uart"; + reg = <0x10000c00 0x100>; + + pinctrl-names = "default"; + pinctrl-0 = <&uartl_pins>; + + clocks = <&clkctrl CLK_UARTL>; + + resets = <&rstctrl UARTL_RST>; + reset-names = "uartl"; + + clock-frequency = <40000000>; + }; + + pinctrl: pinctrl@10000060 { + compatible = "mediatek,mt7620-pinctrl"; + reg = <0x10000060 0x4>; + + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pin_state { + sutif_pins { + groups = "sutif"; + function = "none"; + }; + }; + + nand_pins: nand_pins { + groups = "nand"; + function = "nand"; + }; + + sd_pins: sd_pins { + groups = "nand"; + function = "sd"; + }; + + spi_single_pins: spi_single_pins { + groups = "spi"; + function = "spi"; + }; + + spi_dual_pins: spi_dual_pins { + spi_master_pins { + groups = "spi"; + function = "spi"; + }; + + spi_cs1_pin { + groups = "spi cs1"; + function = "spi cs1"; + }; + }; + + uartl_pins: uartl_pins { + groups = "uartl"; + function = "uartl"; + }; + + uartf_pins: uartf_pins { + groups = "uartf"; + function = "uartf"; + }; + + uartf_pcm_pins: uartf_pcm_pins { + groups = "uartf"; + function = "uartf pcm"; + }; + + uartf_i2s_pins: uartf_i2s_pins { + groups = "uartf"; + function = "i2s uartf"; + }; + + uartf_gpio_pins: uartf_gpio_pins { + groups = "uartf"; + function = "uartf gpio"; + }; + }; + + watchdog: watchdog@10000120 { + compatible = "mediatek,mt7620-wdt"; + reg = <0x10000120 0x10>; + + resets = <&rstctrl TIMER_RST>; + reset-names = "wdt"; + }; + + gpio0: gpio0@10000600 { + compatible = "mediatek,mt7620-gpio"; + reg = <0x10000600 0x34>; + + resets = <&rstctrl PIO_RST>; + reset-names = "pio"; + + mediatek,bank-name = "PIOA"; + mediatek,gpio-num = <24>; + mediatek,register-map = <0x20 0x24 0x2c 0x30>; + + gpio-controller; + #gpio-cells = <2>; + }; + + gpio1: gpio1@10000638 { + compatible = "mediatek,mt7620-gpio"; + reg = <0x10000638 0x24>; + + resets = <&rstctrl PIO_RST>; + reset-names = "pio"; + + mediatek,bank-name = "PIOB"; + mediatek,gpio-num = <16>; + mediatek,register-map = <0x10 0x14 0x1c 0x20>; + + gpio-controller; + #gpio-cells = <2>; + }; + + gpio2: gpio2@10000660 { + compatible = "mediatek,mt7620-gpio"; + reg = <0x10000660 0x24>; + + resets = <&rstctrl PIO_RST>; + reset-names = "pio"; + + mediatek,bank-name = "PIOC"; + mediatek,gpio-num = <32>; + mediatek,register-map = <0x10 0x14 0x1c 0x20>; + + gpio-controller; + #gpio-cells = <2>; + }; + + gpio3: gpio3@10000688 { + compatible = "mediatek,mt7620-gpio"; + reg = <0x10000688 0x24>; + + resets = <&rstctrl PIO_RST>; + reset-names = "pio"; + + mediatek,bank-name = "PIOD"; + mediatek,gpio-num = <1>; + mediatek,register-map = <0x10 0x14 0x1c 0x20>; + + gpio-controller; + #gpio-cells = <2>; + }; + + spi0: spi@10000b00 { + compatible = "mediatek,mt7620-spi"; + reg = <0x10000b00 0x100>; + + pinctrl-names = "default"; + pinctrl-0 = <&spi_single_pins>; + + resets = <&rstctrl SPI_RST>; + reset-names = "spi"; + + #address-cells = <1>; + #size-cells = <0>; + + clocks = <&clkctrl CLK_SPI>; + }; + + eth: eth@10100000 { + compatible = "mediatek,mt7620-eth"; + reg = <0x10100000 0x10000 + 0x10110000 0x8000>; + reg-names = "fe", "esw"; + + mediatek,sysc = <&sysc>; + + resets = <&rstctrl EPHY_RST>, + <&rstctrl ESW_RST>, + <&rstctrl FE_RST>; + reset-names = "ephy", "esw", "fe"; + + clocks = <&clkctrl CLK_EPHY>, + <&clkctrl CLK_ESW>, + <&clkctrl CLK_FE>; + clock-names = "ephy", "esw", "fe"; + + status = "disabled"; + }; + + usb_phy: mt7620-usb-phy { + compatible = "mediatek,mt7620-usbphy"; + + #phy-cells = <0>; + + mediatek,sysc = <&sysc>; + + clocks = <&clkctrl CLK_UPHY_48M>, <&clkctrl CLK_UPHY_12M>; + clock-names = "uphy48m", "uphy12m"; + + resets = <&rstctrl UHST_RST>, <&rstctrl UDEV_RST>; + reset-names = "uhst", "udev"; + }; + + ehci@101c0000 { + compatible = "generic-ehci"; + reg = <0x101c0000 0x1000>; + + phys = <&usb_phy>; + phy-names = "usb"; + }; + + mmc: mmc@10130000 { + compatible = "mediatek,mt7620-mmc"; + reg = <0x10130000 0x4000>; + builtin-cd = <1>; + r_smpl = <1>; + + pinctrl-names = "default"; + pinctrl-0 = <&sd_pins>; + + clocks = <&clk48m>, <&clkctrl CLK_SDHC>; + clock-names = "source", "hclk"; + + resets = <&rstctrl SDHC_RST>; + + status = "disabled"; + }; +}; diff --git a/arch/mips/mach-mtmips/Kconfig b/arch/mips/mach-mtmips/Kconfig index 737de2cb8e2..8756cadb0b6 100644 --- a/arch/mips/mach-mtmips/Kconfig +++ b/arch/mips/mach-mtmips/Kconfig @@ -5,6 +5,7 @@ config SYS_MALLOC_F_LEN default 0x1000 config SYS_SOC + default "mt7620" if SOC_MT7620 default "mt7628" if SOC_MT7628 config SYS_DCACHE_SIZE @@ -31,10 +32,26 @@ config SPL_PAYLOAD config BUILD_TARGET default "u-boot-with-spl.bin" if SPL + default "u-boot.bin" choice prompt "MediaTek MIPS SoC select" +config SOC_MT7620 + bool "MT7620" + select MIPS_L1_CACHE_SHIFT_5 + select SYS_MIPS_CACHE_INIT_RAM_LOAD + select PINCTRL_MT7620 + select MT7620_SERIAL + select MISC + select SPL_SEPARATE_BSS if SPL + select SPL_LOADER_SUPPORT if SPL + select SPL_OF_CONTROL if SPL_DM + select SPL_OF_PLATDATA if SPL_DM + select SPL_DM_SERIAL if SPL_DM + help + This supports MediaTek MT7620. + config SOC_MT7628 bool "MT7628" select MIPS_L1_CACHE_SHIFT_5 @@ -43,6 +60,7 @@ config SOC_MT7628 select SYS_MIPS_CACHE_INIT_RAM_LOAD select PINCTRL_MT7628 select MTK_SERIAL + select SYSRESET select SYSRESET_RESETCTL select SPL_SEPARATE_BSS if SPL select SPL_INIT_STACK_WITHOUT_MALLOC_F if SPL @@ -58,57 +76,7 @@ config SOC_MT7628 endchoice -choice - prompt "Board select" - -config BOARD_GARDENA_SMART_GATEWAY_MT7688 - bool "GARDENA smart Gateway" - depends on SOC_MT7628 - select BOARD_LATE_INIT - help - GARDENA smart Gateway boards have a MT7688 SoC with 128 MiB of RAM - and 8 MiB of flash (SPI NOR) and additional SPI NAND storage. - -config BOARD_LINKIT_SMART_7688 - bool "LinkIt Smart 7688" - depends on SOC_MT7628 - help - Seeed LinkIt Smart 7688 boards have a MT7688 SoC with 128 MiB of RAM - and 32 MiB of flash (SPI). - Between its different peripherals there's an integrated switch with 4 - ethernet ports, 1 USB port, 1 UART, GPIO buttons and LEDs, and - a MT7688 (PCIe). - -config BOARD_MT7628_RFB - bool "MediaTek MT7628 RFB" - depends on SOC_MT7628 - help - The reference design of MT7628. The board has 128 MiB DDR2, 8 MiB - SPI-NOR flash, 1 built-in switch with 5 ports, 1 UART, 1 USB host, - 1 SDXC, 1 PCIe socket and JTAG pins. - -config BOARD_VOCORE2 - bool "VoCore2" - depends on SOC_MT7628 - select SPL_SERIAL_SUPPORT - select SPL_UART2_SPIS_PINMUX - help - VoCore VoCore2 board has a MT7628 SoC with 128 MiB of RAM - and 16 MiB of flash (SPI). - -endchoice - -config SPL_UART2_SPIS_PINMUX - bool "Use alternative pinmux for UART2 in SPL stage" - depends on SPL_SERIAL_SUPPORT - default n - help - Select this if the UART2 of your board is connected to GPIO 16/17 - (shared with SPIS) rather than the usual GPIO 20/21. - -source "board/gardena/smart-gateway-mt7688/Kconfig" -source "board/mediatek/mt7628/Kconfig" -source "board/seeed/linkit-smart-7688/Kconfig" -source "board/vocore/vocore2/Kconfig" +source "arch/mips/mach-mtmips/mt7620/Kconfig" +source "arch/mips/mach-mtmips/mt7628/Kconfig" endmenu diff --git a/arch/mips/mach-mtmips/Makefile b/arch/mips/mach-mtmips/Makefile index a7e6a663047..4909b47ef27 100644 --- a/arch/mips/mach-mtmips/Makefile +++ b/arch/mips/mach-mtmips/Makefile @@ -5,4 +5,5 @@ obj-y += ddr_init.o obj-y += ddr_cal.o obj-$(CONFIG_SPL_BUILD) += spl.o +obj-$(CONFIG_SOC_MT7620) += mt7620/ obj-$(CONFIG_SOC_MT7628) += mt7628/ diff --git a/arch/mips/mach-mtmips/cpu.c b/arch/mips/mach-mtmips/cpu.c index 2ddf8cb0967..ca1967055a2 100644 --- a/arch/mips/mach-mtmips/cpu.c +++ b/arch/mips/mach-mtmips/cpu.c @@ -6,6 +6,7 @@ #include <common.h> #include <init.h> #include <malloc.h> +#include <asm/addrspace.h> #include <linux/bitops.h> #include <linux/io.h> #include <linux/sizes.h> @@ -14,9 +15,7 @@ DECLARE_GLOBAL_DATA_PTR; int dram_init(void) { -#ifdef CONFIG_SKIP_LOWLEVEL_INIT - gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE, SZ_256M); -#endif + gd->ram_size = get_ram_size((void *)KSEG1, SZ_256M); return 0; } diff --git a/arch/mips/mach-mtmips/ddr_init.c b/arch/mips/mach-mtmips/ddr_init.c index 6c6d0933f2e..9c986daea6f 100644 --- a/arch/mips/mach-mtmips/ddr_init.c +++ b/arch/mips/mach-mtmips/ddr_init.c @@ -15,6 +15,13 @@ #define DDR_BW_TEST_PAT 0xaa5555aa +static const u32 sdr_size_cfg1[] = { + [DRAM_8MB] = (1 << NUMROWS_S), + [DRAM_16MB] = (1 << NUMROWS_S) | (1 << NUMCOLS_S), + [DRAM_32MB] = (2 << NUMROWS_S) | (1 << NUMCOLS_S), + [DRAM_64MB] = (2 << NUMROWS_S) | (2 << NUMCOLS_S), +}; + static const u32 dram_size[] = { [DRAM_8MB] = SZ_8M, [DRAM_16MB] = SZ_16M, @@ -193,3 +200,55 @@ void ddr2_init(struct mc_ddr_init_param *param) param->memsize = dram_size[sz]; param->bus_width = bw; } + +static void mc_sdr_init(void __iomem *memc, mc_reset_t mc_reset, u32 cfg0, + u32 cfg1) +{ + mc_reset(1); + __udelay(200); + mc_reset(0); + + writel(cfg0, memc + MEMCTL_SDRAM_CFG0_REG); + writel(cfg1, memc + MEMCTL_SDRAM_CFG1_REG); + + while (!(readl(memc + MEMCTL_SDRAM_CFG1_REG) & SDRAM_INIT_DONE)) + ; + + clrsetbits_32(memc + MEMCTL_PWR_SAVE_CNT_REG, SR_TAR_CNT_M, + 1 << SR_TAR_CNT_S); + + setbits_32(memc + MEMCTL_DDR_SELF_REFRESH_REG, SR_AUTO_EN); +} + +void sdr_init(struct mc_ddr_init_param *param) +{ + enum mc_dram_size sz; + u32 cfg1; + + cfg1 = param->sdr_cfg1 | SDRAM_INIT_START; + cfg1 &= ~(NUMCOLS_M | NUMROWS_M); + + /* First initialization, determine SDR capacity */ + mc_sdr_init(param->memc, param->mc_reset, param->sdr_cfg0, + cfg1 | sdr_size_cfg1[DRAM_64MB]); + + if (dram_addr_test_bit(9)) { + sz = DRAM_8MB; + } else { + if (dram_addr_test_bit(10)) { + if (dram_addr_test_bit(23)) + sz = DRAM_16MB; + else + sz = DRAM_32MB; + } else { + sz = DRAM_64MB; + } + } + + /* Final initialization */ + mc_sdr_init(param->memc, param->mc_reset, param->sdr_cfg0, + cfg1 | sdr_size_cfg1[sz]); + + /* Return actual DDR configuration */ + param->memsize = dram_size[sz]; +} diff --git a/arch/mips/mach-mtmips/include/mach/ddr.h b/arch/mips/mach-mtmips/include/mach/ddr.h index f92198137b1..15ff66ace6a 100644 --- a/arch/mips/mach-mtmips/include/mach/ddr.h +++ b/arch/mips/mach-mtmips/include/mach/ddr.h @@ -35,6 +35,9 @@ typedef void (*mc_reset_t)(int assert); struct mc_ddr_init_param { void __iomem *memc; + u32 sdr_cfg0; + u32 sdr_cfg1; + u32 dq_dly; u32 dqs_dly; @@ -45,6 +48,7 @@ struct mc_ddr_init_param { u32 bus_width; }; +void sdr_init(struct mc_ddr_init_param *param); void ddr1_init(struct mc_ddr_init_param *param); void ddr2_init(struct mc_ddr_init_param *param); void ddr_calibrate(void __iomem *memc, u32 memsize, u32 bw); diff --git a/arch/mips/mach-mtmips/include/mach/mt7620-sysc.h b/arch/mips/mach-mtmips/include/mach/mt7620-sysc.h new file mode 100644 index 00000000000..743ca034c80 --- /dev/null +++ b/arch/mips/mach-mtmips/include/mach/mt7620-sysc.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * + * Definitions of ioctl requests of MT7620 sysc driver + */ + +#ifndef _MT7620_SYSC_H_ +#define _MT7620_SYSC_H_ + +#include <linux/types.h> + +enum mt7620_sysc_requests { + MT7620_SYSC_IOCTL_GET_CLK, + MT7620_SYSC_IOCTL_GET_CHIP_REV, + MT7620_SYSC_IOCTL_SET_GE1_MODE, + MT7620_SYSC_IOCTL_SET_GE2_MODE, + MT7620_SYSC_IOCTL_SET_USB_MODE, + MT7620_SYSC_IOCTL_SET_PCIE_MODE +}; + +struct mt7620_sysc_clks { + u32 cpu_clk; + u32 sys_clk; + u32 xtal_clk; + u32 peri_clk; +}; + +struct mt7620_sysc_chip_rev { + bool bga; + u32 ver : 4; + u32 eco : 4; +}; + +enum mt7620_sysc_ge_mode { + MT7620_SYSC_GE_RGMII, + MT7620_SYSC_GE_MII, + MT7620_SYSC_GE_RMII, + MT7620_SYSC_GE_ESW_PHY, +}; + +enum mt7620_sysc_usb_mode { + MT7620_SYSC_USB_DEVICE_MODE, + MT7620_SYSC_USB_HOST_MODE +}; + +enum mt7620_sysc_pcie_mode { + MT7620_SYSC_PCIE_EP_MODE, + MT7620_SYSC_PCIE_RC_MODE +}; + +#endif /* _MT7620_SYSC_H_ */ diff --git a/arch/mips/mach-mtmips/mt7620/Kconfig b/arch/mips/mach-mtmips/mt7620/Kconfig new file mode 100644 index 00000000000..5db83eb9d9a --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/Kconfig @@ -0,0 +1,71 @@ + +if SOC_MT7620 + +config DEBUG_UART_BOARD_INIT + default y + +choice + prompt "Board select" + +config BOARD_MT7620_RFB + bool "MediaTek MT7620 RFB" + help + The reference design of MT7620A (WS2120). The board has 64 MiB DDR2, + 8 MiB SPI-NOR flash, 1 built-in 6 port switch (two GE PHYs and five + FE PHYs,one port can be configured to use either FE PHY or GE PHY), + 1 UART, 1 USB host, 1 SDXC, 1 PCIe socket and JTAG pins. + +config BOARD_MT7620_MT7530_RFB + bool "MediaTek MT7620-MT7530 RFB" + help + The reference design of MT7620DA (MTKC712). The board has 64 MiB + intergrated DDR2 KGD, 16 MiB SPI-NOR flash, an external 5-port giga + switch MT7530 and 1 UART. + +endchoice + +choice + prompt "CPU frequency select" + default CPU_FREQ_580MHZ + +config CPU_FREQ_480MHZ + bool "480MHz" + +config CPU_FREQ_500MHZ + bool "500MHz" + +config CPU_FREQ_520MHZ + bool "520MHz" + +config CPU_FREQ_540MHZ + bool "540MHz" + +config CPU_FREQ_560MHZ + bool "560MHz" + +config CPU_FREQ_580MHZ + bool "580MHz" + +config CPU_FREQ_600MHZ + bool "600MHz" + +config CPU_FREQ_620MHZ + bool "620MHz" + +endchoice + +config CPU_FREQ_MULTI + int + range 0 7 + default 0 if CPU_FREQ_480MHZ + default 1 if CPU_FREQ_500MHZ + default 2 if CPU_FREQ_520MHZ + default 3 if CPU_FREQ_540MHZ + default 4 if CPU_FREQ_560MHZ + default 5 if CPU_FREQ_580MHZ + default 6 if CPU_FREQ_600MHZ + default 7 if CPU_FREQ_620MHZ + +source "board/mediatek/mt7620/Kconfig" + +endif diff --git a/arch/mips/mach-mtmips/mt7620/Makefile b/arch/mips/mach-mtmips/mt7620/Makefile new file mode 100644 index 00000000000..649f6c3798f --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += lowlevel_init.o +obj-y += init.o +obj-y += dram.o +obj-y += serial.o + +ifndef CONFIG_SPL_BUILD +obj-y += sysc.o +endif diff --git a/arch/mips/mach-mtmips/mt7620/dram.c b/arch/mips/mach-mtmips/mt7620/dram.c new file mode 100644 index 00000000000..0f0e64bf5ea --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/dram.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <asm/addrspace.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/sizes.h> +#include <linux/io.h> +#include <mach/ddr.h> +#include <mach/mc.h> +#include "mt7620.h" + +/* SDR parameters */ +#define SDR_CFG0_VAL 0x51B283B3 +#define SDR_CFG1_VAL 0xC00003A9 + +/* DDR2 DQ_DLY */ +#define DDR2_DQ_DLY 0x88888888 + +/* DDR2 DQS_DLY */ +#define DDR2_DQS_DLY 0x88888888 + +static const struct mc_ddr_cfg ddr1_cfgs_200mhz[] = { + [DRAM_8MB] = { 0x34A1EB94, 0x20262324, 0x28000033, 0x00000002, 0x00000000 }, + [DRAM_16MB] = { 0x34A1EB94, 0x202A2324, 0x28000033, 0x00000002, 0x00000000 }, + [DRAM_32MB] = { 0x34A1E5CA, 0x202E2324, 0x28000033, 0x00000002, 0x00000000 }, + [DRAM_64MB] = { 0x3421E5CA, 0x20322324, 0x28000033, 0x00000002, 0x00000000 }, + [DRAM_128MB] = { 0x241B05CA, 0x20362334, 0x28000033, 0x00000002, 0x00000000 }, +}; + +static const struct mc_ddr_cfg ddr1_cfgs_160mhz[] = { + [DRAM_8MB] = { 0x239964A1, 0x20262323, 0x00000033, 0x00000002, 0x00000000 }, + [DRAM_16MB] = { 0x239964A1, 0x202A2323, 0x00000033, 0x00000002, 0x00000000 }, + [DRAM_32MB] = { 0x239964A1, 0x202E2323, 0x00000033, 0x00000002, 0x00000000 }, + [DRAM_64MB] = { 0x239984A1, 0x20322323, 0x00000033, 0x00000002, 0x00000000 }, + [DRAM_128MB] = { 0x239AB4A1, 0x20362333, 0x00000033, 0x00000002, 0x00000000 }, +}; + +static const struct mc_ddr_cfg ddr2_cfgs_200mhz[] = { + [DRAM_32MB] = { 0x2519E2E5, 0x222E2323, 0x68000C43, 0x00000416, 0x0000000A }, + [DRAM_64MB] = { 0x249AA2E5, 0x22322323, 0x68000C43, 0x00000416, 0x0000000A }, + [DRAM_128MB] = { 0x249B42E5, 0x22362323, 0x68000C43, 0x00000416, 0x0000000A }, + [DRAM_256MB] = { 0x249CE2E5, 0x223A2323, 0x68000C43, 0x00000416, 0x0000000A }, +}; + +static const struct mc_ddr_cfg ddr2_cfgs_160mhz[] = { + [DRAM_32MB] = { 0x23918250, 0x222E2322, 0x40000A43, 0x00000416, 0x00000006 }, + [DRAM_64MB] = { 0x239A2250, 0x22322322, 0x40000A43, 0x00000416, 0x00000008 }, + [DRAM_128MB] = { 0x2392A250, 0x22362322, 0x40000A43, 0x00000416, 0x00000008 }, + [DRAM_256MB] = { 0x24140250, 0x223A2322, 0x40000A43, 0x00000416, 0x00000008 }, +}; + +static void mt7620_memc_reset(int assert) +{ + void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + + if (assert) + setbits_32(sysc + SYSCTL_RSTCTL_REG, MC_RST); + else + clrbits_32(sysc + SYSCTL_RSTCTL_REG, MC_RST); +} + +void mt7620_dram_init(void) +{ + void __iomem *sysc; + bool lspd = false; + int ddr_type, aux; + struct mc_ddr_init_param param; + + sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + ddr_type = (readl(sysc + SYSCTL_SYSCFG0_REG) & DRAM_TYPE_M) + >> DRAM_TYPE_S; + aux = readl(sysc + SYSCTL_CPLL_CFG1_REG) & + (CPU_CLK_AUX1 | CPU_CLK_AUX0); + + if (aux == CPU_CLK_AUX1 || aux == CPU_CLK_AUX0) + lspd = true; + + mt7620_memc_reset(1); + __udelay(200); + + param.memc = ioremap_nocache(MEMCTL_BASE, MEMCTL_SIZE); + param.dq_dly = DDR2_DQ_DLY; + param.dqs_dly = DDR2_DQS_DLY; + param.mc_reset = mt7620_memc_reset; + param.memsize = 0; + param.bus_width = 0; + + if (ddr_type == DRAM_DDR1) { + if (lspd) + param.cfgs = ddr1_cfgs_160mhz; + else + param.cfgs = ddr1_cfgs_200mhz; + + ddr1_init(¶m); + } else if (ddr_type == DRAM_DDR2) { + if (lspd) + param.cfgs = ddr2_cfgs_160mhz; + else + param.cfgs = ddr2_cfgs_200mhz; + + ddr2_init(¶m); + } else { + param.sdr_cfg0 = SDR_CFG0_VAL; + param.sdr_cfg1 = SDR_CFG1_VAL; + + sdr_init(¶m); + } +} diff --git a/arch/mips/mach-mtmips/mt7620/init.c b/arch/mips/mach-mtmips/mt7620/init.c new file mode 100644 index 00000000000..93abf9228d6 --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/init.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <config.h> +#include <asm/global_data.h> +#include <linux/io.h> +#include "mt7620.h" + +DECLARE_GLOBAL_DATA_PTR; + +static const char * const dram_type[] = { + "SDRAM", "DDR", "DDR2", "SDRAM" +}; + +static const char * const boot_mode[(CHIP_MODE_M >> CHIP_MODE_S) + 1] = { + [1] = "NAND 4-cycles 2KB-page", + [2] = "SPI-NOR 3-Byte Addr", + [3] = "SPI-NOR 4-Byte Addr", + [10] = "NAND 4-cycles 512B-page", + [11] = "NAND 5-cycles 2KB-page", + [12] = "NAND 3-cycles 512B-page", +}; + +static void cpu_pll_init(void) +{ + void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + u32 pllmul = CONFIG_CPU_FREQ_MULTI; + + /* Make sure the pll multiplier is valid */ + if (pllmul > 7) + pllmul = 7; + + /* Set init CPU clock to 480MHz */ + clrsetbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPU_CLK_AUX1, CPU_CLK_AUX0); + + /* Enable software control of CPU PLL */ + setbits_32(sysc + SYSCTL_CPLL_CFG0_REG, CPLL_SW_CFG); + + /* CPU PLL power down */ + setbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPLL_PD); + + /* PLL configuration */ + clrsetbits_32(sysc + SYSCTL_CPLL_CFG0_REG, PLL_MULT_RATIO_M | + PLL_DIV_RATIO_M | SSC_UP_BOUND_M | SSC_EN, + (pllmul << PLL_MULT_RATIO_S) | SSC_SWING_M); + + /* CPU PLL power up */ + clrbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPLL_PD); + + /* Wait for CPU PLL locked */ + while (!(readl(sysc + SYSCTL_CPLL_CFG1_REG) & CPLL_LD)) + ; + + /* Set final CPU clock source */ + clrbits_32(sysc + SYSCTL_CPLL_CFG1_REG, CPU_CLK_AUX1 | CPU_CLK_AUX0); + + /* Adjust CPU clock */ + clrsetbits_32(sysc + SYSCTL_CPU_SYS_CLKCFG_REG, + CPU_FDIV_M | CPU_FFRAC_M, + (1 << CPU_FDIV_S) | (1 << CPU_FFRAC_S)); +} + +void mt7620_init(void) +{ + u32 cpu_clk; + + cpu_pll_init(); + + /* + * Set timer freq, which will be used during DRAM initialization + * Note that this function is using a temporary gd which will be + * destroyed after leaving this function. + */ + mt7620_get_clks(&cpu_clk, NULL, NULL); + gd->arch.timer_freq = cpu_clk / 2; + + mt7620_dram_init(); +} + +void mt7620_get_clks(u32 *cpu_clk, u32 *sys_clk, u32 *xtal_clk) +{ + void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + u32 val, multi, div, fdiv, ffrac, dram_type, sys_div; + u32 cpu_freq, xtal_freq; + + static const u32 div_ratio_table[] = {2, 3, 4, 8}; + + val = readl(sysc + SYSCTL_SYSCFG0_REG); + + dram_type = (val & DRAM_TYPE_M) >> DRAM_TYPE_S; + + if (val & XTAL_FREQ_SEL) + xtal_freq = 40000000; + else + xtal_freq = 20000000; + + val = readl(sysc + SYSCTL_CPLL_CFG1_REG); + if (val & CPU_CLK_AUX1) { + cpu_freq = xtal_freq; + } else if (val & CPU_CLK_AUX0) { + cpu_freq = 480000000; + } else { + val = readl(sysc + SYSCTL_CPLL_CFG0_REG); + if (val & CPLL_SW_CFG) { + multi = (val & PLL_MULT_RATIO_M) >> PLL_MULT_RATIO_S; + div = (val & PLL_DIV_RATIO_M) >> PLL_DIV_RATIO_S; + cpu_freq = (multi + 24) * 40000000 / + div_ratio_table[div]; + } else { + cpu_freq = 600000000; + } + } + + val = readl(sysc + SYSCTL_CUR_CLK_STS_REG); + ffrac = (val & CUR_CPU_FFRAC_M) >> CUR_CPU_FFRAC_S; + fdiv = (val & CUR_CPU_FDIV_M) >> CUR_CPU_FDIV_S; + cpu_freq = (cpu_freq * ffrac) / fdiv; + + switch (dram_type) { + case DRAM_SDRAM_E1: + sys_div = 4; + break; + case DRAM_DDR1: + case DRAM_DDR2: + sys_div = 3; + break; + case DRAM_SDRAM: + sys_div = 5; + break; + } + + if (cpu_clk) + *cpu_clk = cpu_freq; + + if (sys_clk) + *sys_clk = cpu_freq / sys_div; + + if (xtal_clk) + *xtal_clk = xtal_freq; +} + +int print_cpuinfo(void) +{ + void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + u32 cpu_clk, bus_clk, xtal_clk; + u32 val, ver, eco, pkg, dram, chipmode; + const char *bootdev; + + val = readl(sysc + SYSCTL_CHIP_REV_ID_REG); + ver = (val & VER_M) >> VER_S; + eco = (val & ECO_M) >> ECO_S; + pkg = !!(val & PKG_ID); + + val = readl(sysc + SYSCTL_SYSCFG0_REG); + dram = (val & DRAM_TYPE_M) >> DRAM_TYPE_S; + chipmode = (val & CHIP_MODE_M) >> CHIP_MODE_S; + + bootdev = boot_mode[chipmode]; + if (!bootdev) + bootdev = "Unsupported boot mode"; + + printf("CPU: MediaTek MT7620%c ver:%u eco:%u\n", + pkg ? 'A' : 'N', ver, eco); + + printf("Boot: %s, %s\n", dram_type[dram], bootdev); + + mt7620_get_clks(&cpu_clk, &bus_clk, &xtal_clk); + + /* Set final timer frequency */ + gd->arch.timer_freq = cpu_clk / 2; + + printf("Clock: CPU: %uMHz, Bus: %uMHz, XTAL: %uMHz\n", + cpu_clk / 1000000, bus_clk / 1000000, xtal_clk / 1000000); + + return 0; +} + +ulong notrace get_tbclk(void) +{ + return gd->arch.timer_freq; +} + +void _machine_restart(void) +{ + void __iomem *sysc = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + + while (1) + writel(SYS_RST, sysc + SYSCTL_RSTCTL_REG); +} diff --git a/arch/mips/mach-mtmips/mt7620/lowlevel_init.S b/arch/mips/mach-mtmips/mt7620/lowlevel_init.S new file mode 100644 index 00000000000..399174620d5 --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/lowlevel_init.S @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <config.h> +#include <asm-offsets.h> +#include <asm/regdef.h> +#include <asm/asm.h> + + .set noreorder + +NESTED(lowlevel_init, 0, ra) + /* Save ra and do real lowlevel initialization */ + move s0, ra + + /* + * Use SRAM from 802.11n MAC/BBP, 16KiB (0x10184000 ~ 0x10187fff) + * NOTE: non-word operations may fail in this SRAM. + * Use it as stack only for CPU/DRAM init which only has word operations. + */ + PTR_LI sp, 0xb0187f00 + + /* We still need a temporary gd for udelay */ + PTR_SUBU \ + sp, sp, GD_SIZE # reserve space for gd + li t0, -16 + and sp, sp, t0 # force 16 byte alignment + move k0, sp # save gd pointer + + move fp, sp + + /* Clear gd */ + move t0, k0 +1: + PTR_S zero, 0(t0) + PTR_ADDIU t0, PTRSIZE + blt t0, t1, 1b + nop + + /* Do CPU & DRAM initialization */ + PTR_LA t9, mt7620_init + jalr t9 + nop + + /* Restore ra */ + move ra, s0 + + jr ra + nop + END(lowlevel_init) diff --git a/arch/mips/mach-mtmips/mt7620/mt7620.h b/arch/mips/mach-mtmips/mt7620/mt7620.h new file mode 100644 index 00000000000..dd5e6d00e5a --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/mt7620.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#ifndef _MT7620_H_ +#define _MT7620_H_ + +#include <linux/bitops.h> + +#define SYSCTL_BASE 0x10000000 +#define SYSCTL_SIZE 0x100 +#define MEMCTL_BASE 0x10000300 +#define MEMCTL_SIZE 0x100 +#define UARTFULL_BASE 0x10000500 +#define UARTFULL_SIZE 0x100 +#define UARTLITE_BASE 0x10000c00 +#define UARTLITE_SIZE 0x100 + +#define SYSCTL_CHIP_REV_ID_REG 0x0c +#define PKG_ID BIT(16) +#define PKG_ID_A 1 +#define PKG_ID_N 0 +#define VER_S 8 +#define VER_M GENMASK(11, 8) +#define ECO_S 0 +#define ECO_M GENMASK(3, 0) + +#define SYSCTL_SYSCFG0_REG 0x10 +#define XTAL_FREQ_SEL BIT(6) +#define XTAL_40MHZ 1 +#define XTAL_20MHZ 0 +#define DRAM_TYPE_S 4 +#define DRAM_TYPE_M GENMASK(5, 4) +#define DRAM_SDRAM 3 +#define DRAM_DDR2 2 +#define DRAM_DDR1 1 +#define DRAM_SDRAM_E1 0 +#define CHIP_MODE_S 0 +#define CHIP_MODE_M GENMASK(3, 0) + +#define SYSCTL_SYSCFG1_REG 0x14 +#define GE2_MODE_S 14 +#define GE2_MODE_M GENMASK(15, 14) +#define GE1_MODE_S 12 +#define GE1_MODE_M GENMASK(13, 12) +#define USB0_HOST_MODE BIT(10) +#define PCIE_RC_MODE BIT(8) +#define GE_MODE_M GENMASK(1, 0) + +#define SYSCTL_RSTCTL_REG 0x34 +#define MC_RST BIT(10) +#define SYS_RST BIT(0) + +#define SYSCTL_CLKCFG0_REG 0x2c +#define PERI_CLK_SEL BIT(4) + +#define SYSCTL_CPU_SYS_CLKCFG_REG 0x3c +#define CPU_OCP_RATIO_S 16 +#define CPU_OCP_RATIO_M GENMASK(19, 16) +#define CPU_FDIV_S 8 +#define CPU_FDIV_M GENMASK(12, 8) +#define CPU_FFRAC_S 0 +#define CPU_FFRAC_M GENMASK(4, 0) + +#define SYSCTL_CUR_CLK_STS_REG 0x44 +#define CUR_CPU_OCP_RATIO_S 16 +#define CUR_CPU_OCP_RATIO_M GENMASK(19, 16) +#define CUR_CPU_FDIV_S 8 +#define CUR_CPU_FDIV_M GENMASK(12, 8) +#define CUR_CPU_FFRAC_S 0 +#define CUR_CPU_FFRAC_M GENMASK(4, 0) + +#define SYSCTL_CPLL_CFG0_REG 0x54 +#define CPLL_SW_CFG BIT(31) +#define PLL_MULT_RATIO_S 16 +#define PLL_MULT_RATIO_M GENMASK(18, 16) +#define PLL_DIV_RATIO_S 10 +#define PLL_DIV_RATIO_M GENMASK(11, 10) +#define SSC_UP_BOUND_S 8 +#define SSC_UP_BOUND_M GENMASK(9, 8) +#define SSC_EN BIT(7) +#define SSC_SWING_S 4 +#define SSC_SWING_M GENMASK(6, 4) + +#define SYSCTL_CPLL_CFG1_REG 0x58 +#define CPLL_PD BIT(26) +#define CPU_CLK_AUX1 BIT(25) +#define CPU_CLK_AUX0 BIT(24) +#define CPLL_LD BIT(23) + +#define SYSCTL_GPIOMODE_REG 0x60 +#define UARTL_GPIO_MODE BIT(5) +#define UARTF_SHARE_MODE_S 2 +#define UARTF_SHARE_MODE_M GENMASK(4, 2) +#define UARTF_MODE_UARTF_GPIO 5 + +void mt7620_dram_init(void); +void mt7620_get_clks(u32 *cpu_clk, u32 *sys_clk, u32 *xtal_clk); + +#endif /* _MT7620_H_ */ diff --git a/arch/mips/mach-mtmips/mt7620/serial.c b/arch/mips/mach-mtmips/mt7620/serial.c new file mode 100644 index 00000000000..44f061cd6cc --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/serial.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <asm/io.h> +#include <asm/addrspace.h> +#include "mt7620.h" + +void board_debug_uart_init(void) +{ + void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + +#if CONFIG_DEBUG_UART_BASE == 0xb0000500 /* KSEG1ADDR(UARTFULL_BASE) */ + clrsetbits_32(base + SYSCTL_GPIOMODE_REG, UARTF_SHARE_MODE_M, + UARTF_MODE_UARTF_GPIO << UARTF_SHARE_MODE_S); +#else + clrbits_32(base + SYSCTL_GPIOMODE_REG, UARTL_GPIO_MODE); +#endif +} + +void mtmips_spl_serial_init(void) +{ +#ifdef CONFIG_SPL_SERIAL_SUPPORT + void __iomem *base = ioremap_nocache(SYSCTL_BASE, SYSCTL_SIZE); + +#if CONFIG_CONS_INDEX == 1 + clrbits_32(base + SYSCTL_GPIOMODE_REG, UARTL_GPIO_MODE); +#elif CONFIG_CONS_INDEX == 2 + clrsetbits_32(base + SYSCTL_GPIOMODE_REG, UARTF_SHARE_MODE_M, + UARTF_MODE_UARTF_GPIO << UARTF_SHARE_MODE_S); +#endif +#endif /* CONFIG_SPL_SERIAL_SUPPORT */ +} diff --git a/arch/mips/mach-mtmips/mt7620/sysc.c b/arch/mips/mach-mtmips/mt7620/sysc.c new file mode 100644 index 00000000000..296a9be07c5 --- /dev/null +++ b/arch/mips/mach-mtmips/mt7620/sysc.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * + * Misc driver for manipulating System control registers + */ + +#include <dm.h> +#include <misc.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <dm/device_compat.h> +#include <mach/mt7620-sysc.h> +#include "mt7620.h" + +struct mt7620_sysc_priv { + void __iomem *base; +}; + +static int mt7620_sysc_read(struct udevice *dev, int offset, void *buf, + int size) +{ + struct mt7620_sysc_priv *priv = dev_get_priv(dev); + u32 val; + + if (offset % sizeof(u32) || size != sizeof(u32) || + offset >= SYSCTL_SIZE) + return -EINVAL; + + val = readl(priv->base + offset); + + if (buf) + *(u32 *)buf = val; + + return 0; +} + +static int mt7620_sysc_write(struct udevice *dev, int offset, const void *buf, + int size) +{ + struct mt7620_sysc_priv *priv = dev_get_priv(dev); + u32 val; + + if (offset % sizeof(u32) || size != sizeof(u32) || + offset >= SYSCTL_SIZE || !buf) + return -EINVAL; + + val = *(u32 *)buf; + writel(val, priv->base + offset); + + return 0; +} + +static int mt7620_sysc_ioctl(struct udevice *dev, unsigned long request, + void *buf) +{ + struct mt7620_sysc_priv *priv = dev_get_priv(dev); + struct mt7620_sysc_chip_rev *chip_rev; + struct mt7620_sysc_clks *clks; + u32 val, shift; + + if (!buf) + return -EINVAL; + + switch (request) { + case MT7620_SYSC_IOCTL_GET_CLK: + clks = buf; + mt7620_get_clks(&clks->cpu_clk, &clks->sys_clk, + &clks->xtal_clk); + + val = readl(priv->base + SYSCTL_CLKCFG0_REG); + if (val & PERI_CLK_SEL) + clks->peri_clk = clks->xtal_clk; + else + clks->peri_clk = 40000000; + + return 0; + + case MT7620_SYSC_IOCTL_GET_CHIP_REV: + chip_rev = buf; + + val = readl(priv->base + SYSCTL_CHIP_REV_ID_REG); + + chip_rev->bga = !!(val & PKG_ID); + chip_rev->ver = (val & VER_M) >> VER_S; + chip_rev->eco = (val & ECO_M) >> ECO_S; + + return 0; + + case MT7620_SYSC_IOCTL_SET_GE1_MODE: + case MT7620_SYSC_IOCTL_SET_GE2_MODE: + val = *(u32 *)buf; + + if (val > MT7620_SYSC_GE_ESW_PHY) + return -EINVAL; + + if (request == MT7620_SYSC_IOCTL_SET_GE1_MODE) + shift = GE1_MODE_S; + else + shift = GE2_MODE_S; + + clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG, + GE_MODE_M << shift, val << shift); + + return 0; + + case MT7620_SYSC_IOCTL_SET_USB_MODE: + val = *(u32 *)buf; + + if (val == MT7620_SYSC_USB_DEVICE_MODE) + val = 0; + else if (val == MT7620_SYSC_USB_HOST_MODE) + val = USB0_HOST_MODE; + + clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG, + USB0_HOST_MODE, val); + + return 0; + + case MT7620_SYSC_IOCTL_SET_PCIE_MODE: + val = *(u32 *)buf; + + if (val == MT7620_SYSC_PCIE_EP_MODE) + val = 0; + else if (val == MT7620_SYSC_PCIE_RC_MODE) + val = PCIE_RC_MODE; + + clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG, + PCIE_RC_MODE, val); + + return 0; + + default: + return -EINVAL; + } +} + +static int mt7620_sysc_probe(struct udevice *dev) +{ + struct mt7620_sysc_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr_index(dev, 0); + if (!priv->base) { + dev_err(dev, "failed to map sysc registers\n"); + return -EINVAL; + } + + return 0; +} + +static struct misc_ops mt7620_sysc_ops = { + .read = mt7620_sysc_read, + .write = mt7620_sysc_write, + .ioctl = mt7620_sysc_ioctl, +}; + +static const struct udevice_id mt7620_sysc_ids[] = { + { .compatible = "mediatek,mt7620-sysc" }, + { } +}; + +U_BOOT_DRIVER(mt7620_sysc) = { + .name = "mt7620_sysc", + .id = UCLASS_MISC, + .of_match = mt7620_sysc_ids, + .probe = mt7620_sysc_probe, + .ops = &mt7620_sysc_ops, + .priv_auto = sizeof(struct mt7620_sysc_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/arch/mips/mach-mtmips/mt7628/Kconfig b/arch/mips/mach-mtmips/mt7628/Kconfig new file mode 100644 index 00000000000..e3f56e782eb --- /dev/null +++ b/arch/mips/mach-mtmips/mt7628/Kconfig @@ -0,0 +1,53 @@ + +if SOC_MT7628 + +choice + prompt "Board select" + +config BOARD_GARDENA_SMART_GATEWAY_MT7688 + bool "GARDENA smart Gateway" + select BOARD_LATE_INIT + help + GARDENA smart Gateway boards have a MT7688 SoC with 128 MiB of RAM + and 8 MiB of flash (SPI NOR) and additional SPI NAND storage. + +config BOARD_LINKIT_SMART_7688 + bool "LinkIt Smart 7688" + help + Seeed LinkIt Smart 7688 boards have a MT7688 SoC with 128 MiB of RAM + and 32 MiB of flash (SPI). + Between its different peripherals there's an integrated switch with 4 + ethernet ports, 1 USB port, 1 UART, GPIO buttons and LEDs, and + a MT7688 (PCIe). + +config BOARD_MT7628_RFB + bool "MediaTek MT7628 RFB" + help + The reference design of MT7628. The board has 128 MiB DDR2, 8 MiB + SPI-NOR flash, 1 built-in switch with 5 ports, 1 UART, 1 USB host, + 1 SDXC, 1 PCIe socket and JTAG pins. + +config BOARD_VOCORE2 + bool "VoCore2" + select SPL_SERIAL_SUPPORT + select SPL_UART2_SPIS_PINMUX + help + VoCore VoCore2 board has a MT7628 SoC with 128 MiB of RAM + and 16 MiB of flash (SPI). + +endchoice + +config SPL_UART2_SPIS_PINMUX + bool "Use alternative pinmux for UART2 in SPL stage" + depends on SPL_SERIAL_SUPPORT + default n + help + Select this if the UART2 of your board is connected to GPIO 16/17 + (shared with SPIS) rather than the usual GPIO 20/21. + +source "board/gardena/smart-gateway-mt7688/Kconfig" +source "board/mediatek/mt7628/Kconfig" +source "board/seeed/linkit-smart-7688/Kconfig" +source "board/vocore/vocore2/Kconfig" + +endif diff --git a/board/mediatek/mt7620/Kconfig b/board/mediatek/mt7620/Kconfig new file mode 100644 index 00000000000..b9137adcc99 --- /dev/null +++ b/board/mediatek/mt7620/Kconfig @@ -0,0 +1,12 @@ +if BOARD_MT7620_RFB || BOARD_MT7620_MT7530_RFB + +config SYS_BOARD + default "mt7620" + +config SYS_VENDOR + default "mediatek" + +config SYS_CONFIG_NAME + default "mt7620" + +endif diff --git a/board/mediatek/mt7620/MAINTAINERS b/board/mediatek/mt7620/MAINTAINERS new file mode 100644 index 00000000000..05db777ced5 --- /dev/null +++ b/board/mediatek/mt7620/MAINTAINERS @@ -0,0 +1,9 @@ +MT7620_RFB BOARD +M: Weijie Gao <weijie.gao@mediatek.com> +S: Maintained +F: board/mediatek/mt7620 +F: include/configs/mt7620.h +F: configs/mt7620_rfb_defconfig +F: configs/mt7620_mt7530_rfb_defconfig +F: arch/mips/dts/mediatek,mt7620-rfb.dts +F: arch/mips/dts/mediatek,mt7620-mt7530-rfb.dts diff --git a/board/mediatek/mt7620/Makefile b/board/mediatek/mt7620/Makefile new file mode 100644 index 00000000000..db129c5abae --- /dev/null +++ b/board/mediatek/mt7620/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += board.o diff --git a/board/mediatek/mt7620/board.c b/board/mediatek/mt7620/board.c new file mode 100644 index 00000000000..119b8fc9e56 --- /dev/null +++ b/board/mediatek/mt7620/board.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ diff --git a/configs/gardena-smart-gateway-mt7688_defconfig b/configs/gardena-smart-gateway-mt7688_defconfig index 001a66d0f6c..c03123f6e46 100644 --- a/configs/gardena-smart-gateway-mt7688_defconfig +++ b/configs/gardena-smart-gateway-mt7688_defconfig @@ -12,6 +12,7 @@ CONFIG_SPL=y CONFIG_SYS_BOOTCOUNT_SINGLEWORD=y CONFIG_ENV_OFFSET_REDUND=0xB0000 CONFIG_ARCH_MTMIPS=y +CONFIG_SOC_MT7628=y CONFIG_DEFAULT_DEVICE_TREE="gardena-smart-gateway-mt7688" CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y # CONFIG_MIPS_BOOT_ENV_LEGACY is not set diff --git a/configs/linkit-smart-7688_defconfig b/configs/linkit-smart-7688_defconfig index 437000b4c79..dd2524e4ec6 100644 --- a/configs/linkit-smart-7688_defconfig +++ b/configs/linkit-smart-7688_defconfig @@ -9,6 +9,7 @@ CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000 CONFIG_SPL=y CONFIG_ARCH_MTMIPS=y +CONFIG_SOC_MT7628=y CONFIG_BOARD_LINKIT_SMART_7688=y CONFIG_DEFAULT_DEVICE_TREE="linkit-smart-7688" CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y diff --git a/configs/mt7620_mt7530_rfb_defconfig b/configs/mt7620_mt7530_rfb_defconfig new file mode 100644 index 00000000000..341d86838c2 --- /dev/null +++ b/configs/mt7620_mt7530_rfb_defconfig @@ -0,0 +1,58 @@ +CONFIG_MIPS=y +CONFIG_SPL_LIBCOMMON_SUPPORT=y +CONFIG_SPL_LIBGENERIC_SUPPORT=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_ENV_SIZE=0x1000 +CONFIG_ENV_OFFSET=0x30000 +CONFIG_ENV_SECT_SIZE=0x10000 +CONFIG_SPL_SERIAL_SUPPORT=y +CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000 +CONFIG_SPL=y +CONFIG_DEBUG_UART_BASE=0xb0000c00 +CONFIG_DEBUG_UART_CLOCK=40000000 +CONFIG_ARCH_MTMIPS=y +CONFIG_BOARD_MT7620_MT7530_RFB=y +CONFIG_DEFAULT_DEVICE_TREE="mediatek,mt7620-mt7530-rfb" +CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEBUG_UART=y +CONFIG_FIT=y +# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set +CONFIG_SPL_SYS_MALLOC_SIMPLE=y +CONFIG_SPL_NOR_SUPPORT=y +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_CRC32 is not set +# CONFIG_CMD_DM is not set +CONFIG_CMD_GPIO=y +# CONFIG_CMD_LOADS is not set +CONFIG_CMD_SPI=y +# CONFIG_CMD_NFS is not set +CONFIG_CMD_MII=y +# CONFIG_CMD_MDIO is not set +# CONFIG_PARTITIONS is not set +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clocks clock-names interrupt-parent interrupts resets reset-names" +CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SPL_DM=y +# CONFIG_SIMPLE_BUS is not set +# CONFIG_SPL_SIMPLE_BUS is not set +CONFIG_GPIO_HOG=y +# CONFIG_INPUT is not set +CONFIG_SPI_FLASH_SFDP_SUPPORT=y +CONFIG_SPI_FLASH_EON=y +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_ISSI=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_XMC=y +CONFIG_MT7620_ETH=y +CONFIG_SPECIFY_CONSOLE_INDEX=y +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_SPI=y +CONFIG_MT7620_SPI=y +CONFIG_LZMA=y +CONFIG_SPL_LZMA=y diff --git a/configs/mt7620_rfb_defconfig b/configs/mt7620_rfb_defconfig new file mode 100644 index 00000000000..98293715137 --- /dev/null +++ b/configs/mt7620_rfb_defconfig @@ -0,0 +1,76 @@ +CONFIG_MIPS=y +CONFIG_SPL_LIBCOMMON_SUPPORT=y +CONFIG_SPL_LIBGENERIC_SUPPORT=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_ENV_SIZE=0x1000 +CONFIG_ENV_OFFSET=0x30000 +CONFIG_ENV_SECT_SIZE=0x10000 +CONFIG_SPL_SERIAL_SUPPORT=y +CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000 +CONFIG_SPL=y +CONFIG_DEBUG_UART_BASE=0xb0000c00 +CONFIG_DEBUG_UART_CLOCK=40000000 +CONFIG_ARCH_MTMIPS=y +CONFIG_DEFAULT_DEVICE_TREE="mediatek,mt7620-rfb" +CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y +CONFIG_MIPS_BOOT_FDT=y +CONFIG_DEBUG_UART=y +CONFIG_FIT=y +# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set +CONFIG_SPL_SYS_MALLOC_SIMPLE=y +CONFIG_SPL_NOR_SUPPORT=y +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_CRC32 is not set +# CONFIG_CMD_DM is not set +CONFIG_CMD_GPIO=y +# CONFIG_CMD_LOADS is not set +CONFIG_CMD_MMC=y +CONFIG_CMD_SPI=y +CONFIG_CMD_USB=y +# CONFIG_CMD_NFS is not set +CONFIG_CMD_MII=y +# CONFIG_CMD_MDIO is not set +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +# CONFIG_SPL_DOS_PARTITION is not set +# CONFIG_ISO_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SPL_EFI_PARTITION is not set +CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clocks clock-names interrupt-parent interrupts resets reset-names" +CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SPL_DM=y +# CONFIG_SIMPLE_BUS is not set +# CONFIG_SPL_SIMPLE_BUS is not set +# CONFIG_SPL_BLK is not set +CONFIG_GPIO_HOG=y +# CONFIG_INPUT is not set +CONFIG_MMC=y +CONFIG_DM_MMC=y +# CONFIG_MMC_QUIRKS is not set +# CONFIG_MMC_HW_PARTITIONING is not set +CONFIG_MMC_MTK=y +CONFIG_SPI_FLASH_SFDP_SUPPORT=y +CONFIG_SPI_FLASH_EON=y +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_ISSI=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_XMC=y +CONFIG_MT7620_ETH=y +CONFIG_PHY=y +CONFIG_MT7620_USB_PHY=y +CONFIG_SPECIFY_CONSOLE_INDEX=y +CONFIG_DEBUG_UART_SHIFT=2 +CONFIG_SPI=y +CONFIG_MT7620_SPI=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_GENERIC=y +CONFIG_LZMA=y +CONFIG_SPL_LZMA=y diff --git a/configs/mt7628_rfb_defconfig b/configs/mt7628_rfb_defconfig index 69025728b2f..831e54e9f35 100644 --- a/configs/mt7628_rfb_defconfig +++ b/configs/mt7628_rfb_defconfig @@ -9,6 +9,7 @@ CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000 CONFIG_SPL=y CONFIG_ARCH_MTMIPS=y +CONFIG_SOC_MT7628=y CONFIG_BOARD_MT7628_RFB=y CONFIG_DEFAULT_DEVICE_TREE="mediatek,mt7628-rfb" CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y diff --git a/configs/vocore2_defconfig b/configs/vocore2_defconfig index acb9abf1be5..e2558293660 100644 --- a/configs/vocore2_defconfig +++ b/configs/vocore2_defconfig @@ -10,6 +10,7 @@ CONFIG_SPL_SYS_MALLOC_F_LEN=0x40000 CONFIG_SPL=y CONFIG_SYS_BOOTCOUNT_SINGLEWORD=y CONFIG_ARCH_MTMIPS=y +CONFIG_SOC_MT7628=y CONFIG_BOARD_VOCORE2=y CONFIG_DEFAULT_DEVICE_TREE="vocore_vocore2" CONFIG_RESTORE_EXCEPTION_VECTOR_BASE=y diff --git a/drivers/clk/mtmips/Makefile b/drivers/clk/mtmips/Makefile index e1938418dae..732e7f25453 100644 --- a/drivers/clk/mtmips/Makefile +++ b/drivers/clk/mtmips/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SOC_MT7620) += clk-mt7620.o obj-$(CONFIG_SOC_MT7628) += clk-mt7628.o diff --git a/drivers/clk/mtmips/clk-mt7620.c b/drivers/clk/mtmips/clk-mt7620.c new file mode 100644 index 00000000000..57d2e2f04c6 --- /dev/null +++ b/drivers/clk/mtmips/clk-mt7620.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <clk-uclass.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dt-bindings/clock/mt7620-clk.h> +#include <misc.h> +#include <mach/mt7620-sysc.h> + +/* CLKCFG1 */ +#define CLKCFG1_REG 0x30 + +#define CLK_SRC_CPU -1 +#define CLK_SRC_CPU_D2 -2 +#define CLK_SRC_SYS -3 +#define CLK_SRC_XTAL -4 +#define CLK_SRC_PERI -5 + +struct mt7620_clk_priv { + struct udevice *dev; + struct udevice *sysc; + struct mt7620_sysc_clks clks; +}; + +static const int mt7620_clks[] = { + [CLK_SYS] = CLK_SRC_SYS, + [CLK_CPU] = CLK_SRC_CPU, + [CLK_XTAL] = CLK_SRC_XTAL, + [CLK_MIPS_CNT] = CLK_SRC_CPU_D2, + [CLK_UARTF] = CLK_SRC_PERI, + [CLK_UARTL] = CLK_SRC_PERI, + [CLK_SPI] = CLK_SRC_SYS, + [CLK_I2C] = CLK_SRC_PERI, + [CLK_I2S] = CLK_SRC_PERI, +}; + +static ulong mt7620_clk_get_rate(struct clk *clk) +{ + struct mt7620_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= ARRAY_SIZE(mt7620_clks)) + return 0; + + switch (mt7620_clks[clk->id]) { + case CLK_SRC_CPU: + return priv->clks.cpu_clk; + case CLK_SRC_CPU_D2: + return priv->clks.cpu_clk / 2; + case CLK_SRC_SYS: + return priv->clks.sys_clk; + case CLK_SRC_XTAL: + return priv->clks.xtal_clk; + case CLK_SRC_PERI: + return priv->clks.peri_clk; + default: + return mt7620_clks[clk->id]; + } +} + +static int mt7620_clkcfg1_rmw(struct mt7620_clk_priv *priv, u32 clr, u32 set) +{ + u32 val; + int ret; + + ret = misc_read(priv->sysc, CLKCFG1_REG, &val, sizeof(val)); + if (ret) { + dev_err(priv->dev, "mt7620_clk: failed to read CLKCFG1\n"); + return ret; + } + + val &= ~clr; + val |= set; + + ret = misc_write(priv->sysc, CLKCFG1_REG, &val, sizeof(val)); + if (ret) { + dev_err(priv->dev, "mt7620_clk: failed to write CLKCFG1\n"); + return ret; + } + + return 0; +} + +static int mt7620_clk_enable(struct clk *clk) +{ + struct mt7620_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id > 30) + return -1; + + return mt7620_clkcfg1_rmw(priv, 0, BIT(clk->id)); +} + +static int mt7620_clk_disable(struct clk *clk) +{ + struct mt7620_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id > 30) + return -1; + + return mt7620_clkcfg1_rmw(priv, BIT(clk->id), 0); +} + +const struct clk_ops mt7620_clk_ops = { + .enable = mt7620_clk_enable, + .disable = mt7620_clk_disable, + .get_rate = mt7620_clk_get_rate, +}; + +static int mt7620_clk_probe(struct udevice *dev) +{ + struct mt7620_clk_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args sysc_args; + int ret; + + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "mediatek,sysc", NULL, + 0, 0, &sysc_args); + if (ret) { + dev_err(dev, "mt7620_clk: sysc property not found\n"); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_MISC, sysc_args.node, + &priv->sysc); + if (ret) { + dev_err(dev, "mt7620_clk: failed to sysc device\n"); + return ret; + } + + ret = misc_ioctl(priv->sysc, MT7620_SYSC_IOCTL_GET_CLK, + &priv->clks); + if (ret) { + dev_err(dev, "mt7620_clk: failed to get base clocks\n"); + return ret; + } + + priv->dev = dev; + + return 0; +} + +static const struct udevice_id mt7620_clk_ids[] = { + { .compatible = "mediatek,mt7620-clk" }, + { } +}; + +U_BOOT_DRIVER(mt7620_clk) = { + .name = "mt7620-clk", + .id = UCLASS_CLK, + .of_match = mt7620_clk_ids, + .probe = mt7620_clk_probe, + .priv_auto = sizeof(struct mt7620_clk_priv), + .ops = &mt7620_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 202fcc6f475..7e94759a796 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -471,6 +471,14 @@ config MPC83XX_SPISEL_BOOT This pin is typically used as spi chip select to a spi nor flash. +config MT7620_GPIO + bool "MediaTek MT7620 GPIO driver" + depends on DM_GPIO && SOC_MT7620 + default y + help + Device model driver for GPIO controller present in MediaTek MT7620 + and earlier SoCs. + config MT7621_GPIO bool "MediaTek MT7621 GPIO driver" depends on DM_GPIO && SOC_MT7628 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d3d0d3cacf0..8541ba0b0ae 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o +obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_NX_GPIO) += nx_gpio.o diff --git a/drivers/gpio/mt7620_gpio.c b/drivers/gpio/mt7620_gpio.c new file mode 100644 index 00000000000..713fa2ed78b --- /dev/null +++ b/drivers/gpio/mt7620_gpio.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * + * GPIO controller driver for MediaTek MT7620 SoC + */ + +#include <dm.h> +#include <errno.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <asm/gpio.h> + +enum mt7620_regs { + GPIO_REG_DATA, + GPIO_REG_DIR, + GPIO_REG_SET, + GPIO_REG_CLR, + + __GPIO_REG_MAX +}; + +struct mt7620_gpio_priv { + void __iomem *base; + u32 regs[__GPIO_REG_MAX]; + u32 count; +}; + +static int mt7620_gpio_get_value(struct udevice *dev, unsigned int offset) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + + return !!(readl(priv->base + priv->regs[GPIO_REG_DATA]) & BIT(offset)); +} + +static int mt7620_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + u32 reg; + + reg = value ? priv->regs[GPIO_REG_SET] : priv->regs[GPIO_REG_CLR]; + + writel(BIT(offset), priv->base + reg); + + return 0; +} + +static int mt7620_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + + clrbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset)); + + return 0; +} + +static int mt7620_gpio_direction_output(struct udevice *dev, + unsigned int offset, int value) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + + /* Set value first */ + mt7620_gpio_set_value(dev, offset, value); + + setbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset)); + + return 0; +} + +static int mt7620_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + + return (readl(priv->base + priv->regs[GPIO_REG_DIR]) & BIT(offset)) ? + GPIOF_OUTPUT : GPIOF_INPUT; +} + +static const struct dm_gpio_ops mt7620_gpio_ops = { + .direction_input = mt7620_gpio_direction_input, + .direction_output = mt7620_gpio_direction_output, + .get_value = mt7620_gpio_get_value, + .set_value = mt7620_gpio_set_value, + .get_function = mt7620_gpio_get_function, +}; + +static int mt7620_gpio_probe(struct udevice *dev) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + const char *name; + + name = dev_read_string(dev, "mediatek,bank-name"); + if (!name) + name = dev->name; + + uc_priv->gpio_count = priv->count; + uc_priv->bank_name = name; + + return 0; +} + +static int mt7620_gpio_of_to_plat(struct udevice *dev) +{ + struct mt7620_gpio_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_remap_addr_index(dev, 0); + if (!priv->base) { + dev_err(dev, "mt7620_gpio: unable to map registers\n"); + return -EINVAL; + } + + ret = dev_read_u32(dev, "mediatek,gpio-num", &priv->count); + if (ret) { + dev_err(dev, "mt7620_gpio: failed to get GPIO count\n"); + return -EINVAL; + } + + ret = dev_read_u32_array(dev, "mediatek,register-map", priv->regs, + __GPIO_REG_MAX); + if (ret) { + dev_err(dev, "mt7620_gpio: unable to get register map\n"); + return -EINVAL; + } + + return 0; +} + +static const struct udevice_id mt7620_gpio_ids[] = { + { .compatible = "mediatek,mt7620-gpio" }, + { } +}; + +U_BOOT_DRIVER(mt7620_gpio) = { + .name = "mt7620_gpio", + .id = UCLASS_GPIO, + .ops = &mt7620_gpio_ops, + .of_match = mt7620_gpio_ids, + .probe = mt7620_gpio_probe, + .of_to_plat = mt7620_gpio_of_to_plat, + .priv_auto = sizeof(struct mt7620_gpio_priv), +}; diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index 95dc9daee96..3b9c12266a2 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -113,7 +113,51 @@ #define MSDC_PB2_RESPWAIT_M 0x0c #define MSDC_PB2_RESPWAIT_S 2 +/* MSDC_PAD_CTRL0 */ +#define MSDC_PAD_CTRL0_CLKRDSEL_M 0xff000000 +#define MSDC_PAD_CTRL0_CLKRDSEL_S 24 +#define MSDC_PAD_CTRL0_CLKTDSEL BIT(20) +#define MSDC_PAD_CTRL0_CLKIES BIT(19) +#define MSDC_PAD_CTRL0_CLKSMT BIT(18) +#define MSDC_PAD_CTRL0_CLKPU BIT(17) +#define MSDC_PAD_CTRL0_CLKPD BIT(16) +#define MSDC_PAD_CTRL0_CLKSR BIT(8) +#define MSDC_PAD_CTRL0_CLKDRVP_M 0x70 +#define MSDC_PAD_CTRL0_CLKDRVP_S 4 +#define MSDC_PAD_CTRL0_CLKDRVN_M 0x7 +#define MSDC_PAD_CTRL0_CLKDRVN_S 0 + +/* MSDC_PAD_CTRL1 */ +#define MSDC_PAD_CTRL1_CMDRDSEL_M 0xff000000 +#define MSDC_PAD_CTRL1_CMDRDSEL_S 24 +#define MSDC_PAD_CTRL1_CMDTDSEL BIT(20) +#define MSDC_PAD_CTRL1_CMDIES BIT(19) +#define MSDC_PAD_CTRL1_CMDSMT BIT(18) +#define MSDC_PAD_CTRL1_CMDPU BIT(17) +#define MSDC_PAD_CTRL1_CMDPD BIT(16) +#define MSDC_PAD_CTRL1_CMDSR BIT(8) +#define MSDC_PAD_CTRL1_CMDDRVP_M 0x70 +#define MSDC_PAD_CTRL1_CMDDRVP_S 4 +#define MSDC_PAD_CTRL1_CMDDRVN_M 0x7 +#define MSDC_PAD_CTRL1_CMDDRVN_S 0 + +/* MSDC_PAD_CTRL2 */ +#define MSDC_PAD_CTRL2_DATRDSEL_M 0xff000000 +#define MSDC_PAD_CTRL2_DATRDSEL_S 24 +#define MSDC_PAD_CTRL2_DATTDSEL BIT(20) +#define MSDC_PAD_CTRL2_DATIES BIT(19) +#define MSDC_PAD_CTRL2_DATSMT BIT(18) +#define MSDC_PAD_CTRL2_DATPU BIT(17) +#define MSDC_PAD_CTRL2_DATPD BIT(16) +#define MSDC_PAD_CTRL2_DATSR BIT(8) +#define MSDC_PAD_CTRL2_DATDRVP_M 0x70 +#define MSDC_PAD_CTRL2_DATDRVP_S 4 +#define MSDC_PAD_CTRL2_DATDRVN_M 0x7 +#define MSDC_PAD_CTRL2_DATDRVN_S 0 + /* PAD_TUNE */ +#define MSDC_PAD_TUNE_CLKTDLY_M 0xf8000000 +#define MSDC_PAD_TUNE_CLKTDLY_S 27 #define MSDC_PAD_TUNE_CMDRRDLY_M 0x7c00000 #define MSDC_PAD_TUNE_CMDRRDLY_S 22 #define MSDC_PAD_TUNE_CMD_SEL BIT(21) @@ -129,6 +173,26 @@ #define PAD_CMD_TUNE_RX_DLY3 0x3E #define PAD_CMD_TUNE_RX_DLY3_S 1 +/* PAD_TUNE0 */ +#define MSDC_PAD_TUNE0_DAT0RDDLY_M 0x1f000000 +#define MSDC_PAD_TUNE0_DAT0RDDLY_S 24 +#define MSDC_PAD_TUNE0_DAT1RDDLY_M 0x1f0000 +#define MSDC_PAD_TUNE0_DAT1RDDLY_S 16 +#define MSDC_PAD_TUNE0_DAT2RDDLY_M 0x1f00 +#define MSDC_PAD_TUNE0_DAT2RDDLY_S 8 +#define MSDC_PAD_TUNE0_DAT3RDDLY_M 0x1f +#define MSDC_PAD_TUNE0_DAT3RDDLY_S 0 + +/* PAD_TUNE1 */ +#define MSDC_PAD_TUNE1_DAT4RDDLY_M 0x1f000000 +#define MSDC_PAD_TUNE1_DAT4RDDLY_S 24 +#define MSDC_PAD_TUNE1_DAT5RDDLY_M 0x1f0000 +#define MSDC_PAD_TUNE1_DAT5RDDLY_S 16 +#define MSDC_PAD_TUNE1_DAT6RDDLY_M 0x1f00 +#define MSDC_PAD_TUNE1_DAT6RDDLY_S 8 +#define MSDC_PAD_TUNE1_DAT7RDDLY_M 0x1f +#define MSDC_PAD_TUNE1_DAT7RDDLY_S 0 + /* EMMC50_CFG0 */ #define EMMC50_CFG_CFCSTS_SEL BIT(4) @@ -166,6 +230,8 @@ #define DEFAULT_CD_DEBOUNCE 8 +#define SCLK_CYCLES_SHIFT 20 + #define CMD_INTS_MASK \ (MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO) @@ -221,7 +287,10 @@ struct mtk_sd_regs { u32 dat3_tune_crc; u32 cmd_tune_crc; u32 sdio_tune_wind; - u32 reserved4[5]; + u32 reserved4[2]; + u32 pad_ctrl0; + u32 pad_ctrl1; + u32 pad_ctrl2; u32 pad_tune; u32 pad_tune0; u32 pad_tune1; @@ -256,13 +325,14 @@ struct msdc_top_regs { struct msdc_compatible { u8 clk_div_bits; - u8 sclk_cycle_shift; bool pad_tune0; bool async_fifo; bool data_tune; bool busy_check; bool stop_clk_fix; bool enhance_rx; + bool builtin_pad_ctrl; + bool default_pad_dly; }; struct msdc_delay_phase { @@ -722,7 +792,7 @@ static int msdc_ops_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) { - u32 timeout, clk_ns, shift; + u32 timeout, clk_ns, shift = SCLK_CYCLES_SHIFT; u32 mode = 0; host->timeout_ns = ns; @@ -731,7 +801,6 @@ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) if (host->sclk == 0) { timeout = 0; } else { - shift = host->dev_comp->sclk_cycle_shift; clk_ns = 1000000000UL / host->sclk; timeout = (ns + clk_ns - 1) / clk_ns + clks; /* unit is 1048576 sclk cycles */ @@ -1391,9 +1460,14 @@ static void msdc_init_hw(struct msdc_host *host) { u32 val; void __iomem *tune_reg = &host->base->pad_tune; + void __iomem *rd_dly0_reg = &host->base->pad_tune0; + void __iomem *rd_dly1_reg = &host->base->pad_tune1; - if (host->dev_comp->pad_tune0) + if (host->dev_comp->pad_tune0) { tune_reg = &host->base->pad_tune0; + rd_dly0_reg = &host->base->dat_rd_dly[0]; + rd_dly1_reg = &host->base->dat_rd_dly[1]; + } /* Configure to MMC/SD mode, clock free running */ setbits_le32(&host->base->msdc_cfg, MSDC_CFG_MODE); @@ -1479,6 +1553,45 @@ static void msdc_init_hw(struct msdc_host *host) setbits_le32(tune_reg, MSDC_PAD_TUNE_RXDLYSEL); } + if (host->dev_comp->builtin_pad_ctrl) { + /* Set pins driving strength */ + writel(MSDC_PAD_CTRL0_CLKPD | MSDC_PAD_CTRL0_CLKSMT | + MSDC_PAD_CTRL0_CLKIES | (4 << MSDC_PAD_CTRL0_CLKDRVN_S) | + (4 << MSDC_PAD_CTRL0_CLKDRVP_S), &host->base->pad_ctrl0); + writel(MSDC_PAD_CTRL1_CMDPU | MSDC_PAD_CTRL1_CMDSMT | + MSDC_PAD_CTRL1_CMDIES | (4 << MSDC_PAD_CTRL1_CMDDRVN_S) | + (4 << MSDC_PAD_CTRL1_CMDDRVP_S), &host->base->pad_ctrl1); + writel(MSDC_PAD_CTRL2_DATPU | MSDC_PAD_CTRL2_DATSMT | + MSDC_PAD_CTRL2_DATIES | (4 << MSDC_PAD_CTRL2_DATDRVN_S) | + (4 << MSDC_PAD_CTRL2_DATDRVP_S), &host->base->pad_ctrl2); + } + + if (host->dev_comp->default_pad_dly) { + /* Default pad delay may be needed if tuning not enabled */ + clrsetbits_le32(tune_reg, MSDC_PAD_TUNE_CLKTDLY_M | + MSDC_PAD_TUNE_CMDRRDLY_M | + MSDC_PAD_TUNE_CMDRDLY_M | + MSDC_PAD_TUNE_DATRRDLY_M | + MSDC_PAD_TUNE_DATWRDLY_M, + (0x10 << MSDC_PAD_TUNE_CLKTDLY_S) | + (0x10 << MSDC_PAD_TUNE_CMDRRDLY_S) | + (0x10 << MSDC_PAD_TUNE_CMDRDLY_S) | + (0x10 << MSDC_PAD_TUNE_DATRRDLY_S) | + (0x10 << MSDC_PAD_TUNE_DATWRDLY_S)); + + writel((0x10 << MSDC_PAD_TUNE0_DAT0RDDLY_S) | + (0x10 << MSDC_PAD_TUNE0_DAT1RDDLY_S) | + (0x10 << MSDC_PAD_TUNE0_DAT2RDDLY_S) | + (0x10 << MSDC_PAD_TUNE0_DAT3RDDLY_S), + rd_dly0_reg); + + writel((0x10 << MSDC_PAD_TUNE1_DAT4RDDLY_S) | + (0x10 << MSDC_PAD_TUNE1_DAT5RDDLY_S) | + (0x10 << MSDC_PAD_TUNE1_DAT6RDDLY_S) | + (0x10 << MSDC_PAD_TUNE1_DAT7RDDLY_S), + rd_dly1_reg); + } + /* Configure to enable SDIO mode otherwise sdio cmd5 won't work */ setbits_le32(&host->base->sdc_cfg, SDC_CFG_SDIO); @@ -1526,12 +1639,14 @@ static int msdc_drv_probe(struct udevice *dev) else cfg->f_min = host->src_clk_freq / (4 * 4095); + cfg->f_max = host->src_clk_freq; + cfg->b_max = 1024; cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; host->mmc = &plat->mmc; host->timeout_ns = 100000000; - host->timeout_clks = 3 * (1 << host->dev_comp->sclk_cycle_shift); + host->timeout_clks = 3 * (1 << SCLK_CYCLES_SHIFT); #ifdef CONFIG_PINCTRL pinctrl_select_state(dev, "default"); @@ -1615,13 +1730,14 @@ static const struct dm_mmc_ops msdc_ops = { static const struct msdc_compatible mt7620_compat = { .clk_div_bits = 8, - .sclk_cycle_shift = 16, .pad_tune0 = false, .async_fifo = false, .data_tune = false, .busy_check = false, .stop_clk_fix = false, - .enhance_rx = false + .enhance_rx = false, + .builtin_pad_ctrl = true, + .default_pad_dly = true, }; static const struct msdc_compatible mt7622_compat = { @@ -1635,7 +1751,6 @@ static const struct msdc_compatible mt7622_compat = { static const struct msdc_compatible mt7623_compat = { .clk_div_bits = 12, - .sclk_cycle_shift = 20, .pad_tune0 = true, .async_fifo = true, .data_tune = true, @@ -1646,7 +1761,6 @@ static const struct msdc_compatible mt7623_compat = { static const struct msdc_compatible mt8512_compat = { .clk_div_bits = 12, - .sclk_cycle_shift = 20, .pad_tune0 = true, .async_fifo = true, .data_tune = true, @@ -1656,7 +1770,6 @@ static const struct msdc_compatible mt8512_compat = { static const struct msdc_compatible mt8516_compat = { .clk_div_bits = 12, - .sclk_cycle_shift = 20, .pad_tune0 = true, .async_fifo = true, .data_tune = true, @@ -1666,7 +1779,6 @@ static const struct msdc_compatible mt8516_compat = { static const struct msdc_compatible mt8183_compat = { .clk_div_bits = 12, - .sclk_cycle_shift = 20, .pad_tune0 = true, .async_fifo = true, .data_tune = true, diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3a5e0368805..d1a52c72643 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -399,6 +399,18 @@ config MACB_ZYNQ The Cadence MACB ethernet interface was used on Zynq platform. Say Y to enable support for the MACB/GEM in Zynq chip. +config MT7620_ETH + bool "MediaTek MT7620 Ethernet Interface" + depends on SOC_MT7620 + select PHYLIB + select DM_RESET + select DM_GPIO + select CLK + help + The MediaTek MT7620 ethernet interface is used on MT7620 based + boards. It has a built-in switch with two configurable ports which + can connect to external PHY/MACs. + config MT7628_ETH bool "MediaTek MT7628 Ethernet Interface" depends on SOC_MT7628 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e3bdda359dc..f2a0df509d3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o +obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVMDIO) += mvmdio.o diff --git a/drivers/net/mt7620-eth.c b/drivers/net/mt7620-eth.c new file mode 100644 index 00000000000..222250d52ad --- /dev/null +++ b/drivers/net/mt7620-eth.c @@ -0,0 +1,1222 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <cpu_func.h> +#include <dm.h> +#include <clk.h> +#include <malloc.h> +#include <miiphy.h> +#include <misc.h> +#include <net.h> +#include <reset.h> +#include <asm/addrspace.h> +#include <asm/cache.h> +#include <asm/gpio.h> +#include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/ethtool.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mdio.h> +#include <linux/mii.h> +#include <mach/mt7620-sysc.h> + +/* Frame Engine block */ +#define GDMA_BASE 0x600 +#define PDMA_BASE 0x800 + +/* GDMA registers */ +#define GDMA_FWD_CFG 0x00 +#define GDMA_DST_PORT GENMASK(2, 0) +#define GDMA_DST_PORT_CPU 0 + +#define GDMA_MAC_ADRL 0x08 +#define GDMA_MAC_ADRH 0x0c + +/* PDMA registers */ +#define TX_BASE_PTR0 0x000 +#define TX_MAX_CNT0 0x004 +#define TX_CTX_IDX0 0x008 +#define TX_DTX_IDX0 0x00c +#define RX_BASE_PTR0 0x100 +#define RX_MAX_CNT0 0x104 +#define RX_CALC_IDX0 0x108 +#define RX_DRX_IDX0 0x10c + +#define PDMA_GLO_CFG 0x204 +#define TX_WB_DDONE BIT(6) +#define PDMA_BT_SIZE GENMASK(5, 4) +#define PDMA_BT_SIZE_32B 1 +#define RX_DMA_BUSY BIT(3) +#define RX_DMA_EN BIT(2) +#define TX_DMA_BUSY BIT(1) +#define TX_DMA_EN BIT(0) + +#define PDMA_RST_IDX 0x208 +#define RST_DRX_IDX0 BIT(16) +#define RST_DTX_IDX0 BIT(0) + +/* Built-in giga ethernet switch block */ + +/* ARL registers */ +#define GSW_MFC 0x0010 +#define BC_FFP GENMASK(31, 24) +#define UNM_FFP GENMASK(23, 16) +#define UNU_FFP GENMASK(15, 8) +#define CPU_EN BIT(7) +#define CPU_PORT GENMASK(6, 4) + +/* Port registers */ +#define GSW_PCR(p) (0x2004 + (p) * 0x100) +#define PORT_MATRIX GENMASK(23, 16) + +#define GSW_PVC(p) (0x2010 + (p) * 0x100) +#define STAG_VPID GENMASK(31, 16) +#define VLAN_ATTR GENMASK(7, 6) +#define VLAN_ATTR_USER 0 + +/* MAC registers */ +#define GSW_PMCR(p) (0x3000 + (p) * 0x100) +#define IPG_CFG GENMASK(19, 18) +#define IPG_96BIT_WITH_SHORT_IPG 1 +#define MAC_MODE BIT(16) +#define FORCE_MODE BIT(15) +#define MAC_TX_EN BIT(14) +#define MAC_RX_EN BIT(13) +#define BKOFF_EN BIT(9) +#define BACKPR_EN BIT(8) +#define FORCE_EEE1G BIT(7) +#define FORCE_EEE100 BIT(6) +#define FORCE_RX_FC BIT(5) +#define FORCE_TX_FC BIT(4) +#define FORCE_SPEED GENMASK(3, 2) +#define FORCE_SPEED_1000 2 +#define FORCE_SPEED_100 1 +#define FORCE_SPEED_10 0 +#define FORCE_DUPLEX BIT(1) +#define FORCE_LINK BIT(0) + +/* GMAC registers */ +#define GSW_PPSC 0x7000 +#define PHY_AP_EN BIT(31) +#define PHY_PRE_EN BIT(30) +#define PHY_MDC_CFG GENMASK(29, 24) +#define EPHY_AP_EN BIT(23) +#define EE_AN_EN BIT(16) +#define PHY_AP_END_ADDR GENMASK(12, 8) +#define PHY_AP_START_ADDR GENMASK(4, 0) + +#define GSW_PIAC 0x7004 +#define PHY_ACS_ST BIT(31) +#define MDIO_REG_ADDR GENMASK(29, 25) +#define MDIO_PHY_ADDR GENMASK(24, 20) +#define MDIO_CMD GENMASK(19, 18) +#define MDIO_CMD_WRITE 1 +#define MDIO_CMD_READ 2 +#define MDIO_ST GENMASK(17, 16) +#define MDIO_RW_DATA GENMASK(15, 0) + +#define GSW_GPC1 0x7014 +#define PHY_DIS GENMASK(28, 24) +#define PHY_BASE GENMASK(20, 16) +#define TX_CLK_MODE BIT(3) +#define RX_CLK_MODE BIT(2) + +/* MII Registers for MDIO clause 45 indirect access */ +#define MII_MMD_ACC_CTL_REG 0x0d +#define MMD_OP_MODE GENMASK(15, 14) +#define MMD_ADDR 0 +#define MMD_DATA 1 +#define MMD_DATA_RW_POST_INC 2 +#define MMD_DATA_W_POST_INC 3 +#define MMD_DEVAD GENMASK(4, 0) + +#define MII_MMD_ADDR_DATA_REG 0x0e + +/* MT7530 internal register access */ +#define MT7530_REG_PAGE_ADDR GENMASK(15, 6) +#define MT7530_REG_ADDR GENMASK(5, 2) + +/* MT7530 system control registers*/ +#define MT7530_SYS_CTRL 0x7000 +#define SW_SYS_RST BIT(1) +#define SW_REG_RST BIT(0) + +#define MT7530_MHWTRAP 0x7804 +#define P5_INTF_SEL_GMAC5 BIT(13) +#define P5_INTF_DIS BIT(6) + +struct pdma_txd_info1 { + u32 SDP0; +}; + +struct pdma_txd_info2 { + u32 SDL1 : 14; + u32 LS1 : 1; + u32 BURST : 1; + u32 SDL0 : 14; + u32 LS0 : 1; + u32 DDONE : 1; +}; + +struct pdma_txd_info3 { + u32 SDP1; +}; + +struct pdma_txd_info4 { + u32 VPRI_VIDX : 8; + u32 SIDX : 4; + u32 INSP : 1; + u32 RESV : 2; + u32 UDF : 5; + u32 FP_BMAP : 8; + u32 TSO : 1; + u32 TUI_CO : 3; +}; + +struct pdma_tx_desc { + struct pdma_txd_info1 txd_info1; + struct pdma_txd_info2 txd_info2; + struct pdma_txd_info3 txd_info3; + struct pdma_txd_info4 txd_info4; +}; + +struct pdma_rxd_info1 { + u32 PDP0; +}; + +struct pdma_rxd_info2 { + u32 PLEN1 : 14; + u32 LS1 : 1; + u32 UN_USED : 1; + u32 PLEN0 : 14; + u32 LS0 : 1; + u32 DDONE : 1; +}; + +struct pdma_rxd_info3 { + u32 PDP1; +}; + +struct pdma_rxd_info4 { + u32 FOE_ENTRY : 14; + u32 CRSN : 5; + u32 SP : 3; + u32 L4F : 1; + u32 L4VLD : 1; + u32 TACK : 1; + u32 IP4F : 1; + u32 IP4 : 1; + u32 IP6 : 1; + u32 UN_USED : 4; +}; + +struct pdma_rx_desc { + struct pdma_rxd_info1 rxd_info1; + struct pdma_rxd_info2 rxd_info2; + struct pdma_rxd_info3 rxd_info3; + struct pdma_rxd_info4 rxd_info4; +}; + +struct mt7620_gsw_port_cfg { + phy_interface_t mode; + bool force_mode; + bool duplex; + u32 speed; + int phy_addr; +}; + +struct mt7620_eth_priv { + struct udevice *dev; + + void __iomem *fe_base; + void __iomem *gsw_base; + + struct mii_dev *mdio_bus; + + struct pdma_tx_desc *tx_ring_noc; + struct pdma_rx_desc *rx_ring_noc; + + int rx_dma_owner_idx0; + int tx_cpu_owner_idx0; + + void *pkt_buf; + void *tx_ring; + void *rx_ring; + + struct reset_ctl_bulk rsts; + struct clk_bulk clks; + + struct udevice *sysc; + + u32 ephy_num; + bool port5_mt7530; + struct gpio_desc gpio_swrst; + struct mt7620_gsw_port_cfg port_cfg[3]; +}; + +#define PDMA_TIMEOUT 100000 + +#define NUM_TX_DESC 64 +#define NUM_RX_DESC 128 +#define NUM_FE_PHYS 5 +#define NUM_PORTS 7 +#define CPU_PORT_NUM 6 + +#define NUM_MT7530_PHYS 5 + +static void pdma_write(struct mt7620_eth_priv *priv, u32 reg, u32 val) +{ + writel(val, priv->fe_base + PDMA_BASE + reg); +} + +static void gdma_write(struct mt7620_eth_priv *priv, u32 reg, u32 val) +{ + writel(val, priv->fe_base + GDMA_BASE + reg); +} + +static void gdma_rmw(struct mt7620_eth_priv *priv, u32 reg, u32 clr, u32 set) +{ + clrsetbits_le32(priv->fe_base + GDMA_BASE + reg, clr, set); +} + +static u32 gsw_read(struct mt7620_eth_priv *priv, u32 reg) +{ + return readl(priv->gsw_base + reg); +} + +static void gsw_write(struct mt7620_eth_priv *priv, u32 reg, u32 val) +{ + writel(val, priv->gsw_base + reg); +} + +static void gsw_rmw(struct mt7620_eth_priv *priv, u32 reg, u32 clr, u32 set) +{ + clrsetbits_le32(priv->gsw_base + reg, clr, set); +} + +static int mt7620_mdio_rw(struct mt7620_eth_priv *priv, u32 phy, u32 reg, + u32 data, u32 cmd) +{ + int ret; + u32 val; + + val = FIELD_PREP(MDIO_ST, 1) | FIELD_PREP(MDIO_CMD, cmd) | + FIELD_PREP(MDIO_PHY_ADDR, phy) | + FIELD_PREP(MDIO_REG_ADDR, reg); + + if (cmd == MDIO_CMD_WRITE) + val |= FIELD_PREP(MDIO_RW_DATA, data); + + gsw_write(priv, GSW_PIAC, val); + gsw_write(priv, GSW_PIAC, val | PHY_ACS_ST); + + ret = readl_poll_timeout(priv->gsw_base + GSW_PIAC, val, + !(val & PHY_ACS_ST), 10000); + if (ret) { + dev_err(priv->dev, "mt7620_eth: MDIO access timeout\n"); + return ret; + } + + if (cmd == MDIO_CMD_READ) { + val = gsw_read(priv, GSW_PIAC); + return FIELD_GET(MDIO_RW_DATA, val); + } + + return 0; +} + +static int mt7620_mii_read(struct mt7620_eth_priv *priv, u32 phy, u32 reg) +{ + return mt7620_mdio_rw(priv, phy, reg, 0, MDIO_CMD_READ); +} + +static int mt7620_mii_write(struct mt7620_eth_priv *priv, u32 phy, u32 reg, + u16 val) +{ + return mt7620_mdio_rw(priv, phy, reg, val, MDIO_CMD_WRITE); +} + +static int mt7620_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mt7620_eth_priv *priv = bus->priv; + int ret; + + if (devad < 0) + return mt7620_mdio_rw(priv, addr, reg, 0, MDIO_CMD_READ); + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ACC_CTL_REG, + FIELD_PREP(MMD_OP_MODE, MMD_ADDR) | + FIELD_PREP(MMD_DEVAD, devad), MDIO_CMD_WRITE); + if (ret) + return ret; + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ADDR_DATA_REG, reg, + MDIO_CMD_WRITE); + if (ret) + return ret; + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ACC_CTL_REG, + FIELD_PREP(MMD_OP_MODE, MMD_DATA) | + FIELD_PREP(MMD_DEVAD, devad), MDIO_CMD_WRITE); + if (ret) + return ret; + + return mt7620_mdio_rw(priv, addr, MII_MMD_ADDR_DATA_REG, 0, + MDIO_CMD_READ); +} + +static int mt7620_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct mt7620_eth_priv *priv = bus->priv; + int ret; + + if (devad < 0) + return mt7620_mdio_rw(priv, addr, reg, val, MDIO_CMD_WRITE); + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ACC_CTL_REG, + FIELD_PREP(MMD_OP_MODE, MMD_ADDR) | + FIELD_PREP(MMD_DEVAD, devad), MDIO_CMD_WRITE); + if (ret) + return ret; + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ADDR_DATA_REG, reg, + MDIO_CMD_WRITE); + if (ret) + return ret; + + ret = mt7620_mdio_rw(priv, addr, MII_MMD_ACC_CTL_REG, + FIELD_PREP(MMD_OP_MODE, MMD_DATA) | + FIELD_PREP(MMD_DEVAD, devad), MDIO_CMD_WRITE); + if (ret) + return ret; + + return mt7620_mdio_rw(priv, addr, MII_MMD_ADDR_DATA_REG, val, + MDIO_CMD_WRITE); +} + +static int mt7620_mdio_register(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + struct mii_dev *mdio_bus = mdio_alloc(); + int ret; + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = mt7620_mdio_read; + mdio_bus->write = mt7620_mdio_write; + snprintf(mdio_bus->name, sizeof(mdio_bus->name), dev->name); + + mdio_bus->priv = (void *)priv; + + ret = mdio_register(mdio_bus); + + if (ret) + return ret; + + priv->mdio_bus = mdio_bus; + + return 0; +} + +static int mt7530_reg_read(struct mt7620_eth_priv *priv, u32 reg, u32 *data) +{ + int ret, low_word, high_word; + + /* Write page address */ + ret = mt7620_mii_write(priv, 0x1f, 0x1f, + FIELD_GET(MT7530_REG_PAGE_ADDR, reg)); + if (ret) + return ret; + + /* Read low word */ + low_word = mt7620_mii_read(priv, 0x1f, FIELD_GET(MT7530_REG_ADDR, reg)); + if (low_word < 0) + return low_word; + + /* Read high word */ + high_word = mt7620_mii_read(priv, 0x1f, 0x10); + if (high_word < 0) + return high_word; + + if (data) + *data = ((u32)high_word << 16) | ((u32)low_word & 0xffff); + + return 0; +} + +static int mt7530_reg_write(struct mt7620_eth_priv *priv, u32 reg, u32 data) +{ + int ret; + + /* Write page address */ + ret = mt7620_mii_write(priv, 0x1f, 0x1f, + FIELD_GET(MT7530_REG_PAGE_ADDR, reg)); + if (ret) + return ret; + + /* Write low word */ + ret = mt7620_mii_write(priv, 0x1f, FIELD_GET(MT7530_REG_ADDR, reg), + data & 0xffff); + if (ret) + return ret; + + /* Write high word */ + return mt7620_mii_write(priv, 0x1f, 0x10, data >> 16); +} + +static void mt7620_phy_restart_an(struct mt7620_eth_priv *priv, u32 phy) +{ + u16 val; + + val = mt7620_mii_read(priv, phy, MII_BMCR); + val |= BMCR_ANRESTART; + mt7620_mii_write(priv, phy, MII_BMCR, val); +} + +static void mt7620_gsw_ephy_init(struct mt7620_eth_priv *priv) +{ + struct mt7620_sysc_chip_rev chip_rev; + int ret; + u32 i; + + ret = misc_ioctl(priv->sysc, MT7620_SYSC_IOCTL_GET_CHIP_REV, &chip_rev); + if (ret) { + /* Assume MT7620A if misc_ioctl() failed */ + dev_warn(priv->dev, "mt7620_eth: failed to get chip rev\n"); + chip_rev.bga = 1; + } + + /* global, page 4 */ + mt7620_mii_write(priv, 1, 31, 0x4000); + mt7620_mii_write(priv, 1, 17, 0x7444); + + if (chip_rev.bga) + mt7620_mii_write(priv, 1, 19, 0x0114); + else + mt7620_mii_write(priv, 1, 19, 0x0117); + + mt7620_mii_write(priv, 1, 22, 0x10cf); + mt7620_mii_write(priv, 1, 25, 0x6212); + mt7620_mii_write(priv, 1, 26, 0x0777); + mt7620_mii_write(priv, 1, 29, 0x4000); + mt7620_mii_write(priv, 1, 28, 0xc077); + mt7620_mii_write(priv, 1, 24, 0x0000); + + /* global, page 3 */ + mt7620_mii_write(priv, 1, 31, 0x3000); + mt7620_mii_write(priv, 1, 17, 0x4838); + + /* global, page 2 */ + mt7620_mii_write(priv, 1, 31, 0x2000); + + if (chip_rev.bga) { + mt7620_mii_write(priv, 1, 21, 0x0515); + mt7620_mii_write(priv, 1, 22, 0x0053); + mt7620_mii_write(priv, 1, 23, 0x00bf); + mt7620_mii_write(priv, 1, 24, 0x0aaf); + mt7620_mii_write(priv, 1, 25, 0x0fad); + mt7620_mii_write(priv, 1, 26, 0x0fc1); + } else { + mt7620_mii_write(priv, 1, 21, 0x0517); + mt7620_mii_write(priv, 1, 22, 0x0fd2); + mt7620_mii_write(priv, 1, 23, 0x00bf); + mt7620_mii_write(priv, 1, 24, 0x0aab); + mt7620_mii_write(priv, 1, 25, 0x00ae); + mt7620_mii_write(priv, 1, 26, 0x0fff); + } + + /* global, page 1 */ + mt7620_mii_write(priv, 1, 31, 0x1000); + mt7620_mii_write(priv, 1, 17, 0xe7f8); + + /* local, page 0 */ + mt7620_mii_write(priv, 1, 31, 0x8000); + for (i = 0; i < priv->ephy_num; i++) + mt7620_mii_write(priv, i, 30, 0xa000); + + for (i = 0; i < priv->ephy_num; i++) + mt7620_mii_write(priv, i, 4, 0x05e1); + + /* local, page 2 */ + mt7620_mii_write(priv, 1, 31, 0xa000); + mt7620_mii_write(priv, 0, 16, 0x1111); + mt7620_mii_write(priv, 1, 16, 0x1010); + mt7620_mii_write(priv, 2, 16, 0x1515); + mt7620_mii_write(priv, 3, 16, 0x0f0f); + if (priv->ephy_num == NUM_FE_PHYS) + mt7620_mii_write(priv, 4, 16, 0x1313); + + /* Restart auto-negotiation */ + for (i = 0; i < priv->ephy_num; i++) + mt7620_phy_restart_an(priv, i); + + if (priv->port_cfg[0].phy_addr > 0) + mt7620_phy_restart_an(priv, priv->port_cfg[0].phy_addr); + + if (priv->port_cfg[1].phy_addr > 0) + mt7620_phy_restart_an(priv, priv->port_cfg[1].phy_addr); +} + +static int mt7620_setup_gmac_mode(struct mt7620_eth_priv *priv, u32 gmac, + phy_interface_t mode) +{ + enum mt7620_sysc_ge_mode ge_mode; + unsigned long req; + int ret; + + switch (gmac) { + case 1: + req = MT7620_SYSC_IOCTL_SET_GE1_MODE; + break; + case 2: + req = MT7620_SYSC_IOCTL_SET_GE2_MODE; + break; + default: + /* Should not reach here */ + return -EINVAL; + } + + switch (mode) { + case PHY_INTERFACE_MODE_MII: + ge_mode = MT7620_SYSC_GE_MII; + break; + case PHY_INTERFACE_MODE_RMII: + ge_mode = MT7620_SYSC_GE_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + ge_mode = MT7620_SYSC_GE_RGMII; + break; + case PHY_INTERFACE_MODE_NONE: + if (gmac == 2) + ge_mode = MT7620_SYSC_GE_ESW_PHY; + else + ge_mode = MT7620_SYSC_GE_RGMII; + break; + default: + /* Should not reach here */ + return -EINVAL; + } + + ret = misc_ioctl(priv->sysc, req, &ge_mode); + if (ret) + dev_warn(priv->dev, "mt7620_eth: failed to set GE%u mode\n", + gmac); + + return 0; +} + +static void mt7620_gsw_setup_port(struct mt7620_eth_priv *priv, u32 port, + struct mt7620_gsw_port_cfg *port_cfg) +{ + u32 pmcr; + + if (port_cfg->mode == PHY_INTERFACE_MODE_NONE) { + if (port == 5) { + gsw_write(priv, GSW_PMCR(port), FORCE_MODE); + return; + } + + port_cfg->force_mode = port == CPU_PORT_NUM ? true : false; + } + + pmcr = FIELD_PREP(IPG_CFG, IPG_96BIT_WITH_SHORT_IPG) | MAC_MODE | + MAC_TX_EN | MAC_RX_EN | BKOFF_EN | BACKPR_EN; + + if (port_cfg->force_mode) { + pmcr |= FORCE_MODE | FORCE_RX_FC | FORCE_TX_FC | + FIELD_PREP(FORCE_SPEED, port_cfg->speed) | FORCE_LINK; + + if (port_cfg->duplex) + pmcr |= FORCE_DUPLEX; + } + + gsw_write(priv, GSW_PMCR(port), pmcr); +} + +static void mt7620_gsw_set_port_isolation(struct mt7620_eth_priv *priv) +{ + u32 i; + + for (i = 0; i < NUM_PORTS; i++) { + /* Set port matrix mode */ + if (i != CPU_PORT_NUM) + gsw_write(priv, GSW_PCR(i), + FIELD_PREP(PORT_MATRIX, 0x40)); + else + gsw_write(priv, GSW_PCR(i), + FIELD_PREP(PORT_MATRIX, 0x3f)); + + /* Set port mode to user port */ + gsw_write(priv, GSW_PVC(i), FIELD_PREP(STAG_VPID, 0x8100) | + FIELD_PREP(VLAN_ATTR, VLAN_ATTR_USER)); + } +} + +static void mt7620_gsw_setup_phy_polling(struct mt7620_eth_priv *priv) +{ + int phy_addr_st, phy_addr_end; + + if (priv->port_cfg[0].mode == PHY_INTERFACE_MODE_NONE) + priv->ephy_num = NUM_FE_PHYS; + else + priv->ephy_num = NUM_FE_PHYS - 1; + + if (priv->port_cfg[0].phy_addr < 0 && priv->port_cfg[1].phy_addr < 0) + return; + + if (priv->port_cfg[0].phy_addr > 0 && priv->port_cfg[1].phy_addr > 0) { + phy_addr_st = priv->port_cfg[0].phy_addr; + phy_addr_end = priv->port_cfg[1].phy_addr; + } else if (priv->port_cfg[0].phy_addr > 0) { + phy_addr_st = priv->port_cfg[0].phy_addr; + phy_addr_end = priv->port_cfg[0].phy_addr + 1; + } else { + phy_addr_st = 4; + phy_addr_end = priv->port_cfg[1].phy_addr; + } + + gsw_rmw(priv, GSW_PPSC, PHY_AP_END_ADDR | PHY_AP_START_ADDR, + PHY_AP_EN | FIELD_PREP(PHY_AP_START_ADDR, phy_addr_st) | + FIELD_PREP(PHY_AP_END_ADDR, phy_addr_end)); +} + +static void mt7530_gsw_set_port_isolation(struct mt7620_eth_priv *priv) +{ + u32 i; + + for (i = 0; i < NUM_PORTS; i++) { + /* Set port matrix mode */ + if (i != CPU_PORT_NUM) + mt7530_reg_write(priv, GSW_PCR(i), + FIELD_PREP(PORT_MATRIX, 0x40)); + else + mt7530_reg_write(priv, GSW_PCR(i), + FIELD_PREP(PORT_MATRIX, 0x3f)); + + /* Set port mode to user port */ + mt7530_reg_write(priv, GSW_PVC(i), + FIELD_PREP(STAG_VPID, 0x8100) | + FIELD_PREP(VLAN_ATTR, VLAN_ATTR_USER)); + } +} + +static void mt7620_gsw_config_mt7530(struct mt7620_eth_priv *priv) +{ + u16 phy_val; + u32 i, val; + + /* Disable internal PHY, set PHY base to 12 */ + gsw_write(priv, GSW_GPC1, PHY_DIS | FIELD_PREP(PHY_BASE, 12) | + TX_CLK_MODE | RX_CLK_MODE); + + /* MT7530 reset deassert */ + dm_gpio_set_value(&priv->gpio_swrst, 1); + mdelay(1000); + + /* Turn off PHYs */ + for (i = 0; i < NUM_MT7530_PHYS; i++) { + phy_val = mt7620_mii_read(priv, i, MII_BMCR); + phy_val |= BMCR_PDOWN; + mt7620_mii_write(priv, i, MII_BMCR, phy_val); + } + + /* Force MAC link down before reset */ + mt7530_reg_write(priv, GSW_PMCR(5), FORCE_MODE); + mt7530_reg_write(priv, GSW_PMCR(6), FORCE_MODE); + + /* MT7530 soft reset */ + mt7530_reg_write(priv, MT7530_SYS_CTRL, SW_SYS_RST | SW_REG_RST); + udelay(100); + + /* MT7530 port6 force to 1G (connects to MT7620 GSW port5) */ + mt7530_reg_write(priv, GSW_PMCR(6), + FIELD_PREP(IPG_CFG, IPG_96BIT_WITH_SHORT_IPG) | + MAC_MODE | FORCE_MODE | MAC_TX_EN | MAC_RX_EN | + BKOFF_EN | BACKPR_EN | FORCE_RX_FC | FORCE_TX_FC | + FIELD_PREP(FORCE_SPEED, FORCE_SPEED_1000) | + FORCE_DUPLEX | FORCE_LINK); + + /* Disable MT7530 port5 */ + mt7530_reg_read(priv, MT7530_MHWTRAP, &val); + val |= P5_INTF_SEL_GMAC5 | P5_INTF_DIS; + mt7530_reg_write(priv, MT7530_MHWTRAP, val); + + /* Isolate each ports */ + mt7530_gsw_set_port_isolation(priv); + + /* Turn on PHYs */ + for (i = 0; i < NUM_MT7530_PHYS; i++) { + phy_val = mt7620_mii_read(priv, i, MII_BMCR); + phy_val &= ~BMCR_PDOWN; + mt7620_mii_write(priv, i, MII_BMCR, phy_val); + } + /* Restart auto-negotiation */ + for (i = 0; i < NUM_MT7530_PHYS; i++) + mt7620_phy_restart_an(priv, i); +} + +static void mt7620_gsw_init(struct mt7620_eth_priv *priv) +{ + /* If port5 connects to MT7530 Giga-switch, reset it first */ + if (priv->port5_mt7530) + dm_gpio_set_value(&priv->gpio_swrst, 0); + + /* Set forward control */ + gsw_write(priv, GSW_MFC, FIELD_PREP(BC_FFP, 0x7f) | + FIELD_PREP(UNM_FFP, 0x7f) | FIELD_PREP(UNU_FFP, 0x7f) | + CPU_EN | FIELD_PREP(CPU_PORT, CPU_PORT_NUM)); + + /* Set GMAC mode (GMAC1 -> Port5, GMAC2 -> Port4) */ + mt7620_setup_gmac_mode(priv, 1, priv->port_cfg[1].mode); + mt7620_setup_gmac_mode(priv, 2, priv->port_cfg[0].mode); + + /* port_cfg[2] is CPU port */ + priv->port_cfg[2].force_mode = true; + priv->port_cfg[2].duplex = true; + priv->port_cfg[2].speed = FORCE_SPEED_1000; + + /* Configure GSW MAC port */ + mt7620_gsw_setup_port(priv, 4, &priv->port_cfg[0]); + mt7620_gsw_setup_port(priv, 5, &priv->port_cfg[1]); + mt7620_gsw_setup_port(priv, 6, &priv->port_cfg[2]); + + /* Isolate each port */ + mt7620_gsw_set_port_isolation(priv); + + /* Polling external phy if exists */ + mt7620_gsw_setup_phy_polling(priv); + + /* Configure ephy */ + mt7620_gsw_ephy_init(priv); + + /* If port5 connects to MT7530 Giga-switch, do initialization */ + if (priv->port5_mt7530) + mt7620_gsw_config_mt7530(priv); +} + +static void mt7620_eth_fifo_init(struct mt7620_eth_priv *priv) +{ + uintptr_t pkt_base = (uintptr_t)priv->pkt_buf; + int i; + + memset(priv->tx_ring, 0, NUM_TX_DESC * sizeof(struct pdma_tx_desc)); + memset(priv->rx_ring, 0, NUM_RX_DESC * sizeof(struct pdma_rx_desc)); + memset(priv->pkt_buf, 0, (NUM_TX_DESC + NUM_RX_DESC) * PKTSIZE_ALIGN); + + priv->tx_ring_noc = (void *)CKSEG1ADDR((uintptr_t)priv->tx_ring); + priv->rx_ring_noc = (void *)CKSEG1ADDR((uintptr_t)priv->rx_ring); + priv->rx_dma_owner_idx0 = 0; + priv->tx_cpu_owner_idx0 = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + priv->tx_ring_noc[i].txd_info2.LS0 = 1; + priv->tx_ring_noc[i].txd_info2.DDONE = 1; + priv->tx_ring_noc[i].txd_info4.FP_BMAP = GDMA_DST_PORT_CPU; + priv->tx_ring_noc[i].txd_info1.SDP0 = CPHYSADDR(pkt_base); + pkt_base += PKTSIZE_ALIGN; + } + + for (i = 0; i < NUM_RX_DESC; i++) { + priv->rx_ring_noc[i].rxd_info2.PLEN0 = PKTSIZE_ALIGN; + priv->rx_ring_noc[i].rxd_info1.PDP0 = CPHYSADDR(pkt_base); + pkt_base += PKTSIZE_ALIGN; + } + + pdma_write(priv, TX_BASE_PTR0, CPHYSADDR(priv->tx_ring_noc)); + pdma_write(priv, TX_MAX_CNT0, NUM_TX_DESC); + pdma_write(priv, TX_CTX_IDX0, priv->tx_cpu_owner_idx0); + + pdma_write(priv, RX_BASE_PTR0, CPHYSADDR(priv->rx_ring_noc)); + pdma_write(priv, RX_MAX_CNT0, NUM_RX_DESC); + pdma_write(priv, RX_CALC_IDX0, NUM_RX_DESC - 1); + + pdma_write(priv, PDMA_RST_IDX, RST_DTX_IDX0 | RST_DRX_IDX0); +} + +static int mt7620_eth_start(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + + mt7620_eth_fifo_init(priv); + + gdma_rmw(priv, GDMA_FWD_CFG, GDMA_DST_PORT, + FIELD_PREP(GDMA_DST_PORT, GDMA_DST_PORT_CPU)); + + pdma_write(priv, PDMA_GLO_CFG, + FIELD_PREP(PDMA_BT_SIZE, PDMA_BT_SIZE_32B) | + TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN); + udelay(500); + + return 0; +} + +static void mt7620_eth_stop(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + u32 val; + int ret; + + pdma_write(priv, PDMA_GLO_CFG, + FIELD_PREP(PDMA_BT_SIZE, PDMA_BT_SIZE_32B)); + udelay(500); + + ret = readl_poll_timeout(priv->fe_base + PDMA_BASE + PDMA_GLO_CFG, + val, !(val & (RX_DMA_BUSY | TX_DMA_BUSY)), + PDMA_TIMEOUT); + if (ret) + dev_warn(dev, "mt7620_eth: PDMA is still busy\n"); +} + +static int mt7620_eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct mt7620_eth_priv *priv = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 macaddr_lsb, macaddr_msb; + + macaddr_msb = ((u32)mac[0] << 8) | (u32)mac[1]; + macaddr_lsb = ((u32)mac[2] << 24) | ((u32)mac[3] << 16) | + ((u32)mac[4] << 8) | (u32)mac[5]; + + gdma_write(priv, GDMA_MAC_ADRH, macaddr_msb); + gdma_write(priv, GDMA_MAC_ADRL, macaddr_lsb); + + return 0; +} + +static int mt7620_eth_send(struct udevice *dev, void *packet, int length) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->tx_cpu_owner_idx0; + void *pkt_base; + + if (!priv->tx_ring_noc[idx].txd_info2.DDONE) { + printf("mt7620_eth: TX DMA descriptor ring is full\n"); + return -EPERM; + } + + pkt_base = (void *)CKSEG0ADDR(priv->tx_ring_noc[idx].txd_info1.SDP0); + memcpy(pkt_base, packet, length); + flush_dcache_range((ulong)pkt_base, (ulong)pkt_base + length); + + priv->tx_ring_noc[idx].txd_info2.SDL0 = length; + priv->tx_ring_noc[idx].txd_info2.DDONE = 0; + + priv->tx_cpu_owner_idx0 = (priv->tx_cpu_owner_idx0 + 1) % NUM_TX_DESC; + pdma_write(priv, TX_CTX_IDX0, priv->tx_cpu_owner_idx0); + + return 0; +} + +static int mt7620_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->rx_dma_owner_idx0, length; + uchar *pkt_base; + + if (!priv->rx_ring_noc[idx].rxd_info2.DDONE) { + debug("mt7620_eth: RX DMA descriptor ring is empty\n"); + return -EAGAIN; + } + + length = priv->rx_ring_noc[idx].rxd_info2.PLEN0; + pkt_base = (void *)CKSEG0ADDR(priv->rx_ring_noc[idx].rxd_info1.PDP0); + invalidate_dcache_range((ulong)pkt_base, (ulong)pkt_base + length); + + if (packetp) + *packetp = pkt_base; + + return length; +} + +static int mt7620_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + u32 idx = priv->rx_dma_owner_idx0; + + priv->rx_ring_noc[idx].rxd_info2.DDONE = 0; + priv->rx_ring_noc[idx].rxd_info2.LS0 = 0; + priv->rx_ring_noc[idx].rxd_info2.PLEN0 = PKTSIZE_ALIGN; + + pdma_write(priv, RX_CALC_IDX0, idx); + priv->rx_dma_owner_idx0 = (priv->rx_dma_owner_idx0 + 1) % NUM_RX_DESC; + + return 0; +} + +static const struct eth_ops mt7620_eth_ops = { + .start = mt7620_eth_start, + .stop = mt7620_eth_stop, + .send = mt7620_eth_send, + .recv = mt7620_eth_recv, + .free_pkt = mt7620_eth_free_pkt, + .write_hwaddr = mt7620_eth_write_hwaddr, +}; + +static int mt7620_eth_alloc_rings_pkts(struct mt7620_eth_priv *priv) +{ + priv->tx_ring = memalign(ARCH_DMA_MINALIGN, + NUM_TX_DESC * sizeof(struct pdma_tx_desc)); + if (!priv->tx_ring) { + dev_err(priv->dev, "mt7620_eth: unable to alloc tx ring\n"); + return -ENOMEM; + } + + priv->rx_ring = memalign(ARCH_DMA_MINALIGN, + NUM_RX_DESC * sizeof(struct pdma_rx_desc)); + if (!priv->rx_ring) { + dev_err(priv->dev, "mt7620_eth: unable to alloc rx ring\n"); + goto cleanup; + } + + priv->pkt_buf = memalign(ARCH_DMA_MINALIGN, + (NUM_TX_DESC + NUM_RX_DESC) * PKTSIZE_ALIGN); + if (!priv->pkt_buf) { + dev_err(priv->dev, "mt7620_eth: unable to alloc pkt buffer\n"); + goto cleanup; + } + + return 0; + +cleanup: + if (priv->tx_ring) + free(priv->tx_ring); + + if (priv->rx_ring) + free(priv->rx_ring); + + return -ENOMEM; +} + +static void mt7620_eth_free_rings_pkts(struct mt7620_eth_priv *priv) +{ + free(priv->tx_ring); + free(priv->rx_ring); + free(priv->pkt_buf); +} + +static int mt7620_eth_probe(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + u32 pcie_mode = MT7620_SYSC_PCIE_RC_MODE; + int ret; + + misc_ioctl(priv->sysc, MT7620_SYSC_IOCTL_SET_PCIE_MODE, &pcie_mode); + + clk_enable_bulk(&priv->clks); + + reset_assert_bulk(&priv->rsts); + udelay(100); + reset_deassert_bulk(&priv->rsts); + udelay(1000); + + ret = mt7620_eth_alloc_rings_pkts(priv); + if (ret) + return ret; + + ret = mt7620_mdio_register(dev); + if (ret) + dev_warn(dev, "mt7620_eth: failed to register MDIO bus\n"); + + mt7620_gsw_init(priv); + + return 0; +} + +static int mt7620_eth_remove(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + + mt7620_eth_stop(dev); + + mt7620_eth_free_rings_pkts(priv); + + return 0; +} + +static int mt7620_eth_parse_gsw_port(struct mt7620_eth_priv *priv, u32 idx, + ofnode node) +{ + ofnode subnode; + const char *str; + int mode, speed, ret; + u32 phy_addr; + + str = ofnode_read_string(node, "phy-mode"); + if (str) { + mode = phy_get_interface_by_name(str); + if (mode < 0) { + dev_err(priv->dev, "mt7620_eth: invalid phy-mode\n"); + return -EINVAL; + } + + switch (mode) { + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_RMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_NONE: + break; + default: + dev_err(priv->dev, + "mt7620_eth: unsupported phy-mode\n"); + return -ENOTSUPP; + } + + priv->port_cfg[idx].mode = mode; + } else { + priv->port_cfg[idx].mode = PHY_INTERFACE_MODE_NONE; + } + + subnode = ofnode_find_subnode(node, "fixed-link"); + if (ofnode_valid(subnode)) { + priv->port_cfg[idx].force_mode = 1; + priv->port_cfg[idx].duplex = ofnode_read_bool(subnode, + "full-duplex"); + speed = ofnode_read_u32_default(subnode, "speed", 0); + switch (speed) { + case SPEED_10: + priv->port_cfg[idx].speed = FORCE_SPEED_10; + break; + case SPEED_100: + priv->port_cfg[idx].speed = FORCE_SPEED_100; + break; + case SPEED_1000: + priv->port_cfg[idx].speed = FORCE_SPEED_1000; + break; + default: + dev_err(priv->dev, + "mt7620_eth: invalid speed for fixed-link\n"); + return -EINVAL; + } + + if (idx == 1 && ofnode_read_bool(subnode, "mediatek,mt7530")) { + priv->port5_mt7530 = true; + + ret = gpio_request_by_name_nodev(subnode, + "mediatek,mt7530-reset", 0, &priv->gpio_swrst, + GPIOD_IS_OUT); + if (ret) { + dev_err(priv->dev, + "mt7620_eth: missing mt7530 reset gpio\n"); + return ret; + } + } + } + + ret = ofnode_read_u32(node, "phy-addr", &phy_addr); + if (!ret) { + if (phy_addr > 31 || (idx == 0 && phy_addr < 3) || + (idx == 1 && phy_addr < 4)) { + dev_err(priv->dev, "mt7620_eth: invalid phy address\n"); + return -EINVAL; + } + + priv->port_cfg[idx].phy_addr = phy_addr; + } else { + priv->port_cfg[idx].phy_addr = -1; + } + + return 0; +} + +static int mt7620_eth_parse_gsw_cfg(struct udevice *dev) +{ + struct mt7620_eth_priv *priv = dev_get_priv(dev); + ofnode subnode; + int ret; + + subnode = ofnode_find_subnode(dev_ofnode(dev), "port4"); + if (ofnode_valid(subnode)) { + ret = mt7620_eth_parse_gsw_port(priv, 0, subnode); + if (ret) + return ret; + } else { + priv->port_cfg[0].mode = PHY_INTERFACE_MODE_NONE; + } + + subnode = ofnode_find_subnode(dev_ofnode(dev), "port5"); + if (ofnode_valid(subnode)) + return mt7620_eth_parse_gsw_port(priv, 1, subnode); + + priv->port_cfg[1].mode = PHY_INTERFACE_MODE_NONE; + return 0; +} + +static int mt7620_eth_of_to_plat(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct mt7620_eth_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args sysc_args; + int ret; + + pdata->iobase = dev_read_addr(dev); + + priv->dev = dev; + + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "mediatek,sysc", NULL, + 0, 0, &sysc_args); + if (ret) { + dev_err(dev, "mt7620_eth: sysc property not found\n"); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_MISC, sysc_args.node, + &priv->sysc); + if (ret) { + dev_err(dev, "mt7620_eth: failed to sysc device\n"); + return ret; + } + + priv->fe_base = dev_remap_addr_name(dev, "fe"); + if (!priv->fe_base) { + dev_err(dev, "mt7620_eth: failed to map fe registers\n"); + return -EINVAL; + } + + priv->gsw_base = dev_remap_addr_name(dev, "esw"); + if (!priv->gsw_base) { + dev_err(dev, "mt7620_eth: failed to map esw registers\n"); + return -EINVAL; + } + + ret = reset_get_bulk(dev, &priv->rsts); + if (ret) { + dev_err(dev, "mt7620_eth: failed to get resetctl\n"); + return ret; + } + + ret = clk_get_bulk(dev, &priv->clks); + if (ret) { + dev_err(dev, "mt7620_eth: failed to get clocks\n"); + return ret; + } + + return mt7620_eth_parse_gsw_cfg(dev); +} + +static const struct udevice_id mt7620_eth_ids[] = { + { .compatible = "mediatek,mt7620-eth" }, + {} +}; + +U_BOOT_DRIVER(mt7620_eth) = { + .name = "mt7620-eth", + .id = UCLASS_ETH, + .of_match = mt7620_eth_ids, + .of_to_plat = mt7620_eth_of_to_plat, + .plat_auto = sizeof(struct eth_pdata), + .probe = mt7620_eth_probe, + .remove = mt7620_eth_remove, + .ops = &mt7620_eth_ops, + .priv_auto = sizeof(struct mt7620_eth_priv), +}; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index d12a6b02ad8..ab638f0e7d9 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -218,6 +218,13 @@ config KEYSTONE_USB_PHY This PHY is found on some Keystone (K2) devices supporting USB. +config MT7620_USB_PHY + bool "MediaTek MT7620 USB PHY support" + depends on PHY + depends on SOC_MT7620 + help + Support the intergated USB PHY in MediaTek MT7620 SoC + config MT76X8_USB_PHY bool "MediaTek MT76x8 (7628/88) USB PHY support" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 45a7fe5b568..6b3761b8c8a 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o +obj-$(CONFIG_MT7620_USB_PHY) += mt7620-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o diff --git a/drivers/phy/mt7620-usb-phy.c b/drivers/phy/mt7620-usb-phy.c new file mode 100644 index 00000000000..5045c3f3f9c --- /dev/null +++ b/drivers/phy/mt7620-usb-phy.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <misc.h> +#include <reset.h> +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <mach/mt7620-sysc.h> + +struct mt7620_usb_phy { + struct udevice *sysc; + struct clk_bulk clocks; + struct reset_ctl_bulk resets; +}; + +static int mt7620_usb_phy_power_on(struct phy *_phy) +{ + struct mt7620_usb_phy *phy = dev_get_priv(_phy->dev); + u32 mode = MT7620_SYSC_USB_HOST_MODE; + int ret; + + reset_deassert_bulk(&phy->resets); + + clk_enable_bulk(&phy->clocks); + + mdelay(10); + + ret = misc_ioctl(phy->sysc, MT7620_SYSC_IOCTL_SET_USB_MODE, &mode); + if (ret) { + dev_err(_phy->dev, + "mt7620_usbphy: failed to set USB host mode\n"); + return ret; + } + + mdelay(10); + + return 0; +} + +static int mt7620_usb_phy_power_off(struct phy *_phy) +{ + struct mt7620_usb_phy *phy = dev_get_priv(_phy->dev); + + clk_disable_bulk(&phy->clocks); + + reset_assert_bulk(&phy->resets); + + return 0; +} + +static int mt7620_usb_phy_probe(struct udevice *dev) +{ + struct mt7620_usb_phy *phy = dev_get_priv(dev); + struct ofnode_phandle_args sysc_args; + int ret; + + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "mediatek,sysc", NULL, + 0, 0, &sysc_args); + if (ret) { + dev_err(dev, "mt7620_usbphy: sysc property not found\n"); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_MISC, sysc_args.node, + &phy->sysc); + if (ret) { + dev_err(dev, "mt7620_usbphy: failed to sysc device\n"); + return ret; + } + + ret = clk_get_bulk(dev, &phy->clocks); + if (ret) { + dev_err(dev, "mt7620_usbphy: failed to get clocks\n"); + return ret; + } + + ret = reset_get_bulk(dev, &phy->resets); + if (ret) { + dev_err(dev, "mt7620_usbphy: failed to get reset control\n"); + return ret; + } + + return 0; +} + +static struct phy_ops mt7620_usb_phy_ops = { + .power_on = mt7620_usb_phy_power_on, + .power_off = mt7620_usb_phy_power_off, +}; + +static const struct udevice_id mt7620_usb_phy_ids[] = { + { .compatible = "mediatek,mt7620-usbphy" }, + { } +}; + +U_BOOT_DRIVER(mt7620_usb_phy) = { + .name = "mt7620_usb_phy", + .id = UCLASS_PHY, + .of_match = mt7620_usb_phy_ids, + .ops = &mt7620_usb_phy_ops, + .probe = mt7620_usb_phy_probe, + .priv_auto = sizeof(struct mt7620_usb_phy), +}; diff --git a/drivers/pinctrl/mtmips/Kconfig b/drivers/pinctrl/mtmips/Kconfig index 8482a38ebc3..844d5b743fe 100644 --- a/drivers/pinctrl/mtmips/Kconfig +++ b/drivers/pinctrl/mtmips/Kconfig @@ -3,6 +3,15 @@ config PINCTRL_MTMIPS depends on ARCH_MTMIPS bool +config PINCTRL_MT7620 + bool "MediaTek MT7620 pin control driver" + select PINCTRL_MTMIPS + depends on SOC_MT7620 && PINCTRL_GENERIC + help + Support pin multiplexing control on MediaTek MT7620. + The driver is controlled by a device tree node which contains + the pin mux functions for each available pin groups. + config PINCTRL_MT7628 bool "MediaTek MT7628 pin control driver" select PINCTRL_MTMIPS diff --git a/drivers/pinctrl/mtmips/Makefile b/drivers/pinctrl/mtmips/Makefile index 3ba5c0c66d7..ba945a89a76 100644 --- a/drivers/pinctrl/mtmips/Makefile +++ b/drivers/pinctrl/mtmips/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_PINCTRL_MTMIPS) += pinctrl-mtmips-common.o # SoC Drivers +obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o obj-$(CONFIG_PINCTRL_MT7628) += pinctrl-mt7628.o diff --git a/drivers/pinctrl/mtmips/pinctrl-mt7620.c b/drivers/pinctrl/mtmips/pinctrl-mt7620.c new file mode 100644 index 00000000000..72ce8731a1d --- /dev/null +++ b/drivers/pinctrl/mtmips/pinctrl-mt7620.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include <linux/bitops.h> +#include <linux/io.h> + +#include "pinctrl-mtmips-common.h" + +#define SUTIF_SHIFT 30 +#define WDT_RST_SHIFT 21 +#define PA_G_SHIFT 20 +#define NAND_SD_SHIFT 18 +#define PERST_SHIFT 16 +#define EPHY_LED_SHIFT 15 +#define WLED_SHIFT 13 +#define SPI_CS1_SHIFT 12 +#define SPI_SHIFT 11 +#define RGMII2_SHIFT 10 +#define RGMII1_SHIFT 9 +#define MDIO_SHIFT 7 +#define UARTL_SHIFT 5 +#define UARTF_SHIFT 2 +#define I2C_SHIFT 0 + +#define GM4_MASK 3 +#define GM8_MASK 7 + +#if CONFIG_IS_ENABLED(PINMUX) +static const struct mtmips_pmx_func sutif_grp[] = { + FUNC("i2c", 2), + FUNC("uartl", 1), + FUNC("none", 0), +}; + +static const struct mtmips_pmx_func wdt_rst_grp[] = { + FUNC("gpio", 2), + FUNC("refclk", 1), + FUNC("wdt rst", 0), +}; + +static const struct mtmips_pmx_func pa_g_grp[] = { + FUNC("gpio", 1), + FUNC("pa", 0), +}; + +static const struct mtmips_pmx_func nand_sd_grp[] = { + FUNC("gpio", 2), + FUNC("sd", 1), + FUNC("nand", 0), +}; + +static const struct mtmips_pmx_func perst_grp[] = { + FUNC("gpio", 2), + FUNC("refclk", 1), + FUNC("perst", 0), +}; + +static const struct mtmips_pmx_func ephy_led_grp[] = { + FUNC("gpio", 1), + FUNC("led", 0), +}; + +static const struct mtmips_pmx_func wled_grp[] = { + FUNC("gpio", 1), + FUNC("led", 0), +}; + +static const struct mtmips_pmx_func spi_cs1_grp[] = { + FUNC("refclk", 1), + FUNC("spi cs1", 0), +}; + +static const struct mtmips_pmx_func spi_grp[] = { + FUNC("gpio", 1), + FUNC("spi", 0), +}; + +static const struct mtmips_pmx_func rgmii2_grp[] = { + FUNC("gpio", 1), + FUNC("rgmii2", 0), +}; + +static const struct mtmips_pmx_func rgmii1_grp[] = { + FUNC("gpio", 1), + FUNC("rgmii1", 0), +}; + +static const struct mtmips_pmx_func mdio_grp[] = { + FUNC("gpio", 2), + FUNC("refclk", 1), + FUNC("mdio", 0), +}; + +static const struct mtmips_pmx_func uartl_grp[] = { + FUNC("gpio", 1), + FUNC("uartl", 0), +}; + +static const struct mtmips_pmx_func uartf_grp[] = { + FUNC("gpio", 7), + FUNC("i2s gpio", 6), + FUNC("uartf gpio", 5), + FUNC("gpio pcm", 4), + FUNC("i2s uartf", 3), + FUNC("i2s pcm", 2), + FUNC("uartf pcm", 1), + FUNC("uartf", 0), +}; + +static const struct mtmips_pmx_func i2c_grp[] = { + FUNC("gpio", 1), + FUNC("i2c", 0), +}; + +static const struct mtmips_pmx_group mt7620_pinmux_data[] = { + GRP("sutif", sutif_grp, 0, SUTIF_SHIFT, GM4_MASK), + GRP("wdt rst", wdt_rst_grp, 0, WDT_RST_SHIFT, GM4_MASK), + GRP("pa", pa_g_grp, 0, PA_G_SHIFT, 1), + GRP("nand", nand_sd_grp, 0, NAND_SD_SHIFT, GM4_MASK), + GRP("perst", perst_grp, 0, PERST_SHIFT, GM4_MASK), + GRP("ephy led", ephy_led_grp, 0, EPHY_LED_SHIFT, 1), + GRP("wled", wled_grp, 0, WLED_SHIFT, 1), + GRP("spi cs1", spi_cs1_grp, 0, SPI_CS1_SHIFT, 1), + GRP("spi", spi_grp, 0, SPI_SHIFT, 1), + GRP("rgmii2", rgmii2_grp, 0, RGMII2_SHIFT, 1), + GRP("rgmii1", rgmii1_grp, 0, RGMII1_SHIFT, 1), + GRP("mdio", mdio_grp, 0, MDIO_SHIFT, GM4_MASK), + GRP("uartl", uartl_grp, 0, UARTL_SHIFT, 1), + GRP("uartf", uartf_grp, 0, UARTF_SHIFT, GM8_MASK), + GRP("i2c", i2c_grp, 0, I2C_SHIFT, 1), +}; + +static int mt7620_get_groups_count(struct udevice *dev) +{ + return ARRAY_SIZE(mt7620_pinmux_data); +} + +static const char *mt7620_get_group_name(struct udevice *dev, + unsigned int selector) +{ + return mt7620_pinmux_data[selector].name; +} +#endif /* CONFIG_IS_ENABLED(PINMUX) */ + +static int mt7620_pinctrl_probe(struct udevice *dev) +{ + struct mtmips_pinctrl_priv *priv = dev_get_priv(dev); + int ret = 0; + +#if CONFIG_IS_ENABLED(PINMUX) + ret = mtmips_pinctrl_probe(priv, ARRAY_SIZE(mt7620_pinmux_data), + mt7620_pinmux_data); +#endif /* CONFIG_IS_ENABLED(PINMUX) */ + + return ret; +} + +static int mt7620_pinctrl_of_to_plat(struct udevice *dev) +{ + struct mtmips_pinctrl_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr_index(dev, 0); + if (!priv->base) + return -EINVAL; + + return 0; +} + +static const struct pinctrl_ops mt7620_pinctrl_ops = { +#if CONFIG_IS_ENABLED(PINMUX) + .get_groups_count = mt7620_get_groups_count, + .get_group_name = mt7620_get_group_name, + .get_functions_count = mtmips_get_functions_count, + .get_function_name = mtmips_get_function_name, + .pinmux_group_set = mtmips_pinmux_group_set, +#endif /* CONFIG_IS_ENABLED(PINMUX) */ + .set_state = pinctrl_generic_set_state, +}; + +static const struct udevice_id mt7620_pinctrl_ids[] = { + { .compatible = "mediatek,mt7620-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(mt7620_pinctrl) = { + .name = "mt7620-pinctrl", + .id = UCLASS_PINCTRL, + .of_match = mt7620_pinctrl_ids, + .of_to_plat = mt7620_pinctrl_of_to_plat, + .ops = &mt7620_pinctrl_ops, + .probe = mt7620_pinctrl_probe, + .priv_auto = sizeof(struct mtmips_pinctrl_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/reset/reset-mtmips.c b/drivers/reset/reset-mtmips.c index 848425afbcb..4e71d525584 100644 --- a/drivers/reset/reset-mtmips.c +++ b/drivers/reset/reset-mtmips.c @@ -82,4 +82,5 @@ U_BOOT_DRIVER(mtmips_reset) = { .probe = mtmips_reset_probe, .priv_auto = sizeof(struct mtmips_reset_priv), .ops = &mtmips_reset_ops, + .flags = DM_FLAG_PRE_RELOC, }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 129494322ce..9db4cae1df1 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -417,6 +417,16 @@ config DEBUG_UART_MTK driver will be available until the real driver model serial is running. +config DEBUG_UART_MT7620 + bool "UART driver for MediaTek MT7620 and earlier SoCs" + depends on MT7620_SERIAL + help + Select this to enable a debug UART using the UART driver for + MediaTek MT7620 and earlier SoCs. + You will need to provide parameters to make this work. The + driver will be available until the real driver model serial is + running. + endchoice config DEBUG_UART_BASE @@ -833,6 +843,16 @@ config MTK_SERIAL The High-speed UART is compatible with the ns16550a UART and have its own high-speed registers. +config MT7620_SERIAL + bool "UART driver for MediaTek MT7620 and earlier SoCs" + depends on DM_SERIAL + help + Select this to enable UART support for MediaTek MT7620 and earlier + SoCs. This driver uses driver model and requires a device tree + binding to operate. + The UART driver for MediaTek MT7620 and earlier SoCs is *NOT* + compatible with the ns16550a UART. + config MPC8XX_CONS bool "Console driver for MPC8XX" depends on MPC8xx diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 25f7f8d342c..0c3810f5d5c 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_NULLDEV_SERIAL) += serial_nulldev.o obj-$(CONFIG_OWL_SERIAL) += serial_owl.o obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o +obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o obj-$(CONFIG_XEN_SERIAL) += serial_xen.o diff --git a/drivers/serial/serial_mt7620.c b/drivers/serial/serial_mt7620.c new file mode 100644 index 00000000000..826a14b49f8 --- /dev/null +++ b/drivers/serial/serial_mt7620.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UART driver for MediaTek MT7620 and earlier SoCs + * + * Copyright (C) 2020 MediaTek Inc. + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#include <clk.h> +#include <div64.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <reset.h> +#include <serial.h> +#include <watchdog.h> +#include <asm/io.h> +#include <asm/types.h> +#include <asm/addrspace.h> +#include <dm/device_compat.h> +#include <linux/err.h> + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +#include <dt-structs.h> +#endif + +struct mt7620_serial_regs { + u32 rbr; + u32 thr; + u32 ier; + u32 iir; + u32 fcr; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 scratch; + u32 dl; + u32 dll; + u32 dlm; + u32 ifctl; +}; + +#define UART_LCR_WLS_8 0x03 /* 8 bit character length */ + +#define UART_LSR_DR 0x01 /* Data ready */ +#define UART_LSR_THRE 0x20 /* Xmit holding register empty */ +#define UART_LSR_TEMT 0x40 /* Xmitter empty */ + +#define UART_MCR_DTR 0x01 /* DTR */ +#define UART_MCR_RTS 0x02 /* RTS */ + +#define UART_FCR_FIFO_EN 0x01 /* Fifo enable */ +#define UART_FCR_RXSR 0x02 /* Receiver soft reset */ +#define UART_FCR_TXSR 0x04 /* Transmitter soft reset */ + +#define UART_MCRVAL (UART_MCR_DTR | \ + UART_MCR_RTS) + +/* Clear & enable FIFOs */ +#define UART_FCRVAL (UART_FCR_FIFO_EN | \ + UART_FCR_RXSR | \ + UART_FCR_TXSR) + +struct mt7620_serial_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_serial_mt7620 dtplat; +#endif + + struct mt7620_serial_regs __iomem *regs; + u32 clock; +}; + +static void _mt7620_serial_setbrg(struct mt7620_serial_plat *plat, int baud) +{ + u32 quot; + + /* set divisor */ + quot = DIV_ROUND_CLOSEST(plat->clock, 16 * baud); + writel(quot, &plat->regs->dl); + + /* set character length and stop bits */ + writel(UART_LCR_WLS_8, &plat->regs->lcr); +} + +static int mt7620_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + + _mt7620_serial_setbrg(plat, baudrate); + + return 0; +} + +static int mt7620_serial_putc(struct udevice *dev, const char ch) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + + if (!(readl(&plat->regs->lsr) & UART_LSR_THRE)) + return -EAGAIN; + + writel(ch, &plat->regs->thr); + + if (ch == '\n') + WATCHDOG_RESET(); + + return 0; +} + +static int mt7620_serial_getc(struct udevice *dev) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + + if (!(readl(&plat->regs->lsr) & UART_LSR_DR)) + return -EAGAIN; + + return readl(&plat->regs->rbr); +} + +static int mt7620_serial_pending(struct udevice *dev, bool input) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + + if (input) + return (readl(&plat->regs->lsr) & UART_LSR_DR) ? 1 : 0; + + return (readl(&plat->regs->lsr) & UART_LSR_THRE) ? 0 : 1; +} + +static int mt7620_serial_probe(struct udevice *dev) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + plat->regs = (void __iomem *)KSEG1ADDR(plat->dtplat.reg[0]); + plat->clock = plat->dtplat.clock_frequency; +#endif + + /* Disable interrupt */ + writel(0, &plat->regs->ier); + + writel(UART_MCRVAL, &plat->regs->mcr); + writel(UART_FCRVAL, &plat->regs->fcr); + + return 0; +} + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static int mt7620_serial_of_to_plat(struct udevice *dev) +{ + struct mt7620_serial_plat *plat = dev_get_plat(dev); + struct reset_ctl reset_uart; + struct clk clk; + int err; + + err = reset_get_by_index(dev, 0, &reset_uart); + if (!err) + reset_deassert(&reset_uart); + + plat->regs = dev_remap_addr_index(dev, 0); + if (!plat->regs) { + dev_err(dev, "mt7620_serial: unable to map UART registers\n"); + return -EINVAL; + } + + err = clk_get_by_index(dev, 0, &clk); + if (!err) { + err = clk_get_rate(&clk); + if (!IS_ERR_VALUE(err)) + plat->clock = err; + } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) { + dev_err(dev, "mt7620_serial: failed to get clock\n"); + return err; + } + + if (!plat->clock) + plat->clock = dev_read_u32_default(dev, "clock-frequency", 0); + + if (!plat->clock) { + dev_err(dev, "mt7620_serial: clock not defined\n"); + return -EINVAL; + } + + return 0; +} + +static const struct udevice_id mt7620_serial_ids[] = { + { .compatible = "mediatek,mt7620-uart" }, + { } +}; +#endif + +static const struct dm_serial_ops mt7620_serial_ops = { + .putc = mt7620_serial_putc, + .pending = mt7620_serial_pending, + .getc = mt7620_serial_getc, + .setbrg = mt7620_serial_setbrg, +}; + +U_BOOT_DRIVER(serial_mt7620) = { + .name = "serial_mt7620", + .id = UCLASS_SERIAL, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + .of_match = mt7620_serial_ids, + .of_to_plat = mt7620_serial_of_to_plat, +#endif + .plat_auto = sizeof(struct mt7620_serial_plat), + .probe = mt7620_serial_probe, + .ops = &mt7620_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +DM_DRIVER_ALIAS(serial_mt7620, mediatek_mt7620_uart); + +#ifdef CONFIG_DEBUG_UART_MT7620 + +#include <debug_uart.h> + +static inline void _debug_uart_init(void) +{ + struct mt7620_serial_plat plat; + + plat.regs = (void *)CONFIG_DEBUG_UART_BASE; + plat.clock = CONFIG_DEBUG_UART_CLOCK; + + writel(0, &plat.regs->ier); + writel(UART_MCRVAL, &plat.regs->mcr); + writel(UART_FCRVAL, &plat.regs->fcr); + + _mt7620_serial_setbrg(&plat, CONFIG_BAUDRATE); +} + +static inline void _debug_uart_putc(int ch) +{ + struct mt7620_serial_regs __iomem *regs = + (void *)CONFIG_DEBUG_UART_BASE; + + while (!(readl(®s->lsr) & UART_LSR_THRE)) + ; + + writel(ch, ®s->thr); +} + +DEBUG_UART_FUNCS + +#endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index cd19b2d4b3d..1efb5b18f50 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -210,6 +210,13 @@ config MSCC_BB_SPI Enable MSCC bitbang SPI driver. This driver can be used on MSCC SOCs. +config MT7620_SPI + bool "MediaTek MT7620 SPI driver" + depends on SOC_MT7620 + help + Enable the MT7620 SPI driver. This driver can be used to access + generic SPI devices on MediaTek MT7620 SoC. + config MT7621_SPI bool "MediaTek MT7621 SPI driver" depends on SOC_MT7628 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index dc9ea34c0a7..d06949d2f59 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o +obj-$(CONFIG_MT7620_SPI) += mt7620_spi.o obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o diff --git a/drivers/spi/mt7620_spi.c b/drivers/spi/mt7620_spi.c new file mode 100644 index 00000000000..6554e371671 --- /dev/null +++ b/drivers/spi/mt7620_spi.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * + * Generic SPI driver for MediaTek MT7620 SoC + */ + +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <spi.h> +#include <linux/bitops.h> +#include <linux/iopoll.h> +#include <linux/io.h> +#include <linux/log2.h> + +#define MT7620_SPI_NUM_CS 2 +#define MT7620_SPI_MASTER1_OFF 0x00 +#define MT7620_SPI_MASTER2_OFF 0x40 + +/* SPI_STAT */ +#define SPI_BUSY BIT(0) + +/* SPI_CFG */ +#define MSB_FIRST BIT(8) +#define SPI_CLK_POL BIT(6) +#define RX_CLK_EDGE BIT(5) +#define TX_CLK_EDGE BIT(4) +#define SPI_CLK_S 0 +#define SPI_CLK_M GENMASK(2, 0) + +/* SPI_CTL */ +#define START_WR BIT(2) +#define START_RD BIT(1) +#define SPI_HIGH BIT(0) + +#define SPI_ARB 0xf0 +#define ARB_EN BIT(31) + +#define POLLING_SCALE 10 +#define POLLING_FRAC_USEC 100 + +struct mt7620_spi_master_regs { + u32 stat; + u32 reserved0[3]; + u32 cfg; + u32 ctl; + u32 reserved1[2]; + u32 data; +}; + +struct mt7620_spi { + void __iomem *regs; + struct mt7620_spi_master_regs *m[MT7620_SPI_NUM_CS]; + unsigned int sys_freq; + u32 wait_us; + uint mode; + uint speed; +}; + +static void mt7620_spi_master_setup(struct mt7620_spi *ms, int cs) +{ + u32 rate, prescale, freq, tmo, cfg; + + /* Calculate the clock divsior */ + rate = DIV_ROUND_UP(ms->sys_freq, ms->speed); + rate = roundup_pow_of_two(rate); + + prescale = ilog2(rate / 2); + if (prescale > 6) + prescale = 6; + + /* Calculate the real clock, and usecs for one byte transaction */ + freq = ms->sys_freq >> (prescale + 1); + tmo = DIV_ROUND_UP(8 * 1000000, freq); + + /* 10 times tolerance plus 100us */ + ms->wait_us = POLLING_SCALE * tmo + POLLING_FRAC_USEC; + + /* set SPI_CFG */ + cfg = prescale << SPI_CLK_S; + + switch (ms->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + cfg |= TX_CLK_EDGE; + break; + case SPI_MODE_1: + cfg |= RX_CLK_EDGE; + break; + case SPI_MODE_2: + cfg |= SPI_CLK_POL | RX_CLK_EDGE; + break; + case SPI_MODE_3: + cfg |= SPI_CLK_POL | TX_CLK_EDGE; + break; + } + + if (!(ms->mode & SPI_LSB_FIRST)) + cfg |= MSB_FIRST; + + writel(cfg, &ms->m[cs]->cfg); + + writel(SPI_HIGH, &ms->m[cs]->ctl); +} + +static void mt7620_spi_set_cs(struct mt7620_spi *ms, int cs, bool enable) +{ + if (enable) + mt7620_spi_master_setup(ms, cs); + + if (ms->mode & SPI_CS_HIGH) + enable = !enable; + + if (enable) + clrbits_32(&ms->m[cs]->ctl, SPI_HIGH); + else + setbits_32(&ms->m[cs]->ctl, SPI_HIGH); +} + +static int mt7620_spi_set_mode(struct udevice *bus, uint mode) +{ + struct mt7620_spi *ms = dev_get_priv(bus); + + ms->mode = mode; + + /* Mode 0 is buggy. Force to use mode 3 */ + if ((mode & SPI_MODE_3) == SPI_MODE_0) + ms->mode |= SPI_MODE_3; + + return 0; +} + +static int mt7620_spi_set_speed(struct udevice *bus, uint speed) +{ + struct mt7620_spi *ms = dev_get_priv(bus); + + ms->speed = speed; + + return 0; +} + +static inline int mt7620_spi_busy_poll(struct mt7620_spi *ms, int cs) +{ + u32 val; + + return readl_poll_timeout(&ms->m[cs]->stat, val, !(val & SPI_BUSY), + ms->wait_us); +} + +static int mt7620_spi_read(struct mt7620_spi *ms, int cs, u8 *buf, size_t len) +{ + int ret; + + while (len) { + setbits_32(&ms->m[cs]->ctl, START_RD); + + ret = mt7620_spi_busy_poll(ms, cs); + if (ret) + return ret; + + *buf++ = (u8)readl(&ms->m[cs]->data); + + len--; + } + + return 0; +} + +static int mt7620_spi_write(struct mt7620_spi *ms, int cs, const u8 *buf, + size_t len) +{ + int ret; + + while (len) { + writel(*buf++, &ms->m[cs]->data); + setbits_32(&ms->m[cs]->ctl, START_WR); + + ret = mt7620_spi_busy_poll(ms, cs); + if (ret) + return ret; + + len--; + } + + return 0; +} + +static int mt7620_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct mt7620_spi *ms = dev_get_priv(bus); + int total_size = bitlen >> 3; + int cs, ret = 0; + + /* + * This driver only supports half-duplex, so complain and bail out + * upon full-duplex messages + */ + if (dout && din) { + dev_err(dev, "mt7620_spi: Only half-duplex is supported\n"); + return -EIO; + } + + cs = spi_chip_select(dev); + if (cs < 0 || cs >= MT7620_SPI_NUM_CS) { + dev_err(dev, "mt7620_spi: Invalid chip select %d\n", cs); + return -EINVAL; + } + + if (flags & SPI_XFER_BEGIN) + mt7620_spi_set_cs(ms, cs, true); + + if (din) + ret = mt7620_spi_read(ms, cs, din, total_size); + else if (dout) + ret = mt7620_spi_write(ms, cs, dout, total_size); + + if (ret) + dev_err(dev, "mt7620_spi: %s transaction timeout\n", + din ? "read" : "write"); + + if (flags & SPI_XFER_END) + mt7620_spi_set_cs(ms, cs, false); + + return ret; +} + +static int mt7620_spi_probe(struct udevice *dev) +{ + struct mt7620_spi *ms = dev_get_priv(dev); + struct clk clk; + int ret; + + ms->regs = dev_remap_addr(dev); + if (!ms->regs) + return -EINVAL; + + ms->m[0] = ms->regs + MT7620_SPI_MASTER1_OFF; + ms->m[1] = ms->regs + MT7620_SPI_MASTER2_OFF; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "mt7620_spi: Please provide a clock!\n"); + return ret; + } + + clk_enable(&clk); + + ms->sys_freq = clk_get_rate(&clk); + if (!ms->sys_freq) { + dev_err(dev, "mt7620_spi: Please provide a valid bus clock!\n"); + return -EINVAL; + } + + writel(ARB_EN, ms->regs + SPI_ARB); + + return 0; +} + +static const struct dm_spi_ops mt7620_spi_ops = { + .set_mode = mt7620_spi_set_mode, + .set_speed = mt7620_spi_set_speed, + .xfer = mt7620_spi_xfer, +}; + +static const struct udevice_id mt7620_spi_ids[] = { + { .compatible = "mediatek,mt7620-spi" }, + { } +}; + +U_BOOT_DRIVER(mt7620_spi) = { + .name = "mt7620_spi", + .id = UCLASS_SPI, + .of_match = mt7620_spi_ids, + .ops = &mt7620_spi_ops, + .priv_auto = sizeof(struct mt7620_spi), + .probe = mt7620_spi_probe, +}; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d5edabf3eff..602ccbe41c0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -134,6 +134,13 @@ config WDT_MPC8xx help Select this to enable mpc8xx watchdog timer +config WDT_MT7620 + bool "MediaTek MT7620 watchdog timer support" + depends on WDT && SOC_MT7620 + help + Select this to enable watchdog timer on MediaTek MT7620 and earlier + SoC chips. + config WDT_MT7621 bool "MediaTek MT7621 watchdog timer support" depends on WDT && SOC_MT7628 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cbb6d8407cc..6e70c7ae19c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o obj-$(CONFIG_WDT_ORION) += orion_wdt.o obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o +obj-$(CONFIG_WDT_MT7620) += mt7620_wdt.o obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o obj-$(CONFIG_WDT_MTK) += mtk_wdt.o obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o diff --git a/drivers/watchdog/mt7620_wdt.c b/drivers/watchdog/mt7620_wdt.c new file mode 100644 index 00000000000..fe89721ddb9 --- /dev/null +++ b/drivers/watchdog/mt7620_wdt.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + * + * Watchdog timer for MT7620 and earlier SoCs + */ + +#include <div64.h> +#include <dm.h> +#include <reset.h> +#include <wdt.h> +#include <linux/bitops.h> +#include <linux/io.h> + +struct mt7620_wdt { + void __iomem *regs; + u64 timeout; +}; + +#define TIMER_FREQ 40000000 +#define TIMER_MASK 0xffff +#define TIMER_PRESCALE 65536 + +#define TIMER_LOAD 0x00 +#define TIMER_CTL 0x08 + +#define TIMER_ENABLE BIT(7) +#define TIMER_MODE_SHIFT 4 +#define TIMER_MODE_WDT 3 +#define TIMER_PRESCALE_SHIFT 0 +#define TIMER_PRESCALE_65536 15 + +static void mt7620_wdt_ping(struct mt7620_wdt *priv) +{ + u64 val; + + val = (TIMER_FREQ / TIMER_PRESCALE) * priv->timeout; + do_div(val, 1000); + + if (val > TIMER_MASK) + val = TIMER_MASK; + + writel(val, priv->regs + TIMER_LOAD); +} + +static int mt7620_wdt_start(struct udevice *dev, u64 ms, ulong flags) +{ + struct mt7620_wdt *priv = dev_get_priv(dev); + + priv->timeout = ms; + mt7620_wdt_ping(priv); + + writel(TIMER_ENABLE | (TIMER_MODE_WDT << TIMER_MODE_SHIFT) | + (TIMER_PRESCALE_65536 << TIMER_PRESCALE_SHIFT), + priv->regs + TIMER_CTL); + + return 0; +} + +static int mt7620_wdt_stop(struct udevice *dev) +{ + struct mt7620_wdt *priv = dev_get_priv(dev); + + mt7620_wdt_ping(priv); + + clrbits_32(priv->regs + TIMER_CTL, TIMER_ENABLE); + + return 0; +} + +static int mt7620_wdt_reset(struct udevice *dev) +{ + struct mt7620_wdt *priv = dev_get_priv(dev); + + mt7620_wdt_ping(priv); + + return 0; +} + +static int mt7620_wdt_expire_now(struct udevice *dev, ulong flags) +{ + struct mt7620_wdt *priv = dev_get_priv(dev); + + mt7620_wdt_start(dev, 1, flags); + + /* + * 0 will disable the timer directly, a positive number must be used + * instead. Since the timer is a countdown timer, 1 (tick) is used. + * + * For a timer with input clock = 40MHz, 1 timer tick is short + * enough to trigger a timeout immediately. + * + * Restore prescale to 1, and load timer with 1 to trigger timeout. + */ + writel(TIMER_ENABLE | (TIMER_MODE_WDT << TIMER_MODE_SHIFT), + priv->regs + TIMER_CTL); + writel(1, priv->regs + TIMER_LOAD); + + return 0; +} + +static int mt7620_wdt_probe(struct udevice *dev) +{ + struct mt7620_wdt *priv = dev_get_priv(dev); + struct reset_ctl reset_wdt; + int ret; + + ret = reset_get_by_index(dev, 0, &reset_wdt); + if (!ret) + reset_deassert(&reset_wdt); + + priv->regs = dev_remap_addr(dev); + if (!priv->regs) + return -EINVAL; + + mt7620_wdt_stop(dev); + + return 0; +} + +static const struct wdt_ops mt7620_wdt_ops = { + .start = mt7620_wdt_start, + .reset = mt7620_wdt_reset, + .stop = mt7620_wdt_stop, + .expire_now = mt7620_wdt_expire_now, +}; + +static const struct udevice_id mt7620_wdt_ids[] = { + { .compatible = "mediatek,mt7620-wdt" }, + {} +}; + +U_BOOT_DRIVER(mt7620_wdt) = { + .name = "mt7620_wdt", + .id = UCLASS_WDT, + .of_match = mt7620_wdt_ids, + .probe = mt7620_wdt_probe, + .priv_auto = sizeof(struct mt7620_wdt), + .ops = &mt7620_wdt_ops, +}; diff --git a/include/configs/mt7620.h b/include/configs/mt7620.h new file mode 100644 index 00000000000..4d074a36888 --- /dev/null +++ b/include/configs/mt7620.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#ifndef __CONFIG_MT7620_H +#define __CONFIG_MT7620_H + +#define CONFIG_SYS_HZ 1000 +#define CONFIG_SYS_MIPS_TIMER_FREQ 290000000 + +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE + +#define CONFIG_SYS_MALLOC_LEN 0x100000 +#define CONFIG_SYS_BOOTPARAMS_LEN 0x20000 + +#define CONFIG_SYS_SDRAM_BASE 0x80000000 +#define CONFIG_SYS_LOAD_ADDR 0x80010000 + +#define CONFIG_SYS_INIT_SP_OFFSET 0x400000 + +#define CONFIG_SYS_BOOTM_LEN 0x1000000 + +#define CONFIG_SYS_MAXARGS 16 +#define CONFIG_SYS_CBSIZE 1024 + +/* Serial common */ +#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } + +/* SPL */ +#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD) +#define CONFIG_SKIP_LOWLEVEL_INIT +#endif + +#define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE +#define CONFIG_SPL_BSS_START_ADDR 0x80010000 +#define CONFIG_SPL_BSS_MAX_SIZE 0x10000 +#define CONFIG_SPL_MAX_SIZE 0x10000 +#define CONFIG_SPL_PAD_TO 0 + +/* Dummy value */ +#define CONFIG_SYS_UBOOT_BASE 0 + +#endif /* __CONFIG_MT7620_H */ diff --git a/include/dt-bindings/clock/mt7620-clk.h b/include/dt-bindings/clock/mt7620-clk.h new file mode 100644 index 00000000000..3bb91ebdf14 --- /dev/null +++ b/include/dt-bindings/clock/mt7620-clk.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#ifndef _DT_BINDINGS_MT7620_CLK_H_ +#define _DT_BINDINGS_MT7620_CLK_H_ + +/* Base clocks */ +#define CLK_SYS 34 +#define CLK_CPU 33 +#define CLK_XTAL 32 + +/* Peripheral clocks */ +#define CLK_SDHC 30 +#define CLK_MIPS_CNT 28 +#define CLK_PCIE 26 +#define CLK_UPHY_12M 25 +#define CLK_EPHY 24 +#define CLK_ESW 23 +#define CLK_UPHY_48M 22 +#define CLK_FE 21 +#define CLK_UARTL 19 +#define CLK_SPI 18 +#define CLK_I2S 17 +#define CLK_I2C 16 +#define CLK_NAND 15 +#define CLK_GDMA 14 +#define CLK_PIO 13 +#define CLK_UARTF 12 +#define CLK_PCM 11 +#define CLK_MC 10 +#define CLK_INTC 9 +#define CLK_TIMER 8 +#define CLK_GE2 7 +#define CLK_GE1 6 + +#endif /* _DT_BINDINGS_MT7620_CLK_H_ */ diff --git a/include/dt-bindings/reset/mt7620-reset.h b/include/dt-bindings/reset/mt7620-reset.h new file mode 100644 index 00000000000..3096b29cdb1 --- /dev/null +++ b/include/dt-bindings/reset/mt7620-reset.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * + * Author: Weijie Gao <weijie.gao@mediatek.com> + */ + +#ifndef _DT_BINDINGS_MT7620_RESET_H_ +#define _DT_BINDINGS_MT7620_RESET_H_ + +#define PPE_RST 31 +#define SDHC_RST 30 +#define MIPS_CNT_RST 28 +#define PCIE_RST 26 +#define UHST_RST 25 +#define EPHY_RST 24 +#define ESW_RST 23 +#define UDEV_RST 22 +#define FE_RST 21 +#define WLAN_RST 20 +#define UARTL_RST 19 +#define SPI_RST 18 +#define I2S_RST 17 +#define I2C_RST 16 +#define NAND_RST 15 +#define DMA_RST 14 +#define PIO_RST 13 +#define UARTF_RST 12 +#define PCM_RST 11 +#define MC_RST 10 +#define INTC_RST 9 +#define TIMER_RST 8 +#define SYS_RST 0 + +#endif /* _DT_BINDINGS_MT7620_RESET_H_ */ |