From 73472887d76f4fc70a3b3633af6705f236cee727 Mon Sep 17 00:00:00 2001 From: Dimitrios Poulios Date: Tue, 16 Jul 2024 19:00:21 +0300 Subject: [PATCH] Add support for Raspberry Pi 5 - Added basic support for rpi5 (mmc, usb, ethernet, devicetree) - Drivers ported from the oficial Raspberry Pi kernel (https://github.com/raspberrypi/linux/) using commit da87f91ad8450ccc5274cd7b6ba8d823b396c96f - TODO: Fix random mac address on ethernet Signed-off-by: Dimitrios Poulios --- .../arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 867 ++++++ arch/arm/boot/dts/broadcom/bcm2712-rpi.dtsi | 343 +++ arch/arm/boot/dts/broadcom/bcm2712.dtsi | 1302 +++++++++ arch/arm/boot/dts/broadcom/rp1.dtsi | 1307 +++++++++ arch/arm64/boot/dts/broadcom/Makefile | 6 + .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 2 + arch/arm64/configs/eve_defconfig | 16 + drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-rp1.c | 2422 +++++++++++++++++ drivers/dma/bcm2835-dma.c | 735 ++++- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/bcm2712-iommu-cache.c | 77 + drivers/iommu/bcm2712-iommu.c | 665 +++++ drivers/iommu/bcm2712-iommu.h | 45 + drivers/irqchip/Kconfig | 8 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-bcm2712-mip.c | 323 +++ drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 2 + drivers/mfd/bcm2835-pm.c | 31 +- drivers/mfd/rp1.c | 376 +++ drivers/mmc/host/sdhci-brcmstb.c | 335 ++- drivers/mmc/host/sdhci.c | 10 + drivers/mmc/host/sdhci.h | 1 + drivers/net/ethernet/cadence/macb.h | 75 +- drivers/net/ethernet/cadence/macb_main.c | 456 +++- drivers/net/ethernet/cadence/macb_ptp.c | 87 +- drivers/net/phy/broadcom.c | 275 +- drivers/pci/controller/pcie-brcmstb.c | 634 ++++- .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 160 +- drivers/phy/broadcom/phy-brcm-usb-init.c | 124 +- drivers/phy/broadcom/phy-brcm-usb-init.h | 20 +- drivers/phy/broadcom/phy-brcm-usb.c | 51 +- drivers/pinctrl/Kconfig | 7 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/bcm/Kconfig | 9 + drivers/pinctrl/bcm/Makefile | 1 + drivers/pinctrl/bcm/pinctrl-bcm2712.c | 1247 +++++++++ drivers/pinctrl/pinctrl-rp1.c | 1605 +++++++++++ drivers/soc/bcm/Kconfig | 1 + drivers/soc/bcm/bcm2835-power.c | 32 +- drivers/soc/bcm/brcmstb/Kconfig | 4 +- include/dt-bindings/clock/rp1.h | 56 + include/dt-bindings/mfd/rp1.h | 235 ++ include/linux/brcmphy.h | 68 +- include/linux/platform_data/dma-bcm2708.h | 143 + include/linux/rp1_platform.h | 20 + 49 files changed, 13665 insertions(+), 547 deletions(-) create mode 100644 arch/arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts create mode 100644 arch/arm/boot/dts/broadcom/bcm2712-rpi.dtsi create mode 100644 arch/arm/boot/dts/broadcom/bcm2712.dtsi create mode 100644 arch/arm/boot/dts/broadcom/rp1.dtsi create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts create mode 100644 drivers/clk/clk-rp1.c create mode 100644 drivers/iommu/bcm2712-iommu-cache.c create mode 100644 drivers/iommu/bcm2712-iommu.c create mode 100644 drivers/iommu/bcm2712-iommu.h create mode 100644 drivers/irqchip/irq-bcm2712-mip.c create mode 100644 drivers/mfd/rp1.c create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm2712.c create mode 100644 drivers/pinctrl/pinctrl-rp1.c create mode 100644 include/dt-bindings/clock/rp1.h create mode 100644 include/dt-bindings/mfd/rp1.h create mode 100644 include/linux/platform_data/dma-bcm2708.h create mode 100644 include/linux/rp1_platform.h diff --git a/arch/arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts new file mode 100644 index 0000000000000..26f0dcc081ba9 --- /dev/null +++ b/arch/arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include +#include +#include +#include +#include +#include + +#define i2c0 _i2c0 +#define i2c3 _i2c3 +#define i2c4 _i2c4 +#define i2c5 _i2c5 +#define i2c6 _i2c6 +#define i2c8 _i2c8 +#define i2s _i2s +#define pwm0 _pwm0 +#define pwm1 _pwm1 +#define spi0 _spi0 +#define spi3 _spi3 +#define spi4 _spi4 +#define spi5 _spi5 +#define spi6 _spi6 +#define uart0 _uart0 +#define uart2 _uart2 +#define uart5 _uart5 + +#include "bcm2712.dtsi" + +#undef i2c0 +#undef i2c3 +#undef i2c4 +#undef i2c5 +#undef i2c6 +#undef i2c8 +#undef i2s +#undef pwm0 +#undef pwm1 +#undef spi0 +#undef spi3 +#undef spi4 +#undef spi5 +#undef spi6 +#undef uart0 +#undef uart2 +#undef uart3 +#undef uart4 +#undef uart5 + +/ { + compatible = "raspberrypi,5-model-b", "brcm,bcm2712"; + model = "Raspberry Pi 5"; + + /* Will be filled by the bootloader */ + memory@0 { + device_type = "memory"; + reg = <0 0 0x28000000>; + }; + + leds: leds { + compatible = "gpio-leds"; + + led_pwr: led-pwr { + label = "PWR"; + gpios = <&rp1_gpio 44 GPIO_ACTIVE_LOW>; + default-state = "off"; + linux,default-trigger = "none"; + }; + + led_act: led-act { + label = "ACT"; + gpios = <&gio_aon 9 GPIO_ACTIVE_LOW>; + default-state = "off"; + linux,default-trigger = "mmc0"; + }; + }; + + sd_io_1v8_reg: sd_io_1v8_reg { + compatible = "regulator-gpio"; + regulator-name = "vdd-sd-io"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + regulator-settling-time-us = <5000>; + gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>; + states = <1800000 0x1 + 3300000 0x0>; + status = "okay"; + }; + + sd_vcc_reg: sd_vcc_reg { + compatible = "regulator-fixed"; + regulator-name = "vcc-sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + enable-active-high; + gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + wl_on_reg: wl_on_reg { + compatible = "regulator-fixed"; + regulator-name = "wl-on-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + pinctrl-0 = <&wl_on_pins>; + pinctrl-names = "default"; + + gpio = <&gio 28 GPIO_ACTIVE_HIGH>; + + startup-delay-us = <150000>; + enable-active-high; + }; + + clocks: clocks { + }; + + cam1_clk: cam1_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + status = "disabled"; + }; + + cam0_clk: cam0_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + status = "disabled"; + }; + + cam0_reg: cam0_reg { + compatible = "regulator-fixed"; + regulator-name = "cam0_reg"; + enable-active-high; + status = "okay"; + gpio = <&rp1_gpio 34 0>; // CD0_IO0_MICCLK, to MIPI 0 connector + }; + + cam1_reg: cam1_reg { + compatible = "regulator-fixed"; + regulator-name = "cam1_reg"; + enable-active-high; + status = "okay"; + gpio = <&rp1_gpio 46 0>; // CD1_IO0_MICCLK, to MIPI 1 connector + }; + + cam_dummy_reg: cam_dummy_reg { + compatible = "regulator-fixed"; + regulator-name = "cam-dummy-reg"; + status = "okay"; + }; + + dummy: dummy { + // A target for unwanted overlay fragments + }; + + + // A few extra labels to keep overlays happy + + i2c0if: i2c0if {}; + i2c0mux: i2c0mux {}; +}; + +rp1_target: &pcie2 { + brcm,enable-mps-rcb; + brcm,vdm-qos-map = <0xbbaa9888>; + aspm-no-l0s; + status = "okay"; +}; + +&pcie1 { + brcm,vdm-qos-map = <0x33333333>; +}; + +// Add some labels to 2712 device + +// The system UART +uart10: &_uart0 { status = "okay"; }; + +// The system SPI for the bootloader EEPROM +spi10: &_spi0 { status = "okay"; }; + +i2c_rp1boot: &_i2c3 { }; + +#include "rp1.dtsi" + +&rp1 { + // PCIe address space layout: + // 00_00000000-00_00xxxxxx = RP1 peripherals + // 10_00000000-1x_xxxxxxxx = up to 64GB system RAM + + // outbound access aimed at PCIe 0_00xxxxxx -> RP1 c0_40xxxxxx + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 + 0x00 0x00400000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x10 0x00000000 + 0x43000000 0x10 0x00000000 + 0x10 0x00000000>, + + // inbound RP1 c0_40xxxxxx -> PCIe 00_00xxxxxx + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 + 0x0 0x00400000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 + 0x02000000 0x10 0x00000000 + 0x10 0x00000000>; +}; + +// Expose RP1 nodes as system nodes with labels + +&rp1_dma { + status = "okay"; +}; + +&rp1_eth { + status = "okay"; + phy-handle = <&phy1>; + phy-reset-gpios = <&rp1_gpio 32 GPIO_ACTIVE_LOW>; + phy-reset-duration = <5>; + + phy1: ethernet-phy@1 { + reg = <0x1>; + brcm,powerdown-enable; + }; +}; + +gpio: &rp1_gpio { + status = "okay"; +}; + +aux: &dummy {}; + +&rp1_usb0 { + pinctrl-0 = <&usb_vbus_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&rp1_usb1 { + status = "okay"; +}; + +#include "bcm2712-rpi.dtsi" + +i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only + pinctrl-0 = <&rp1_i2c6_38_39>; + pinctrl-names = "default"; + clock-frequency = <100000>; +}; + +i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only + pinctrl-0 = <&rp1_i2c4_40_41>; + pinctrl-names = "default"; + clock-frequency = <100000>; +}; + +i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility + +csi0: &rp1_csi0 { }; +csi1: &rp1_csi1 { }; +dsi0: &rp1_dsi0 { }; +dsi1: &rp1_dsi1 { }; +dpi: &rp1_dpi { }; +vec: &rp1_vec { }; +dpi_gpio0: &rp1_dpi_24bit_gpio0 { }; +dpi_gpio1: &rp1_dpi_24bit_gpio2 { }; +dpi_18bit_cpadhi_gpio0: &rp1_dpi_18bit_cpadhi_gpio0 { }; +dpi_18bit_cpadhi_gpio2: &rp1_dpi_18bit_cpadhi_gpio2 { }; +dpi_18bit_gpio0: &rp1_dpi_18bit_gpio0 { }; +dpi_18bit_gpio2: &rp1_dpi_18bit_gpio2 { }; +dpi_16bit_cpadhi_gpio0: &rp1_dpi_16bit_cpadhi_gpio0 { }; +dpi_16bit_cpadhi_gpio2: &rp1_dpi_16bit_cpadhi_gpio2 { }; +dpi_16bit_gpio0: &rp1_dpi_16bit_gpio0 { }; +dpi_16bit_gpio2: &rp1_dpi_16bit_gpio2 { }; + +/* Add the IOMMUs for some RP1 bus masters */ + +&csi0 { + iommus = <&iommu5>; +}; + +&csi1 { + iommus = <&iommu5>; +}; + +&dsi0 { + iommus = <&iommu5>; +}; + +&dsi1 { + iommus = <&iommu5>; +}; + +&dpi { + iommus = <&iommu5>; +}; + +&vec { + iommus = <&iommu5>; +}; + +&ddc0 { + status = "disabled"; +}; + +&ddc1 { + status = "disabled"; +}; + +&hdmi0 { + clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>; + clock-names = "hdmi", "bvb", "audio", "cec"; + status = "disabled"; +}; + +&hdmi1 { + clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>; + clock-names = "hdmi", "bvb", "audio", "cec"; + status = "disabled"; +}; + +&hvs { + clocks = <&firmware_clocks 4>, <&firmware_clocks 16>; + clock-names = "core", "disp"; +}; + +&mop { + status = "disabled"; +}; + +&moplet { + status = "disabled"; +}; + +&pixelvalve0 { + status = "disabled"; +}; + +&pixelvalve1 { + status = "disabled"; +}; + +&disp_intr { + status = "disabled"; +}; + +/* SDIO1 is used to drive the SD card */ +&sdio1 { + pinctrl-0 = <&emmc_sd_pulls>, <&emmc_aon_cd_pins>; + pinctrl-names = "default"; + vqmmc-supply = <&sd_io_1v8_reg>; + vmmc-supply = <&sd_vcc_reg>; + bus-width = <4>; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr104; + cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>; + //no-1-8-v; + status = "okay"; +}; + +&pinctrl_aon { + emmc_aon_cd_pins: emmc_aon_cd_pins { + function = "sd_card_g"; + pins = "aon_gpio5"; + bias-pull-up; + }; + + /* Slight hack - only one PWM pin (status LED) is usable */ + aon_pwm_1pin: aon_pwm_1pin { + function = "aon_pwm"; + pins = "aon_gpio9"; + }; +}; + +&pinctrl { + pwr_button_pins: pwr_button_pins { + function = "gpio"; + pins = "gpio20"; + bias-pull-up; + }; + + wl_on_pins: wl_on_pins { + function = "gpio"; + pins = "gpio28"; + }; + + bt_shutdown_pins: bt_shutdown_pins { + function = "gpio"; + pins = "gpio29"; + }; + + emmc_sd_pulls: emmc_sd_pulls { + pins = "emmc_cmd", "emmc_dat0", "emmc_dat1", "emmc_dat2", "emmc_dat3"; + bias-pull-up; + }; +}; + +/* uarta communicates with the BT module */ +&uarta { + uart-has-rtscts; + auto-flow-control; + status = "okay"; + clock-frequency = <96000000>; + pinctrl-0 = <&uarta_24_pins &bt_shutdown_pins>; + pinctrl-names = "default"; + + bluetooth: bluetooth { + compatible = "brcm,bcm43438-bt"; + max-speed = <3000000>; + shutdown-gpios = <&gio 29 GPIO_ACTIVE_HIGH>; + local-bd-address = [ 00 00 00 00 00 00 ]; + }; +}; + +&i2c_rp1boot { + clock-frequency = <400000>; + pinctrl-0 = <&i2c3_m4_agpio0_pins>; + pinctrl-names = "default"; +}; + +/ { + chosen: chosen { + bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe"; + stdout-path = "serial10:115200n8"; + }; + + fan: cooling_fan { + status = "disabled"; + compatible = "pwm-fan"; + #cooling-cells = <2>; + cooling-min-state = <0>; + cooling-max-state = <3>; + cooling-levels = <0 75 125 175 250>; + pwms = <&rp1_pwm1 3 41566 PWM_POLARITY_INVERTED>; + rpm-regmap = <&rp1_pwm1>; + rpm-offset = <0x3c>; + }; + + pwr_button { + compatible = "gpio-keys"; + + pinctrl-names = "default"; + pinctrl-0 = <&pwr_button_pins>; + status = "okay"; + + pwr_key: pwr { + label = "pwr_button"; + // linux,code = <205>; // KEY_SUSPEND + linux,code = <116>; // KEY_POWER + gpios = <&gio 20 GPIO_ACTIVE_LOW>; + debounce-interval = <50>; // ms + }; + }; +}; + +&usb { + power-domains = <&power RPI_POWER_DOMAIN_USB>; +}; + +/* SDIO2 drives the WLAN interface */ +&sdio2 { + pinctrl-0 = <&sdio2_30_pins>; + pinctrl-names = "default"; + bus-width = <4>; + vmmc-supply = <&wl_on_reg>; + sd-uhs-ddr50; + non-removable; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + wifi: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + local-mac-address = [00 00 00 00 00 00]; + }; +}; + +&rpivid { + status = "okay"; +}; + +&pinctrl { + spi10_gpio2: spi10_gpio2 { + function = "vc_spi0"; + pins = "gpio2", "gpio3", "gpio4"; + bias-disable; + }; + + spi10_cs_gpio1: spi10_cs_gpio1 { + function = "gpio"; + pins = "gpio1"; + bias-pull-up; + }; +}; + +spi10_pins: &spi10_gpio2 {}; +spi10_cs_pins: &spi10_cs_gpio1 {}; + +&spi10 { + pinctrl-names = "default"; + cs-gpios = <&gio 1 1>; + pinctrl-0 = <&spi10_pins &spi10_cs_pins>; + + spidev10: spidev@0 { + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <20000000>; + status = "okay"; + }; +}; + +// ============================================= +// Board specific stuff here + +&gio_aon { + // Don't use GIO_AON as an interrupt controller because it will + // clash with the firmware monitoring the PMIC interrupt via the VPU. + + /delete-property/ interrupt-controller; +}; + +&main_aon_irq { + // Don't use the MAIN_AON_IRQ interrupt controller because it will + // clash with the firmware monitoring the PMIC interrupt via the VPU. + + status = "disabled"; +}; + +&rp1_pwm1 { + status = "disabled"; + pinctrl-0 = <&rp1_pwm1_gpio45>; + pinctrl-names = "default"; +}; + +&thermal_trips { + cpu_tepid: cpu-tepid { + temperature = <50000>; + hysteresis = <5000>; + type = "active"; + }; + + cpu_warm: cpu-warm { + temperature = <60000>; + hysteresis = <5000>; + type = "active"; + }; + + cpu_hot: cpu-hot { + temperature = <67500>; + hysteresis = <5000>; + type = "active"; + }; + + cpu_vhot: cpu-vhot { + temperature = <75000>; + hysteresis = <5000>; + type = "active"; + }; +}; + +&cooling_maps { + tepid { + trip = <&cpu_tepid>; + cooling-device = <&fan 1 1>; + }; + + warm { + trip = <&cpu_warm>; + cooling-device = <&fan 2 2>; + }; + + hot { + trip = <&cpu_hot>; + cooling-device = <&fan 3 3>; + }; + + vhot { + trip = <&cpu_vhot>; + cooling-device = <&fan 4 4>; + }; + + melt { + trip = <&cpu_crit>; + cooling-device = <&fan 4 4>; + }; +}; + +&gio { + // The GPIOs above 35 are not used on Pi 5, so shrink the upper bank + // to reduce the clutter in gpioinfo/pinctrl + brcm,gpio-bank-widths = <32 4>; + + gpio-line-names = + "-", // GPIO_000 + "2712_BOOT_CS_N", // GPIO_001 + "2712_BOOT_MISO", // GPIO_002 + "2712_BOOT_MOSI", // GPIO_003 + "2712_BOOT_SCLK", // GPIO_004 + "-", // GPIO_005 + "-", // GPIO_006 + "-", // GPIO_007 + "-", // GPIO_008 + "-", // GPIO_009 + "-", // GPIO_010 + "-", // GPIO_011 + "-", // GPIO_012 + "-", // GPIO_013 + "PCIE_SDA", // GPIO_014 + "PCIE_SCL", // GPIO_015 + "-", // GPIO_016 + "-", // GPIO_017 + "-", // GPIO_018 + "-", // GPIO_019 + "PWR_GPIO", // GPIO_020 + "2712_G21_FS", // GPIO_021 + "-", // GPIO_022 + "-", // GPIO_023 + "BT_RTS", // GPIO_024 + "BT_CTS", // GPIO_025 + "BT_TXD", // GPIO_026 + "BT_RXD", // GPIO_027 + "WL_ON", // GPIO_028 + "BT_ON", // GPIO_029 + "WIFI_SDIO_CLK", // GPIO_030 + "WIFI_SDIO_CMD", // GPIO_031 + "WIFI_SDIO_D0", // GPIO_032 + "WIFI_SDIO_D1", // GPIO_033 + "WIFI_SDIO_D2", // GPIO_034 + "WIFI_SDIO_D3"; // GPIO_035 +}; + +&gio_aon { + gpio-line-names = + "RP1_SDA", // AON_GPIO_00 + "RP1_SCL", // AON_GPIO_01 + "RP1_RUN", // AON_GPIO_02 + "SD_IOVDD_SEL", // AON_GPIO_03 + "SD_PWR_ON", // AON_GPIO_04 + "SD_CDET_N", // AON_GPIO_05 + "SD_FLG_N", // AON_GPIO_06 + "-", // AON_GPIO_07 + "2712_WAKE", // AON_GPIO_08 + "2712_STAT_LED", // AON_GPIO_09 + "-", // AON_GPIO_10 + "-", // AON_GPIO_11 + "PMIC_INT", // AON_GPIO_12 + "UART_TX_FS", // AON_GPIO_13 + "UART_RX_FS", // AON_GPIO_14 + "-", // AON_GPIO_15 + "-", // AON_GPIO_16 + + // Pad bank0 out to 32 entries + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + + "HDMI0_SCL", // AON_SGPIO_00 + "HDMI0_SDA", // AON_SGPIO_01 + "HDMI1_SCL", // AON_SGPIO_02 + "HDMI1_SDA", // AON_SGPIO_03 + "PMIC_SCL", // AON_SGPIO_04 + "PMIC_SDA"; // AON_SGPIO_05 + + rp1_run_hog { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "RP1 RUN pin"; + }; +}; + +&rp1_gpio { + gpio-line-names = + "ID_SDA", // GPIO0 + "ID_SCL", // GPIO1 + "GPIO2", // GPIO2 + "GPIO3", // GPIO3 + "GPIO4", // GPIO4 + "GPIO5", // GPIO5 + "GPIO6", // GPIO6 + "GPIO7", // GPIO7 + "GPIO8", // GPIO8 + "GPIO9", // GPIO9 + "GPIO10", // GPIO10 + "GPIO11", // GPIO11 + "GPIO12", // GPIO12 + "GPIO13", // GPIO13 + "GPIO14", // GPIO14 + "GPIO15", // GPIO15 + "GPIO16", // GPIO16 + "GPIO17", // GPIO17 + "GPIO18", // GPIO18 + "GPIO19", // GPIO19 + "GPIO20", // GPIO20 + "GPIO21", // GPIO21 + "GPIO22", // GPIO22 + "GPIO23", // GPIO23 + "GPIO24", // GPIO24 + "GPIO25", // GPIO25 + "GPIO26", // GPIO26 + "GPIO27", // GPIO27 + + "PCIE_RP1_WAKE", // GPIO28 + "FAN_TACH", // GPIO29 + "HOST_SDA", // GPIO30 + "HOST_SCL", // GPIO31 + "ETH_RST_N", // GPIO32 + "-", // GPIO33 + + "CD0_IO0_MICCLK", // GPIO34 + "CD0_IO0_MICDAT0", // GPIO35 + "RP1_PCIE_CLKREQ_N", // GPIO36 + "-", // GPIO37 + "CD0_SDA", // GPIO38 + "CD0_SCL", // GPIO39 + "CD1_SDA", // GPIO40 + "CD1_SCL", // GPIO41 + "USB_VBUS_EN", // GPIO42 + "USB_OC_N", // GPIO43 + "RP1_STAT_LED", // GPIO44 + "FAN_PWM", // GPIO45 + "CD1_IO0_MICCLK", // GPIO46 + "2712_WAKE", // GPIO47 + "CD1_IO1_MICDAT1", // GPIO48 + "EN_MAX_USB_CUR", // GPIO49 + "-", // GPIO50 + "-", // GPIO51 + "-", // GPIO52 + "-"; // GPIO53 + + usb_vbus_pins: usb_vbus_pins { + function = "vbus1"; + pins = "gpio42", "gpio43"; + }; +}; + +/ { + aliases: aliases { + blconfig = &blconfig; + blpubkey = &blpubkey; + bluetooth = &bluetooth; + console = &uart10; + ethernet0 = &rp1_eth; + wifi0 = &wifi; + fb = &fb; + mailbox = &mailbox; + mmc0 = &sdio1; + uart0 = &uart0; + uart1 = &uart1; + uart2 = &uart2; + uart3 = &uart3; + uart4 = &uart4; + uart10 = &uart10; + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + serial4 = &uart4; + serial10 = &uart10; + i2c = &i2c_arm; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + i2c6 = &i2c6; + i2c10 = &i2c_rp1boot; + // Bit-bashed i2c_gpios start at 10 + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + spi3 = &spi3; + spi4 = &spi4; + spi5 = &spi5; + spi10 = &spi10; + gpio0 = &gpio; + gpio1 = &gio; + gpio2 = &gio_aon; + gpio3 = &pinctrl; + gpio4 = &pinctrl_aon; + usb0 = &rp1_usb0; + usb1 = &rp1_usb1; + drm-dsi1 = &dsi0; + drm-dsi2 = &dsi1; + }; + + __overrides__ { + bdaddr = <&bluetooth>, "local-bd-address["; + button_debounce = <&pwr_key>, "debounce-interval:0"; + cooling_fan = <&fan>, "status", <&rp1_pwm1>, "status"; + uart0_console = <&uart0>,"status", <&aliases>, "console=",&uart0; + i2c0 = <&i2c0>, "status"; + i2c1 = <&i2c1>, "status"; + i2c = <&i2c1>, "status"; + i2c_arm = <&i2c_arm>, "status"; + i2c_vc = <&i2c_vc>, "status"; + i2c_csi_dsi = <&i2c_csi_dsi>, "status"; + i2c_csi_dsi0 = <&i2c_csi_dsi0>, "status"; + i2c_csi_dsi1 = <&i2c_csi_dsi1>, "status"; + i2c0_baudrate = <&i2c0>, "clock-frequency:0"; + i2c1_baudrate = <&i2c1>, "clock-frequency:0"; + i2c_baudrate = <&i2c_arm>, "clock-frequency:0"; + i2c_arm_baudrate = <&i2c_arm>, "clock-frequency:0"; + i2c_vc_baudrate = <&i2c_vc>, "clock-frequency:0"; + krnbt = <&bluetooth>, "status"; + nvme = <&pciex1>, "status"; + pciex1 = <&pciex1>, "status"; + pciex1_gen = <&pciex1> , "max-link-speed:0"; + pciex1_no_l0s = <&pciex1>, "aspm-no-l0s?"; + pciex1_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0"; + pcie_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0"; + random = <&random>, "status"; + rtc = <&rpi_rtc>, "status"; + rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0"; + sd_cqe = <&sdio1>, "supports-cqe?"; + spi = <&spi0>, "status"; + suspend = <&pwr_key>, "linux,code:0=205"; + uart0 = <&uart0>, "status"; + wifiaddr = <&wifi>, "local-mac-address["; + + act_led_gpio = <&led_act>,"gpios:4",<&led_act>,"gpios:0=",<&gpio>; + act_led_activelow = <&led_act>,"gpios:8"; + act_led_trigger = <&led_act>, "linux,default-trigger"; + pwr_led_gpio = <&led_pwr>,"gpios:4"; + pwr_led_activelow = <&led_pwr>, "gpios:8"; + pwr_led_trigger = <&led_pwr>, "linux,default-trigger"; + eth_led0 = <&phy1>,"led-modes:0"; + eth_led1 = <&phy1>,"led-modes:4"; + drm_fb0_rp1_dsi0 = <&aliases>, "drm-fb0=",&dsi0; + drm_fb0_rp1_dsi1 = <&aliases>, "drm-fb0=",&dsi1; + drm_fb0_rp1_dpi = <&aliases>, "drm-fb0=",&dpi; + drm_fb0_vc4 = <&aliases>, "drm-fb0=",&vc4; + drm_fb1_rp1_dsi0 = <&aliases>, "drm-fb1=",&dsi0; + drm_fb1_rp1_dsi1 = <&aliases>, "drm-fb1=",&dsi1; + drm_fb1_rp1_dpi = <&aliases>, "drm-fb1=",&dpi; + drm_fb1_vc4 = <&aliases>, "drm-fb1=",&vc4; + drm_fb2_rp1_dsi0 = <&aliases>, "drm-fb2=",&dsi0; + drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1; + drm_fb2_rp1_dpi = <&aliases>, "drm-fb2=",&dpi; + drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4; + + fan_temp0 = <&cpu_tepid>,"temperature:0"; + fan_temp1 = <&cpu_warm>,"temperature:0"; + fan_temp2 = <&cpu_hot>,"temperature:0"; + fan_temp3 = <&cpu_vhot>,"temperature:0"; + fan_temp0_hyst = <&cpu_tepid>,"hysteresis:0"; + fan_temp1_hyst = <&cpu_warm>,"hysteresis:0"; + fan_temp2_hyst = <&cpu_hot>,"hysteresis:0"; + fan_temp3_hyst = <&cpu_vhot>,"hysteresis:0"; + fan_temp0_speed = <&fan>, "cooling-levels:4"; + fan_temp1_speed = <&fan>, "cooling-levels:8"; + fan_temp2_speed = <&fan>, "cooling-levels:12"; + fan_temp3_speed = <&fan>, "cooling-levels:16"; + }; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm2712-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2712-rpi.dtsi new file mode 100644 index 0000000000000..cbaab8246d76f --- /dev/null +++ b/arch/arm/boot/dts/broadcom/bcm2712-rpi.dtsi @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +&soc { + firmware: firmware { + compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + + mboxes = <&mailbox>; + dma-ranges; + + firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; + + reset: reset { + compatible = "raspberrypi,firmware-reset"; + #reset-cells = <1>; + }; + + vcio: vcio { + compatible = "raspberrypi,vcio"; + }; + }; + + power: power { + compatible = "raspberrypi,bcm2835-power"; + firmware = <&firmware>; + #power-domain-cells = <1>; + }; + + fb: fb { + compatible = "brcm,bcm2708-fb"; + firmware = <&firmware>; + status = "okay"; + }; + + rpi_rtc: rpi_rtc { + compatible = "raspberrypi,rpi-rtc"; + firmware = <&firmware>; + status = "okay"; + trickle-charge-microvolt = <0>; + }; + + nvmem { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + + nvmem_otp: nvmem_otp { + compatible = "raspberrypi,rpi-otp"; + firmware = <&firmware>; + reg = <0 192>; + status = "okay"; + }; + + nvmem_cust: nvmem_cust { + compatible = "raspberrypi,rpi-otp"; + firmware = <&firmware>; + reg = <1 8>; + status = "okay"; + }; + + nvmem_mac: nvmem_mac { + compatible = "raspberrypi,rpi-otp"; + firmware = <&firmware>; + reg = <2 6>; + status = "okay"; + }; + + nvmem_priv: nvmem_priv { + compatible = "raspberrypi,rpi-otp"; + firmware = <&firmware>; + reg = <3 16>; + status = "okay"; + }; + }; + + /* Define these notional regulators for use by overlays, etc. */ + vdd_3v3_reg: fixedregulator_3v3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3"; + }; + + vdd_5v0_reg: fixedregulator_5v0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "5v0"; + }; +}; + +/ { + __overrides__ { + arm_freq; + axiperf = <&axiperf>,"status"; + + nvmem_cust_rw = <&nvmem_cust>,"rw?"; + nvmem_priv_rw = <&nvmem_priv>,"rw?"; + nvmem_mac_rw = <&nvmem_mac>,"rw?"; + strict_gpiod = <&chosen>, "bootargs=pinctrl_rp1.persist_gpio_outputs=n"; + }; +}; + +pciex1: &pcie1 { }; +pciex4: &pcie2 { }; + +&dma32 { + /* The VPU firmware uses DMA channel 11 for VCHIQ */ + brcm,dma-channel-mask = <0x03f>; +}; + +&dma40 { + /* The VPU firmware DMA channel 11 for VCHIQ */ + brcm,dma-channel-mask = <0x07c0>; +}; + +&hdmi0 { + dmas = <&dma40 (10|(1<<30)|(1<<24)|(10<<16)|(15<<20))>; +}; + +&hdmi1 { + dmas = <&dma40 (17|(1<<30)|(1<<24)|(10<<16)|(15<<20))>; +}; + +&spi10 { + dmas = <&dma40 6>, <&dma40 7>; + dma-names = "tx", "rx"; +}; + +&usb { + power-domains = <&power RPI_POWER_DOMAIN_USB>; +}; + +&rmem { + /* + * RPi5's co-processor will copy the board's bootloader configuration + * into memory for the OS to consume. It'll also update this node with + * its placement information. + */ + blconfig: nvram@0 { + compatible = "raspberrypi,bootloader-config", "nvmem-rmem"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x0>; + no-map; + status = "disabled"; + }; + /* + * RPi5 will copy the binary public key blob (if present) from the bootloader + * into memory for use by the OS. + */ + blpubkey: nvram@1 { + compatible = "raspberrypi,bootloader-public-key", "nvmem-rmem"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x0>; + no-map; + status = "disabled"; + }; +}; + +&rp1_adc { + status = "okay"; +}; + +/* Add some gpiomem nodes to make the devices accessible to userspace. + * /dev/gpiomem should expose the registers for the interface with DT alias + * gpio. + */ + +&rp1 { + gpiomem@d0000 { + /* Export IO_BANKs, RIO_BANKs and PADS_BANKs to userspace */ + compatible = "raspberrypi,gpiomem"; + reg = <0xc0 0x400d0000 0x0 0x30000>; + chardev-name = "gpiomem0"; + }; +}; + +&soc { + gpiomem@7d508500 { + compatible = "raspberrypi,gpiomem"; + reg = <0x7d508500 0x40>; + chardev-name = "gpiomem1"; + }; + + gpiomem@7d517c00 { + compatible = "raspberrypi,gpiomem"; + reg = <0x7d517c00 0x40>; + chardev-name = "gpiomem2"; + }; + + gpiomem@7d504100 { + compatible = "raspberrypi,gpiomem"; + reg = <0x7d504100 0x20>; + chardev-name = "gpiomem3"; + }; + + gpiomem@7d510700 { + compatible = "raspberrypi,gpiomem"; + reg = <0x7d510700 0x20>; + chardev-name = "gpiomem4"; + }; + + sound: sound { + status = "disabled"; + }; +}; + +i2c0: &rp1_i2c0 { }; +i2c1: &rp1_i2c1 { }; +i2c2: &rp1_i2c2 { }; +i2c3: &rp1_i2c3 { }; +i2c4: &rp1_i2c4 { }; +i2c5: &rp1_i2c5 { }; +i2c6: &rp1_i2c6 { }; +i2s: &rp1_i2s0 { }; +i2s_clk_producer: &rp1_i2s0 { }; +i2s_clk_consumer: &rp1_i2s1 { }; +pwm0: &rp1_pwm0 { }; +pwm1: &rp1_pwm1 { }; +pwm: &pwm0 { }; +spi0: &rp1_spi0 { }; +spi1: &rp1_spi1 { }; +spi2: &rp1_spi2 { }; +spi3: &rp1_spi3 { }; +spi4: &rp1_spi4 { }; +spi5: &rp1_spi5 { }; + +uart0_pins: &rp1_uart0_14_15 {}; +uart0_ctsrts_pins: &rp1_uart0_ctsrts_16_17 {}; +uart0: &rp1_uart0 { + pinctrl-0 = <&uart0_pins>; +}; + +uart1_pins: &rp1_uart1_0_1 {}; +uart1_ctsrts_pins: &rp1_uart1_ctsrts_2_3 {}; +uart1: &rp1_uart1 { }; + +uart2_pins: &rp1_uart2_4_5 {}; +uart2_ctsrts_pins: &rp1_uart2_ctsrts_6_7 {}; +uart2: &rp1_uart2 { }; + +uart3_pins: &rp1_uart3_8_9 {}; +uart3_ctsrts_pins: &rp1_uart3_ctsrts_10_11 {}; +uart3: &rp1_uart3 { }; + +uart4_pins: &rp1_uart4_12_13 {}; +uart4_ctsrts_pins: &rp1_uart4_ctsrts_14_15 {}; +uart4: &rp1_uart4 { }; + +i2c0_pins: &rp1_i2c0_0_1 {}; +i2c_vc: &i2c0 { // This is pins 27,28 on the header (not MIPI) + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + clock-frequency = <100000>; +}; + +i2c1_pins: &rp1_i2c1_2_3 {}; +i2c_arm: &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + clock-frequency = <100000>; +}; + +i2c2_pins: &rp1_i2c2_4_5 {}; +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; +}; + +i2c3_pins: &rp1_i2c3_6_7 {}; +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; +}; + +&i2s_clk_producer { + pinctrl-names = "default"; + pinctrl-0 = <&rp1_i2s0_18_21>; +}; + +&i2s_clk_consumer { + pinctrl-names = "default"; + pinctrl-0 = <&rp1_i2s1_18_21>; +}; + +spi0_pins: &rp1_spi0_gpio9 {}; +spi0_cs_pins: &rp1_spi0_cs_gpio7 {}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + + spidev0: spidev@0 { + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <125000000>; + }; + + spidev1: spidev@1 { + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <125000000>; + }; +}; + +spi2_pins: &rp1_spi2_gpio1 {}; +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_pins>; +}; + +spi3_pins: &rp1_spi3_gpio5 {}; +&spi3 { + pinctrl-names = "default"; + pinctrl-0 = <&spi3_pins>; +}; + +spi4_pins: &rp1_spi4_gpio9 {}; +&spi4 { + pinctrl-names = "default"; + pinctrl-0 = <&spi4_pins>; +}; + +spi5_pins: &rp1_spi5_gpio13 {}; +&spi5 { + pinctrl-names = "default"; + pinctrl-0 = <&spi5_pins>; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm2712.dtsi b/arch/arm/boot/dts/broadcom/bcm2712.dtsi new file mode 100644 index 0000000000000..9df5d5c2bcf5a --- /dev/null +++ b/arch/arm/boot/dts/broadcom/bcm2712.dtsi @@ -0,0 +1,1302 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +/ { + compatible = "brcm,bcm2712", "brcm,bcm2711"; + model = "BCM2712"; + + #address-cells = <2>; + #size-cells = <1>; + + interrupt-parent = <&gicv2>; + + rmem: reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + atf@0 { + reg = <0x0 0x0 0x80000>; + no-map; + }; + + cma: linux,cma { + compatible = "shared-dma-pool"; + size = <0x4000000>; /* 64MB */ + reusable; + linux,cma-default; + + /* + * arm64 reserves the CMA by default somewhere in + * ZONE_DMA32, that's not good enough for the BCM2711 + * as some devices can only address the lower 1G of + * memory (ZONE_DMA). + */ + alloc-ranges = <0x0 0x00000000 0x40000000>; + }; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <2000>; + polling-delay = <1000>; + coefficients = <(-550) 450000>; + thermal-sensors = <&thermal>; + + thermal_trips: trips { + cpu_crit: cpu-crit { + temperature = <110000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling_maps: cooling-maps { + }; + }; + }; + + clk_27MHz: clk-27M { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "27MHz-clock"; + }; + + clk_108MHz: clk-108M { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <108000000>; + clock-output-names = "108MHz-clock"; + }; + + hvs: hvs@107c580000 { + compatible = "brcm,bcm2712-hvs"; + reg = <0x10 0x7c580000 0x1a000>; + interrupt-parent = <&disp_intr>; + interrupts = <2>, <9>, <16>; + interrupt-names = "ch0-eof", "ch1-eof", "ch2-eof"; + //iommus = <&iommu4>; + status = "disabled"; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x7c000000 0x10 0x7c000000 0x04000000>; + /* Emulate a contiguous 30-bit address range for DMA */ + dma-ranges = <0xc0000000 0x00 0x00000000 0x40000000>, + <0x7c000000 0x10 0x7c000000 0x04000000>; + + system_timer: timer@7c003000 { + compatible = "brcm,bcm2835-system-timer"; + reg = <0x7c003000 0x1000>; + interrupts = , + , + , + ; + clock-frequency = <1000000>; + }; + + firmwarekms: firmwarekms@7d503000 { + compatible = "raspberrypi,rpi-firmware-kms-2712"; + /* SUN_L2 interrupt reg */ + reg = <0x7d503000 0x18>; + interrupt-parent = <&cpu_l2_irq>; + interrupts = <19>; + brcm,firmware = <&firmware>; + status = "disabled"; + }; + + axiperf: axiperf { + compatible = "brcm,bcm2712-axiperf"; + reg = <0x7c012800 0x100>, + <0x7e000000 0x100>; + firmware = <&firmware>; + status = "disabled"; + }; + + mailbox: mailbox@7c013880 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7c013880 0x40>; + interrupts = ; + #mbox-cells = <0>; + }; + + pixelvalve0: pixelvalve@7c410000 { + compatible = "brcm,bcm2712-pixelvalve0"; + reg = <0x7c410000 0x100>; + interrupts = ; + status = "disabled"; + }; + + pixelvalve1: pixelvalve@7c411000 { + compatible = "brcm,bcm2712-pixelvalve1"; + reg = <0x7c411000 0x100>; + interrupts = ; + status = "disabled"; + }; + + mop: mop@7c500000 { + compatible = "brcm,bcm2712-mop"; + reg = <0x7c500000 0x28>; + interrupt-parent = <&disp_intr>; + interrupts = <1>; + status = "disabled"; + }; + + moplet: moplet@7c501000 { + compatible = "brcm,bcm2712-moplet"; + reg = <0x7c501000 0x20>; + interrupt-parent = <&disp_intr>; + interrupts = <0>; + status = "disabled"; + }; + + disp_intr: interrupt-controller@7c502000 { + compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc"; + reg = <0x7c502000 0x30>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + status = "disabled"; + }; + + dvp: clock@7c700000 { + compatible = "brcm,brcm2711-dvp"; + reg = <0x7c700000 0x10>; + clocks = <&clk_108MHz>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + /* + * This node is the provider for the enable-method for + * bringing up secondary cores. + */ + local_intc: local_intc@7cd00000 { + compatible = "brcm,bcm2836-l1-intc"; + reg = <0x7cd00000 0x100>; + }; + + uart0: serial@7d001000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x7d001000 0x200>; + interrupts = ; + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; + arm,primecell-periphid = <0x00241011>; + status = "disabled"; + }; + + uart2: serial@7d001400 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x7d001400 0x200>; + interrupts = ; + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; + arm,primecell-periphid = <0x00241011>; + status = "disabled"; + }; + + uart5: serial@7d001a00 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x7d001a00 0x200>; + interrupts = ; + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; + arm,primecell-periphid = <0x00241011>; + status = "disabled"; + }; + + sdhost: mmc@7d002000 { + compatible = "brcm,bcm2835-sdhost"; + reg = <0x7d002000 0x100>; + //interrupts = ; + clocks = <&clk_vpu>; + status = "disabled"; + }; + + i2s: i2s@7d003000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7d003000 0x24>; + //clocks = <&cprman BCM2835_CLOCK_PCM>; + status = "disabled"; + }; + + spi0: spi@7d004000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7d004000 0x200>; + interrupts = ; + clocks = <&clk_vpu>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi3: spi@7d004600 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7d004600 0x0200>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi4: spi@7d004800 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7d004800 0x0200>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi5: spi@7d004a00 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7d004a00 0x0200>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi6: spi@7d004c00 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7d004c00 0x0200>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c0: i2c@7d005000 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005000 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c3: i2c@7d005600 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005600 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@7d005800 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005800 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c5: i2c@7d005a00 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005a00 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c6: i2c@7d005c00 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005c00 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c8: i2c@7d005e00 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + reg = <0x7d005e00 0x20>; + interrupts = ; + clocks = <&clk_vpu>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pwm0: pwm@7d00c000 { + compatible = "brcm,bcm2835-pwm"; + reg = <0x7d00c000 0x28>; + assigned-clock-rates = <50000000>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pwm1: pwm@7d00c800 { + compatible = "brcm,bcm2835-pwm"; + reg = <0x7d00c800 0x28>; + assigned-clock-rates = <50000000>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pm: watchdog@7d200000 { + compatible = "brcm,bcm2712-pm"; + reg = <0x7d200000 0x308>; + reg-names = "pm"; + #power-domain-cells = <1>; + #reset-cells = <1>; + //clocks = <&cprman BCM2835_CLOCK_V3D>, + // <&cprman BCM2835_CLOCK_PERI_IMAGE>, + // <&cprman BCM2835_CLOCK_H264>, + // <&cprman BCM2835_CLOCK_ISP>; + clock-names = "v3d", "peri_image", "h264", "isp"; + system-power-controller; + }; + + cprman: cprman@7d202000 { + compatible = "brcm,bcm2711-cprman"; + reg = <0x7d202000 0x2000>; + #clock-cells = <1>; + + /* CPRMAN derives almost everything from the + * platform's oscillator. However, the DSI + * pixel clocks come from the DSI analog PHY. + */ + clocks = <&clk_osc>; + status = "disabled"; + }; + + random: rng@7d208000 { + compatible = "brcm,bcm2711-rng200"; + reg = <0x7d208000 0x28>; + status = "okay"; + }; + + cpu_l2_irq: intc@7d503000 { + compatible = "brcm,l2-intc"; + reg = <0x7d503000 0x18>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + pinctrl: pinctrl@7d504100 { + compatible = "brcm,bcm2712-pinctrl"; + reg = <0x7d504100 0x30>; + + uarta_24_pins: uarta_24_pins { + pin_rts { + function = "uart0"; + pins = "gpio24"; + bias-disable; + }; + pin_cts { + function = "uart0"; + pins = "gpio25"; + bias-pull-up; + }; + pin_txd { + function = "uart0"; + pins = "gpio26"; + bias-disable; + }; + pin_rxd { + function = "uart0"; + pins = "gpio27"; + bias-pull-up; + }; + }; + + sdio2_30_pins: sdio2_30_pins { + pin_clk { + function = "sd2"; + pins = "gpio30"; + bias-disable; + }; + pin_cmd { + function = "sd2"; + pins = "gpio31"; + bias-pull-up; + }; + pins_dat { + function = "sd2"; + pins = "gpio32", "gpio33", "gpio34", "gpio35"; + bias-pull-up; + }; + }; + }; + + ddc0: i2c@7d508200 { + compatible = "brcm,brcmstb-i2c"; + reg = <0x7d508200 0x58>; + interrupt-parent = <&bsc_irq>; + interrupts = <1>; + clock-frequency = <97500>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + ddc1: i2c@7d508280 { + compatible = "brcm,brcmstb-i2c"; + reg = <0x7d508280 0x58>; + interrupt-parent = <&bsc_irq>; + interrupts = <2>; + clock-frequency = <97500>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + bscd: i2c@7d508300 { + compatible = "brcm,brcmstb-i2c"; + reg = <0x7d508300 0x58>; + interrupt-parent = <&bsc_irq>; + interrupts = <0>; + clock-frequency = <200000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + bsc_irq: intc@7d508380 { + compatible = "brcm,bcm7271-l2-intc"; + reg = <0x7d508380 0x10>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + main_irq: intc@7d508400 { + compatible = "brcm,bcm7271-l2-intc"; + reg = <0x7d508400 0x10>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gio: gpio@7d508500 { + compatible = "brcm,brcmstb-gpio"; + reg = <0x7d508500 0x40>; + interrupt-parent = <&main_irq>; + interrupts = <0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + brcm,gpio-bank-widths = <32 22>; + brcm,gpio-direct; + }; + + uarta: serial@7d50c000 { + compatible = "brcm,bcm7271-uart"; + reg = <0x7d50c000 0x20>; + reg-names = "uart"; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + skip-init; + status = "disabled"; + }; + + uartb: serial@7d50d000 { + compatible = "brcm,bcm7271-uart"; + reg = <0x7d50d000 0x20>; + reg-names = "uart"; + reg-shift = <2>; + reg-io-width = <4>; + interrupts = ; + skip-init; + status = "disabled"; + }; + + aon_intr: interrupt-controller@7d510600 { + compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc"; + reg = <0x7d510600 0x30>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + status = "disabled"; + }; + + pinctrl_aon: pinctrl@7d510700 { + compatible = "brcm,bcm2712-aon-pinctrl"; + reg = <0x7d510700 0x20>; + + i2c3_m4_agpio0_pins: i2c3_m4_agpio0_pins { + function = "vc_i2c3"; + pins = "aon_gpio0", "aon_gpio1"; + bias-pull-up; + }; + + bsc_m1_agpio13_pins: bsc_m1_agpio13_pins { + function = "bsc_m1"; + pins = "aon_gpio13", "aon_gpio14"; + bias-pull-up; + }; + + bsc_pmu_sgpio4_pins: bsc_pmu_sgpio4_pins { + function = "avs_pmu_bsc"; + pins = "aon_sgpio4", "aon_sgpio5"; + }; + + bsc_m2_sgpio4_pins: bsc_m2_sgpio4_pins { + function = "bsc_m2"; + pins = "aon_sgpio4", "aon_sgpio5"; + }; + + pwm_aon_agpio1_pins: pwm_aon_agpio1_pins { + function = "aon_pwm"; + pins = "aon_gpio1", "aon_gpio2"; + }; + + pwm_aon_agpio4_pins: pwm_aon_agpio4_pins { + function = "vc_pwm0"; + pins = "aon_gpio4", "aon_gpio5"; + }; + + pwm_aon_agpio7_pins: pwm_aon_agpio7_pins { + function = "aon_pwm"; + pins = "aon_gpio7", "aon_gpio9"; + }; + }; + + intc@7d517000 { + compatible = "brcm,bcm7271-l2-intc"; + reg = <0x7d517000 0x10>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + status = "disabled"; + }; + + bscc: i2c@7d517a00 { + compatible = "brcm,brcmstb-i2c"; + reg = <0x7d517a00 0x58>; + interrupt-parent = <&bsc_aon_irq>; + interrupts = <0>; + clock-frequency = <200000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pwm_aon: pwm@7d517a80 { + compatible = "brcm,bcm7038-pwm"; + reg = <0x7d517a80 0x28>; + #pwm-cells = <3>; + clocks = <&clk_27MHz>; + }; + + main_aon_irq: intc@7d517ac0 { + compatible = "brcm,bcm7271-l2-intc"; + reg = <0x7d517ac0 0x10>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + bsc_aon_irq: intc@7d517b00 { + compatible = "brcm,bcm7271-l2-intc"; + reg = <0x7d517b00 0x10>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gio_aon: gpio@7d517c00 { + compatible = "brcm,brcmstb-gpio"; + reg = <0x7d517c00 0x40>; + interrupt-parent = <&main_aon_irq>; + interrupts = <0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + brcm,gpio-bank-widths = <17 6>; + brcm,gpio-direct; + }; + + avs_monitor: avs-monitor@7d542000 { + compatible = "brcm,bcm2711-avs-monitor", + "syscon", "simple-mfd"; + reg = <0x7d542000 0xf00>; + status = "okay"; + + thermal: thermal { + compatible = "brcm,bcm2711-thermal"; + #thermal-sensor-cells = <0>; + }; + }; + + bsc_pmu: i2c@7d544000 { + compatible = "brcm,brcmstb-i2c"; + reg = <0x7d544000 0x58>; + interrupt-parent = <&bsc_aon_irq>; + interrupts = <1>; + clock-frequency = <200000>; + status = "disabled"; + }; + + hdmi0: hdmi@7ef00700 { + compatible = "brcm,bcm2712-hdmi0"; + reg = <0x7c701400 0x300>, + <0x7c701000 0x200>, + <0x7c701d00 0x300>, + <0x7c702000 0x80>, + <0x7c703800 0x200>, + <0x7c704000 0x800>, + <0x7c700100 0x80>, + <0x7d510800 0x100>, + <0x7c720000 0x100>; + reg-names = "hdmi", + "dvp", + "phy", + "rm", + "packet", + "metadata", + "csc", + "cec", + "hd"; + resets = <&dvp 1>; + interrupt-parent = <&aon_intr>; + interrupts = <1>, <2>, <3>, + <7>, <8>; + interrupt-names = "cec-tx", "cec-rx", "cec-low", + "hpd-connected", "hpd-removed"; + ddc = <&ddc0>; + dmas = <&dma32 10>; + dma-names = "audio-rx"; + status = "disabled"; + }; + + hdmi1: hdmi@7ef05700 { + compatible = "brcm,bcm2712-hdmi1"; + reg = <0x7c706400 0x300>, + <0x7c706000 0x200>, + <0x7c706d00 0x300>, + <0x7c707000 0x80>, + <0x7c708800 0x200>, + <0x7c709000 0x800>, + <0x7c700180 0x80>, + <0x7d511000 0x100>, + <0x7c720000 0x100>; + reg-names = "hdmi", + "dvp", + "phy", + "rm", + "packet", + "metadata", + "csc", + "cec", + "hd"; + ddc = <&ddc1>; + resets = <&dvp 2>; + interrupt-parent = <&aon_intr>; + interrupts = <11>, <12>, <13>, + <14>, <15>; + interrupt-names = "cec-tx", "cec-rx", "cec-low", + "hpd-connected", "hpd-removed"; + dmas = <&dma32 17>; + dma-names = "audio-rx"; + status = "disabled"; + }; + }; + + arm-pmu { + compatible = "arm,cortex-a76-pmu"; + interrupts = , + , + , + ; + interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + /* This only applies to the ARMv7 stub */ + arm,cpu-registers-not-fw-configured; + }; + + cpus: cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "brcm,bcm2836-smp"; // for ARM 32-bit + + /* Source for d/i cache-line-size, cache-sets, cache-size + * https://developer.arm.com/documentation/100798/0401 + * /L1-memory-system/About-the-L1-memory-system?lang=en + */ + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a76"; + reg = <0x000>; + enable-method = "psci"; + d-cache-size = <0x10000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + i-cache-size = <0x10000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + next-level-cache = <&l2_cache_l0>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a76"; + reg = <0x100>; + enable-method = "psci"; + d-cache-size = <0x10000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + i-cache-size = <0x10000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + next-level-cache = <&l2_cache_l1>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a76"; + reg = <0x200>; + enable-method = "psci"; + d-cache-size = <0x10000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + i-cache-size = <0x10000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + next-level-cache = <&l2_cache_l2>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a76"; + reg = <0x300>; + enable-method = "psci"; + d-cache-size = <0x10000>; + d-cache-line-size = <64>; + d-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + i-cache-size = <0x10000>; + i-cache-line-size = <64>; + i-cache-sets = <256>; // 64KiB(size)/64(line-size)=1024ways/4-way set + next-level-cache = <&l2_cache_l3>; + }; + + /* Source for cache-line-size and cache-sets: + * https://developer.arm.com/documentation/100798/0401 + * /L2-memory-system/About-the-L2-memory-system?lang=en + * and for cache-size: + * https://www.raspberrypi.com/documentation/computers + * /processors.html#bcm2712 + */ + l2_cache_l0: l2-cache-l0 { + compatible = "cache"; + cache-size = <0x80000>; + cache-line-size = <128>; + cache-sets = <1024>; // 512KiB(size)/64(line-size)=8192ways/8-way set + cache-level = <2>; + cache-unified; + next-level-cache = <&l3_cache>; + }; + + l2_cache_l1: l2-cache-l1 { + compatible = "cache"; + cache-size = <0x80000>; + cache-line-size = <128>; + cache-sets = <1024>; // 512KiB(size)/64(line-size)=8192ways/8-way set + cache-level = <2>; + cache-unified; + next-level-cache = <&l3_cache>; + }; + + l2_cache_l2: l2-cache-l2 { + compatible = "cache"; + cache-size = <0x80000>; + cache-line-size = <128>; + cache-sets = <1024>; // 512KiB(size)/64(line-size)=8192ways/8-way set + cache-level = <2>; + cache-unified; + next-level-cache = <&l3_cache>; + }; + + l2_cache_l3: l2-cache-l3 { + compatible = "cache"; + cache-size = <0x80000>; + cache-line-size = <128>; + cache-sets = <1024>; // 512KiB(size)/64(line-size)=8192ways/8-way set + cache-level = <2>; + cache-unified; + next-level-cache = <&l3_cache>; + }; + + /* Source for cache-line-size and cache-sets: + * https://developer.arm.com/documentation/100453/0401/L3-cache?lang=en + * Source for cache-size: + * https://www.raspberrypi.com/documentation/computers/processors.html#bcm2712 + */ + l3_cache: l3-cache { + compatible = "cache"; + cache-size = <0x200000>; + cache-line-size = <64>; + cache-sets = <2048>; // 2MiB(size)/64(line-size)=32768ways/16-way set + cache-level = <3>; + }; + }; + + psci { + method = "smc"; + compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; + cpu_on = <0xc4000003>; + cpu_suspend = <0xc4000001>; + cpu_off = <0x84000002>; + }; + + axi: axi { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + + ranges = <0x00 0x00000000 0x00 0x00000000 0x10 0x00000000>, + <0x10 0x00000000 0x10 0x00000000 0x01 0x00000000>, + <0x14 0x00000000 0x14 0x00000000 0x04 0x00000000>, + <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>, + <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>; + + dma-ranges = <0x00 0x00000000 0x00 0x00000000 0x10 0x00000000>, + <0x10 0x00000000 0x10 0x00000000 0x01 0x00000000>, + <0x14 0x00000000 0x14 0x00000000 0x04 0x00000000>, + <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>, + <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>; + + vc4: gpu { + compatible = "brcm,bcm2712-vc6"; + }; + + iommu2: iommu@5100 { + /* IOMMU2 for PISP-BE, HEVC; and (unused) H264 accelerators */ + compatible = "brcm,bcm2712-iommu"; + reg = <0x10 0x5100 0x0 0x80>; + cache = <&iommuc>; + #iommu-cells = <0>; + }; + + iommu4: iommu@5200 { + /* IOMMU4 for HVS, MPL/TXP; and (unused) Unicam, PISP-FE, MiniBVN */ + compatible = "brcm,bcm2712-iommu"; + reg = <0x10 0x5200 0x0 0x80>; + cache = <&iommuc>; + #iommu-cells = <0>; + #interconnect-cells = <0>; + }; + + iommu5: iommu@5280 { + /* IOMMU5 for PCIe2 (RP1); and (unused) BSTM */ + compatible = "brcm,bcm2712-iommu"; + reg = <0x10 0x5280 0x0 0x80>; + cache = <&iommuc>; + #iommu-cells = <0>; + dma-iova-offset = <0x10 0x00000000>; // HACK for RP1 masters over PCIe + }; + + iommuc: iommuc@5b00 { + compatible = "brcm,bcm2712-iommuc"; + reg = <0x10 0x5b00 0x0 0x80>; + }; + + dma32: dma@10000 { + compatible = "brcm,bcm2712-dma"; + reg = <0x10 0x00010000 0 0x600>; + interrupts = , + , + , + , + , + ; + interrupt-names = "dma0", + "dma1", + "dma2", + "dma3", + "dma4", + "dma5"; + #dma-cells = <1>; + brcm,dma-channel-mask = <0x0035>; + }; + + dma40: dma@10600 { + compatible = "brcm,bcm2712-dma"; + reg = <0x10 0x00010600 0 0x600>; + interrupts = + , /* dma4 6 */ + , /* dma4 7 */ + , /* dma4 8 */ + , /* dma4 9 */ + , /* dma4 10 */ + ; /* dma4 11 */ + interrupt-names = "dma6", + "dma7", + "dma8", + "dma9", + "dma10", + "dma11"; + #dma-cells = <1>; + brcm,dma-channel-mask = <0x0fc0>; + }; + + // Single-lane Gen3 PCIe + // Outbound window at 0x14_000000-0x17_ffffff + pcie0: pcie@100000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00100000 0x0 0x9310>; + device_type = "pci"; + max-link-speed = <2>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + /* + * Unused interrupts: + * 208: AER + * 215: NMI + * 216: PME + */ + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 209 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 210 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 211 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 212 + IRQ_TYPE_LEVEL_HIGH>; + resets = <&bcm_reset 5>, <&bcm_reset 42>, <&pcie_rescal>; + reset-names = "swinit", "bridge", "rescal"; + msi-controller; + msi-parent = <&pcie0>; + + ranges = <0x02000000 0x00 0x00000000 + 0x17 0x00000000 + 0x0 0xfffffffc>, + <0x43000000 0x04 0x00000000 + 0x14 0x00000000 + 0x3 0x00000000>; + + dma-ranges = <0x43000000 0x10 0x00000000 + 0x00 0x00000000 + 0x10 0x00000000>; + + status = "disabled"; + }; + + // Single-lane Gen3 PCIe + // Outbound window at 0x18_000000-0x1b_ffffff + pcie1: pcie@110000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00110000 0x0 0x9310>; + device_type = "pci"; + max-link-speed = <2>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + /* + * Unused interrupts: + * 218: AER + * 225: NMI + * 226: PME + */ + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 219 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 220 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 221 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 222 + IRQ_TYPE_LEVEL_HIGH>; + resets = <&bcm_reset 7>, <&bcm_reset 43>, <&pcie_rescal>; + reset-names = "swinit", "bridge", "rescal"; + msi-controller; + msi-parent = <&mip1>; + + ranges = <0x02000000 0x00 0x00000000 + 0x1b 0x00000000 + 0x00 0xfffffffc>, + <0x43000000 0x04 0x00000000 + 0x18 0x00000000 + 0x03 0x00000000>; + + dma-ranges = <0x03000000 0x10 0x00000000 + 0x00 0x00000000 + 0x10 0x00000000>; + + status = "disabled"; + }; + + pcie_rescal: reset-controller@119500 { + compatible = "brcm,bcm7216-pcie-sata-rescal"; + reg = <0x10 0x00119500 0x0 0x10>; + #reset-cells = <0>; + }; + + // Quad-lane Gen3 PCIe + // Outbound window at 0x1c_000000-0x1f_ffffff + pcie2: pcie@120000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00120000 0x0 0x9310>; + device_type = "pci"; + max-link-speed = <2>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + /* + * Unused interrupts: + * 228: AER + * 235: NMI + * 236: PME + */ + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 229 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 230 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 231 + IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 232 + IRQ_TYPE_LEVEL_HIGH>; + resets = <&bcm_reset 32>, <&bcm_reset 44>, <&pcie_rescal>; + reset-names = "swinit", "bridge", "rescal"; + msi-controller; + msi-parent = <&mip0>; + + // ~4GB, 32-bit, not-prefetchable at PCIe 00_00000000 + ranges = <0x02000000 0x00 0x00000000 + 0x1f 0x00000000 + 0x0 0xfffffffc>, + // 12GB, 64-bit, prefetchable at PCIe 04_00000000 + <0x43000000 0x04 0x00000000 + 0x1c 0x00000000 + 0x03 0x00000000>; + + // 64GB system RAM space at PCIe 10_00000000 + dma-ranges = <0x02000000 0x00 0x00000000 + 0x1f 0x00000000 + 0x00 0x00400000>, + <0x43000000 0x10 0x00000000 + 0x00 0x00000000 + 0x10 0x00000000>; + + status = "disabled"; + }; + + mip0: msi-controller@130000 { + compatible = "brcm,bcm2712-mip-intc"; + reg = <0x10 0x00130000 0x0 0xc0>; + msi-controller; + interrupt-controller; + #interrupt-cells = <2>; + brcm,msi-base-spi = <128>; + brcm,msi-num-spis = <64>; + brcm,msi-offset = <0>; + brcm,msi-pci-addr = <0xff 0xfffff000>; + }; + + mip1: msi-controller@131000 { + compatible = "brcm,bcm2712-mip-intc"; + reg = <0x10 0x00131000 0x0 0xc0>; + msi-controller; + interrupt-controller; + #interrupt-cells = <2>; + brcm,msi-base-spi = <247>; + /* Actually 20 total, but the others are + * both sparse and non-consecutive */ + brcm,msi-num-spis = <8>; + brcm,msi-offset = <8>; + brcm,msi-pci-addr = <0xff 0xffffe000>; + }; + + syscon_piarbctl: syscon@400018 { + compatible = "brcm,syscon-piarbctl", "syscon", "simple-mfd"; + reg = <0x10 0x00400018 0x0 0x18>; + }; + + usb: usb@480000 { + compatible = "brcm,bcm2835-usb"; + reg = <0x10 0x00480000 0x0 0x10000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk_usb>; + clock-names = "otg"; + phys = <&usbphy>; + phy-names = "usb2-phy"; + status = "disabled"; + }; + + rpivid: codec@800000 { + compatible = "raspberrypi,rpivid-vid-decoder"; + reg = <0x10 0x00800000 0x0 0x10000>, /* HEVC */ + <0x10 0x00840000 0x0 0x1000>; /* INTC */ + reg-names = "hevc", + "intc"; + + interrupts = ; + + clocks = <&firmware_clocks 11>; + clock-names = "hevc"; + iommus = <&iommu2>; + status = "disabled"; + }; + + sdio1: mmc@fff000 { + compatible = "brcm,bcm2712-sdhci"; + reg = <0x10 0x00fff000 0x0 0x260>, + <0x10 0x00fff400 0x0 0x200>, + <0x10 0x015040b0 0x0 0x4>, // Bus isolation control + <0x10 0x015200f0 0x0 0x24>; // LCPLL control misc0-8 + reg-names = "host", "cfg", "busisol", "lcpll"; + interrupts = ; + clocks = <&clk_emmc2>; + sdhci-caps-mask = <0x0000C000 0x0>; + sdhci-caps = <0x0 0x0>; + mmc-ddr-3_3v; + }; + + sdio2: mmc@1100000 { + compatible = "brcm,bcm2712-sdhci"; + reg = <0x10 0x01100000 0x0 0x260>, + <0x10 0x01100400 0x0 0x200>; + reg-names = "host", "cfg"; + interrupts = ; + clocks = <&clk_emmc2>; + sdhci-caps-mask = <0x0000C000 0x0>; + sdhci-caps = <0x0 0x0>; + supports-cqe; + mmc-ddr-3_3v; + status = "disabled"; + }; + + bcm_reset: reset-controller@1504318 { + compatible = "brcm,brcmstb-reset"; + reg = <0x10 0x01504318 0x0 0x30>; + #reset-cells = <1>; + }; + + v3d: v3d@2000000 { + compatible = "brcm,2712-v3d"; + reg = <0x10 0x02000000 0x0 0x4000>, + <0x10 0x02008000 0x0 0x6000>; + reg-names = "hub", "core0"; + + power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; + resets = <&pm BCM2835_RESET_V3D>; + clocks = <&firmware_clocks 5>; + clocks-names = "v3d"; + interrupts = , + ; + status = "disabled"; + }; + + gicv2: interrupt-controller@7fff9000 { + interrupt-controller; + #interrupt-cells = <3>; + compatible = "arm,gic-400"; + reg = <0x10 0x7fff9000 0x0 0x1000>, + <0x10 0x7fffa000 0x0 0x2000>, + <0x10 0x7fffc000 0x0 0x2000>, + <0x10 0x7fffe000 0x0 0x2000>; + interrupts = ; + }; + + pisp_be: pisp_be@880000 { + compatible = "raspberrypi,pispbe"; + reg = <0x10 0x00880000 0x0 0x4000>; + interrupts = ; + clocks = <&firmware_clocks 7>; + clocks-names = "isp_be"; + status = "okay"; + iommus = <&iommu2>; + }; + }; + + clocks { + /* The oscillator is the root of the clock tree. */ + clk_osc: clk-osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "osc"; + clock-frequency = <54000000>; + }; + + clk_usb: clk-usb { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "otg"; + clock-frequency = <480000000>; + }; + + clk_vpu: clk_vpu { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <750000000>; + clock-output-names = "vpu-clock"; + }; + + clk_uart: clk_uart { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <9216000>; + clock-output-names = "uart-clock"; + }; + + clk_emmc2: clk_emmc2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <200000000>; + clock-output-names = "emmc2-clock"; + }; + }; + + usbphy: phy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; + }; +}; diff --git a/arch/arm/boot/dts/broadcom/rp1.dtsi b/arch/arm/boot/dts/broadcom/rp1.dtsi new file mode 100644 index 0000000000000..fd9fb2dde0f7b --- /dev/null +++ b/arch/arm/boot/dts/broadcom/rp1.dtsi @@ -0,0 +1,1307 @@ +#include +#include +#include + +&rp1_target { + rp1: rp1 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&rp1>; + + // ranges and dma-ranges must be provided by the includer + + rp1_clocks: clocks@18000 { + compatible = "raspberrypi,rp1-clocks"; + #clock-cells = <1>; + reg = <0xc0 0x40018000 0x0 0x10038>; + clocks = <&clk_xosc>; + + assigned-clocks = <&rp1_clocks RP1_PLL_SYS_CORE>, + <&rp1_clocks RP1_PLL_AUDIO_CORE>, + // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers + <&rp1_clocks RP1_PLL_SYS>, + <&rp1_clocks RP1_PLL_SYS_SEC>, + <&rp1_clocks RP1_PLL_AUDIO>, + <&rp1_clocks RP1_PLL_AUDIO_SEC>, + <&rp1_clocks RP1_CLK_SYS>, + <&rp1_clocks RP1_PLL_SYS_PRI_PH>, + // RP1_CLK_SLOW_SYS is used for the frequency counter (FC0) + <&rp1_clocks RP1_CLK_SLOW_SYS>, + <&rp1_clocks RP1_CLK_SDIO_TIMER>, + <&rp1_clocks RP1_CLK_SDIO_ALT_SRC>, + <&rp1_clocks RP1_CLK_ETH_TSU>; + + assigned-clock-rates = <1000000000>, // RP1_PLL_SYS_CORE + <1536000000>, // RP1_PLL_AUDIO_CORE + <200000000>, // RP1_PLL_SYS + <125000000>, // RP1_PLL_SYS_SEC + <61440000>, // RP1_PLL_AUDIO + <192000000>, // RP1_PLL_AUDIO_SEC + <200000000>, // RP1_CLK_SYS + <100000000>, // RP1_PLL_SYS_PRI_PH + // Must match the XOSC frequency + <50000000>, // RP1_CLK_SLOW_SYS + <1000000>, // RP1_CLK_SDIO_TIMER + <200000000>, // RP1_CLK_SDIO_ALT_SRC + <50000000>; // RP1_CLK_ETH_TSU + }; + + rp1_uart0: serial@30000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x40030000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + dmas = <&rp1_dma RP1_DMA_UART0_TX>, + <&rp1_dma RP1_DMA_UART0_RX>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_uart1: serial@34000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x40034000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + // dmas = <&rp1_dma RP1_DMA_UART1_TX>, + // <&rp1_dma RP1_DMA_UART1_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_uart2: serial@38000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x40038000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + // dmas = <&rp1_dma RP1_DMA_UART2_TX>, + // <&rp1_dma RP1_DMA_UART2_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_uart3: serial@3c000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x4003c000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + // dmas = <&rp1_dma RP1_DMA_UART3_TX>, + // <&rp1_dma RP1_DMA_UART3_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_uart4: serial@40000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x40040000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + // dmas = <&rp1_dma RP1_DMA_UART4_TX>, + // <&rp1_dma RP1_DMA_UART4_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_uart5: serial@44000 { + compatible = "arm,pl011-axi"; + reg = <0xc0 0x40044000 0x0 0x100>; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; + // dmas = <&rp1_dma RP1_DMA_UART5_TX>, + // <&rp1_dma RP1_DMA_UART5_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; + arm,primecell-periphid = <0x00541011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; + status = "disabled"; + }; + + rp1_spi8: spi@4c000 { + reg = <0xc0 0x4004c000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI8_TX>, + <&rp1_dma RP1_DMA_SPI8_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_spi0: spi@50000 { + reg = <0xc0 0x40050000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI0_TX>, + <&rp1_dma RP1_DMA_SPI0_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_spi1: spi@54000 { + reg = <0xc0 0x40054000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI1_TX>, + <&rp1_dma RP1_DMA_SPI1_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_spi2: spi@58000 { + reg = <0xc0 0x40058000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI2_TX>, + <&rp1_dma RP1_DMA_SPI2_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_spi3: spi@5c000 { + reg = <0xc0 0x4005c000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI3_TX>, + <&rp1_dma RP1_DMA_SPI3_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + // SPI4 is a target/slave interface + rp1_spi4: spi@60000 { + reg = <0xc0 0x40060000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <0>; + #size-cells = <0>; + num-cs = <1>; + spi-slave; + dmas = <&rp1_dma RP1_DMA_SPI4_TX>, + <&rp1_dma RP1_DMA_SPI4_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + + slave { + compatible = "spidev"; + spi-max-frequency = <1000000>; + }; + }; + + rp1_spi5: spi@64000 { + reg = <0xc0 0x40064000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI5_TX>, + <&rp1_dma RP1_DMA_SPI5_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_spi6: spi@68000 { + reg = <0xc0 0x40068000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <2>; + dmas = <&rp1_dma RP1_DMA_SPI6_TX>, + <&rp1_dma RP1_DMA_SPI6_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + // SPI7 is a target/slave interface + rp1_spi7: spi@6c000 { + reg = <0xc0 0x4006c000 0x0 0x130>; + compatible = "snps,dw-apb-ssi"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + clock-names = "ssi_clk"; + #address-cells = <0>; + #size-cells = <0>; + num-cs = <1>; + spi-slave; + dmas = <&rp1_dma RP1_DMA_SPI7_TX>, + <&rp1_dma RP1_DMA_SPI7_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + + slave { + compatible = "spidev"; + spi-max-frequency = <1000000>; + }; + }; + + rp1_i2c0: i2c@70000 { + reg = <0xc0 0x40070000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c1: i2c@74000 { + reg = <0xc0 0x40074000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c2: i2c@78000 { + reg = <0xc0 0x40078000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c3: i2c@7c000 { + reg = <0xc0 0x4007c000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c4: i2c@80000 { + reg = <0xc0 0x40080000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c5: i2c@84000 { + reg = <0xc0 0x40084000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_i2c6: i2c@88000 { + reg = <0xc0 0x40088000 0x0 0x1000>; + compatible = "snps,designware-i2c"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + status = "disabled"; + }; + + rp1_pwm0: pwm@98000 { + compatible = "raspberrypi,rp1-pwm"; + reg = <0xc0 0x40098000 0x0 0x100>; + #pwm-cells = <3>; + clocks = <&rp1_clocks RP1_CLK_PWM0>; + assigned-clocks = <&rp1_clocks RP1_CLK_PWM0>; + assigned-clock-rates = <50000000>; + status = "disabled"; + }; + + rp1_pwm1: pwm@9c000 { + compatible = "raspberrypi,rp1-pwm"; + reg = <0xc0 0x4009c000 0x0 0x100>; + #pwm-cells = <3>; + clocks = <&rp1_clocks RP1_CLK_PWM1>; + assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>; + assigned-clock-rates = <50000000>; + status = "disabled"; + }; + + rp1_i2s0: i2s@a0000 { + reg = <0xc0 0x400a0000 0x0 0x1000>; + compatible = "snps,designware-i2s"; + // Providing an interrupt disables DMA + // interrupts = ; + clocks = <&rp1_clocks RP1_CLK_I2S>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + dmas = <&rp1_dma RP1_DMA_I2S0_TX>,<&rp1_dma RP1_DMA_I2S0_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_i2s1: i2s@a4000 { + reg = <0xc0 0x400a4000 0x0 0x1000>; + compatible = "snps,designware-i2s"; + // Providing an interrupt disables DMA + // interrupts = ; + clocks = <&rp1_clocks RP1_CLK_I2S>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + dmas = <&rp1_dma RP1_DMA_I2S1_TX>,<&rp1_dma RP1_DMA_I2S1_RX>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + rp1_i2s2: i2s@a8000 { + reg = <0xc0 0x400a8000 0x0 0x1000>; + compatible = "snps,designware-i2s"; + // Providing an interrupt disables DMA + // interrupts = ; + clocks = <&rp1_clocks RP1_CLK_I2S>; + status = "disabled"; + }; + + rp1_sdio_clk0: sdio_clk0@b0004 { + compatible = "raspberrypi,rp1-sdio-clk"; + reg = <0xc0 0x400b0004 0x0 0x1c>; + clocks = <&sdio_src &sdhci_core>; + clock-names = "src", "base"; + #clock-cells = <0>; + status = "disabled"; + }; + + rp1_sdio_clk1: sdio_clk1@b4004 { + compatible = "raspberrypi,rp1-sdio-clk"; + reg = <0xc0 0x400b4004 0x0 0x1c>; + clocks = <&sdio_src &sdhci_core>; + clock-names = "src", "base"; + #clock-cells = <0>; + status = "disabled"; + }; + + rp1_adc: adc@c8000 { + compatible = "raspberrypi,rp1-adc"; + reg = <0xc0 0x400c8000 0x0 0x4000>; + clocks = <&rp1_clocks RP1_CLK_ADC>; + clock-names = "adcclk"; + #clock-cells = <0>; + vref-supply = <&rp1_vdd_3v3>; + status = "disabled"; + }; + + rp1_gpio: gpio@d0000 { + reg = <0xc0 0x400d0000 0x0 0xc000>, + <0xc0 0x400e0000 0x0 0xc000>, + <0xc0 0x400f0000 0x0 0xc000>; + compatible = "raspberrypi,rp1-gpio"; + interrupts = , + , + ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&rp1_gpio 0 0 54>; + + rp1_uart0_14_15: rp1_uart0_14_15 { + pin_txd { + function = "uart0"; + pins = "gpio14"; + bias-disable; + }; + pin_rxd { + function = "uart0"; + pins = "gpio15"; + bias-pull-up; + }; + }; + rp1_uart0_ctsrts_16_17: rp1_uart0_ctsrts_16_17 { + pin_cts { + function = "uart0"; + pins = "gpio16"; + bias-pull-up; + }; + pin_rts { + function = "uart0"; + pins = "gpio17"; + bias-disable; + }; + }; + rp1_uart1_0_1: rp1_uart1_0_1 { + pin_txd { + function = "uart1"; + pins = "gpio0"; + bias-disable; + }; + pin_rxd { + function = "uart1"; + pins = "gpio1"; + bias-pull-up; + }; + }; + rp1_uart1_ctsrts_2_3: rp1_uart1_ctsrts_2_3 { + pin_cts { + function = "uart1"; + pins = "gpio2"; + bias-pull-up; + }; + pin_rts { + function = "uart1"; + pins = "gpio3"; + bias-disable; + }; + }; + rp1_uart2_4_5: rp1_uart2_4_5 { + pin_txd { + function = "uart2"; + pins = "gpio4"; + bias-disable; + }; + pin_rxd { + function = "uart2"; + pins = "gpio5"; + bias-pull-up; + }; + }; + rp1_uart2_ctsrts_6_7: rp1_uart2_ctsrts_6_7 { + pin_cts { + function = "uart2"; + pins = "gpio6"; + bias-pull-up; + }; + pin_rts { + function = "uart2"; + pins = "gpio7"; + bias-disable; + }; + }; + rp1_uart3_8_9: rp1_uart3_8_9 { + pin_txd { + function = "uart3"; + pins = "gpio8"; + bias-disable; + }; + pin_rxd { + function = "uart3"; + pins = "gpio9"; + bias-pull-up; + }; + }; + rp1_uart3_ctsrts_10_11: rp1_uart3_ctsrts_10_11 { + pin_cts { + function = "uart3"; + pins = "gpio10"; + bias-pull-up; + }; + pin_rts { + function = "uart3"; + pins = "gpio11"; + bias-disable; + }; + }; + rp1_uart4_12_13: rp1_uart4_12_13 { + pin_txd { + function = "uart4"; + pins = "gpio12"; + bias-disable; + }; + pin_rxd { + function = "uart4"; + pins = "gpio13"; + bias-pull-up; + }; + }; + rp1_uart4_ctsrts_14_15: rp1_uart4_ctsrts_14_15 { + pin_cts { + function = "uart4"; + pins = "gpio14"; + bias-pull-up; + }; + pin_rts { + function = "uart4"; + pins = "gpio15"; + bias-disable; + }; + }; + + rp1_sdio0_22_27: rp1_sdio0_22_27 { + pin_clk { + function = "sd0"; + pins = "gpio22"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + pin_cmd { + function = "sd0"; + pins = "gpio23"; + bias-pull-up; + drive-strength = <12>; + slew-rate = <1>; + }; + pins_dat { + function = "sd0"; + pins = "gpio24", "gpio25", "gpio26", "gpio27"; + bias-pull-up; + drive-strength = <12>; + slew-rate = <1>; + }; + }; + + rp1_sdio1_28_33: rp1_sdio1_28_33 { + pin_clk { + function = "sd1"; + pins = "gpio28"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + pin_cmd { + function = "sd1"; + pins = "gpio29"; + bias-pull-up; + drive-strength = <12>; + slew-rate = <1>; + }; + pins_dat { + function = "sd1"; + pins = "gpio30", "gpio31", "gpio32", "gpio33"; + bias-pull-up; + drive-strength = <12>; + slew-rate = <1>; + }; + }; + + rp1_i2s0_18_21: rp1_i2s0_18_21 { + function = "i2s0"; + pins = "gpio18", "gpio19", "gpio20", "gpio21"; + bias-disable; + }; + + rp1_i2s1_18_21: rp1_i2s1_18_21 { + function = "i2s1"; + pins = "gpio18", "gpio19", "gpio20", "gpio21"; + bias-disable; + }; + + rp1_i2c4_34_35: rp1_i2c4_34_35 { + function = "i2c4"; + pins = "gpio34", "gpio35"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c6_38_39: rp1_i2c6_38_39 { + function = "i2c6"; + pins = "gpio38", "gpio39"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c4_40_41: rp1_i2c4_40_41 { + function = "i2c4"; + pins = "gpio40", "gpio41"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c5_44_45: rp1_i2c5_44_45 { + function = "i2c5"; + pins = "gpio44", "gpio45"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c0_0_1: rp1_i2c0_0_1 { + function = "i2c0"; + pins = "gpio0", "gpio1"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c0_8_9: rp1_i2c0_8_9 { + function = "i2c0"; + pins = "gpio8", "gpio9"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c1_2_3: rp1_i2c1_2_3 { + function = "i2c1"; + pins = "gpio2", "gpio3"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c1_10_11: rp1_i2c1_10_11 { + function = "i2c1"; + pins = "gpio10", "gpio11"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c2_4_5: rp1_i2c2_4_5 { + function = "i2c2"; + pins = "gpio4", "gpio5"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c2_12_13: rp1_i2c2_12_13 { + function = "i2c2"; + pins = "gpio12", "gpio13"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c3_6_7: rp1_i2c3_6_7 { + function = "i2c3"; + pins = "gpio6", "gpio7"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c3_14_15: rp1_i2c3_14_15 { + function = "i2c3"; + pins = "gpio14", "gpio15"; + drive-strength = <12>; + bias-pull-up; + }; + rp1_i2c3_22_23: rp1_i2c3_22_23 { + function = "i2c3"; + pins = "gpio22", "gpio23"; + drive-strength = <12>; + bias-pull-up; + }; + + // DPI mappings with HSYNC,VSYNC but without PIXCLK,DE + rp1_dpi_16bit_gpio2: rp1_dpi_16bit_gpio2 { /* Mode 2, not fully supported by RP1 */ + function = "dpi"; + pins = "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19"; + bias-disable; + }; + rp1_dpi_16bit_cpadhi_gpio2: rp1_dpi_16bit_cpadhi_gpio2 { /* Mode 3 */ + function = "dpi"; + pins = "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24"; + bias-disable; + }; + rp1_dpi_16bit_pad666_gpio2: rp1_dpi_16bit_pad666_gpio2 { /* Mode 4 */ + function = "dpi"; + pins = "gpio2", "gpio3", + "gpio5", "gpio6", "gpio7", "gpio8", + "gpio9", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio21", "gpio22", "gpio23", "gpio24", + "gpio25"; + bias-disable; + }; + rp1_dpi_18bit_gpio2: rp1_dpi_18bit_gpio2 { /* Mode 5, not fully supported by RP1 */ + function = "dpi"; + pins = "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21"; + bias-disable; + }; + rp1_dpi_18bit_cpadhi_gpio2: rp1_dpi_18bit_cpadhi_gpio2 { /* Mode 6 */ + function = "dpi"; + pins = "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25"; + bias-disable; + }; + rp1_dpi_24bit_gpio2: rp1_dpi_24bit_gpio2 { /* Mode 7 */ + function = "dpi"; + pins = "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", + "gpio26", "gpio27"; + bias-disable; + }; + rp1_dpi_hvsync: rp1_dpi_hvsync { /* Sync only, for use with int VDAC */ + function = "dpi"; + pins = "gpio2", "gpio3"; + bias-disable; + }; + + // More DPI mappings, including PIXCLK,DE on GPIOs 0,1 + rp1_dpi_16bit_gpio0: rp1_dpi_16bit_gpio0 { /* Mode 2, not fully supported by RP1 */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", "gpio18", "gpio19"; + bias-disable; + }; + rp1_dpi_16bit_cpadhi_gpio0: rp1_dpi_16bit_cpadhi_gpio0 { /* Mode 3 */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24"; + bias-disable; + }; + rp1_dpi_16bit_pad666_gpio0: rp1_dpi_16bit_pad666_gpio0 { /* Mode 4 */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio5", "gpio6", "gpio7", "gpio8", + "gpio9", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio21", "gpio22", "gpio23", "gpio24", + "gpio25"; + bias-disable; + }; + rp1_dpi_18bit_gpio0: rp1_dpi_18bit_gpio0 { /* Mode 5, not fully supported by RP1 */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", "gpio18", "gpio19", + "gpio20", "gpio21"; + bias-disable; + }; + rp1_dpi_18bit_cpadhi_gpio0: rp1_dpi_18bit_cpadhi_gpio0 { /* Mode 6 */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25"; + bias-disable; + }; + rp1_dpi_24bit_gpio0: rp1_dpi_24bit_gpio0 { /* Mode 7 -- All GPIOs used! */ + function = "dpi"; + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27"; + bias-disable; + }; + + rp1_gpclksrc0_gpio4: rp1_gpclksrc0_gpio4 { + function = "gpclk0"; + pins = "gpio4"; + bias-disable; + }; + + rp1_gpclksrc0_gpio20: rp1_gpclksrc0_gpio20 { + function = "gpclk0"; + pins = "gpio20"; + bias-disable; + }; + + rp1_gpclksrc1_gpio5: rp1_gpclksrc1_gpio5 { + function = "gpclk1"; + pins = "gpio5"; + bias-disable; + }; + + rp1_gpclksrc1_gpio18: rp1_gpclksrc1_gpio18 { + function = "gpclk1"; + pins = "gpio18"; + bias-disable; + }; + + rp1_gpclksrc1_gpio21: rp1_gpclksrc1_gpio21 { + function = "gpclk1"; + pins = "gpio21"; + bias-disable; + }; + + rp1_pwm1_gpio45: rp1_pwm1_gpio45 { + function = "pwm1"; + pins = "gpio45"; + bias-pull-down; + }; + + rp1_spi0_gpio9: rp1_spi0_gpio9 { + function = "spi0"; + pins = "gpio9", "gpio10", "gpio11"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi0_cs_gpio7: rp1_spi0_cs_gpio7 { + function = "spi0"; + pins = "gpio7", "gpio8"; + bias-pull-up; + }; + + rp1_spi1_gpio19: rp1_spi1_gpio19 { + function = "spi1"; + pins = "gpio19", "gpio20", "gpio21"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi2_gpio1: rp1_spi2_gpio1 { + function = "spi2"; + pins = "gpio1", "gpio2", "gpio3"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi3_gpio5: rp1_spi3_gpio5 { + function = "spi3"; + pins = "gpio5", "gpio6", "gpio7"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi4_gpio9: rp1_spi4_gpio9 { + function = "spi4"; + pins = "gpio9", "gpio10", "gpio11"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi5_gpio13: rp1_spi5_gpio13 { + function = "spi5"; + pins = "gpio13", "gpio14", "gpio15"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi8_gpio49: rp1_spi8_gpio49 { + function = "spi8"; + pins = "gpio49", "gpio50", "gpio51"; + bias-disable; + drive-strength = <12>; + slew-rate = <1>; + }; + + rp1_spi8_cs_gpio52: rp1_spi8_cs_gpio52 { + function = "spi0"; + pins = "gpio52", "gpio53"; + bias-pull-up; + }; + }; + + rp1_eth: ethernet@100000 { + reg = <0xc0 0x40100000 0x0 0x4000>; + compatible = "cdns,macb"; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + clocks = <&macb_pclk &macb_hclk &rp1_clocks RP1_CLK_ETH_TSU>; + clock-names = "pclk", "hclk", "tsu_clk"; + phy-mode = "rgmii-id"; + cdns,aw2w-max-pipe = /bits/ 8 <8>; + cdns,ar2r-max-pipe = /bits/ 8 <8>; + cdns,use-aw2b-fill; + local-mac-address = [00 00 00 00 00 00]; + status = "disabled"; + }; + + rp1_csi0: csi@110000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0xc0 0x40110000 0x0 0x100>, // CSI2 DMA address + <0xc0 0x40114000 0x0 0x100>, // PHY/CSI Host address + <0xc0 0x40120000 0x0 0x100>, // MIPI CFG address + <0xc0 0x40124000 0x0 0x1000>; // PiSP FE address + + // interrupts must match rp1_pisp_fe setup + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; + assigned-clock-rates = <25000000>; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + rp1_csi1: csi@128000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0xc0 0x40128000 0x0 0x100>, // CSI2 DMA address + <0xc0 0x4012c000 0x0 0x100>, // PHY/CSI Host address + <0xc0 0x40138000 0x0 0x100>, // MIPI CFG address + <0xc0 0x4013c000 0x0 0x1000>; // PiSP FE address + + // interrupts must match rp1_pisp_fe setup + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; + assigned-clock-rates = <25000000>; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + rp1_mmc0: mmc@180000 { + reg = <0xc0 0x40180000 0x0 0x100>; + compatible = "raspberrypi,rp1-dwcmshc"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core + &rp1_clocks RP1_CLK_SDIO_TIMER + &rp1_sdio_clk0>; + clock-names = "bus", "core", "timeout", "sdio"; + /* Bank 0 VDDIO is fixed */ + no-1-8-v; + bus-width = <4>; + vmmc-supply = <&rp1_vdd_3v3>; + broken-cd; + status = "disabled"; + }; + + rp1_mmc1: mmc@184000 { + reg = <0xc0 0x40184000 0x0 0x100>; + compatible = "raspberrypi,rp1-dwcmshc"; + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core + &rp1_clocks RP1_CLK_SDIO_TIMER + &rp1_sdio_clk1>; + clock-names = "bus", "core", "timeout", "sdio"; + bus-width = <4>; + vmmc-supply = <&rp1_vdd_3v3>; + /* Nerf SDR speeds */ + sdhci-caps-mask = <0x3 0x0>; + broken-cd; + status = "disabled"; + }; + + rp1_dma: dma@188000 { + reg = <0xc0 0x40188000 0x0 0x1000>; + compatible = "snps,axi-dma-1.01a"; + interrupts = ; + clocks = <&sdhci_core &rp1_clocks RP1_CLK_SYS>; + clock-names = "core-clk", "cfgr-clk"; + + #dma-cells = <1>; + dma-channels = <8>; + snps,dma-masters = <1>; + snps,dma-targets = <64>; + snps,data-width = <4>; // (8 << 4) == 128 bits + snps,block-size = <0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000>; + snps,priority = <0 1 2 3 4 5 6 7>; + snps,axi-max-burst-len = <8>; + status = "disabled"; + }; + + rp1_usb0: usb@200000 { + reg = <0xc0 0x40200000 0x0 0x100000>; + compatible = "snps,dwc3"; + dr_mode = "host"; + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; + snps,tx-max-burst = /bits/ 8 <8>; + snps,tx-thr-num-pkt = /bits/ 8 <2>; + interrupts = ; + status = "disabled"; + }; + + rp1_usb1: usb@300000 { + reg = <0xc0 0x40300000 0x0 0x100000>; + compatible = "snps,dwc3"; + dr_mode = "host"; + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; + snps,tx-max-burst = /bits/ 8 <8>; + snps,tx-thr-num-pkt = /bits/ 8 <2>; + interrupts = ; + status = "disabled"; + }; + + rp1_dsi0: dsi@110000 { + compatible = "raspberrypi,rp1dsi"; + status = "disabled"; + reg = <0xc0 0x40118000 0x0 0x1000>, // MIPI0 DSI DMA (ArgonDPI) + <0xc0 0x4011c000 0x0 0x1000>, // MIPI0 DSI Host (SNPS) + <0xc0 0x40120000 0x0 0x1000>; // MIPI0 CFG + + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>, // required, config bus clock + <&rp1_clocks RP1_CLK_MIPI0_DPI>, // required, pixel clock + <&clksrc_mipi0_dsi_byteclk>, // internal, parent for divide + <&clk_xosc>; // hardwired to DSI "refclk" + clock-names = "cfgclk", "dpiclk", "byteclk", "refclk"; + + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>, + <&rp1_clocks RP1_CLK_MIPI0_DPI>; + assigned-clock-rates = <25000000>; + assigned-clock-parents = <0>, <&clksrc_mipi0_dsi_byteclk>; + }; + + rp1_dsi1: dsi@128000 { + compatible = "raspberrypi,rp1dsi"; + status = "disabled"; + reg = <0xc0 0x40130000 0x0 0x1000>, // MIPI1 DSI DMA (ArgonDPI) + <0xc0 0x40134000 0x0 0x1000>, // MIPI1 DSI Host (SNPS) + <0xc0 0x40138000 0x0 0x1000>; // MIPI1 CFG + + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>, // required, config bus clock + <&rp1_clocks RP1_CLK_MIPI1_DPI>, // required, pixel clock + <&clksrc_mipi1_dsi_byteclk>, // internal, parent for divide + <&clk_xosc>; // hardwired to DSI "refclk" + clock-names = "cfgclk", "dpiclk", "byteclk", "refclk"; + + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>, + <&rp1_clocks RP1_CLK_MIPI1_DPI>; + assigned-clock-rates = <25000000>; + assigned-clock-parents = <0>, <&clksrc_mipi1_dsi_byteclk>; + }; + + /* VEC and DPI both need to control PLL_VIDEO and cannot work together; */ + /* config.txt should enable one or other using dtparam=vec or an overlay. */ + rp1_vec: vec@144000 { + compatible = "raspberrypi,rp1vec"; + status = "disabled"; + reg = <0xc0 0x40144000 0x0 0x1000>, // VIDEO_OUT_VEC + <0xc0 0x40140000 0x0 0x1000>; // VIDEO_OUT_CFG + + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_VEC>; + + assigned-clocks = <&rp1_clocks RP1_PLL_VIDEO_CORE>, + <&rp1_clocks RP1_PLL_VIDEO_SEC>, + <&rp1_clocks RP1_CLK_VEC>; + assigned-clock-rates = <1188000000>, + <108000000>, + <108000000>; + assigned-clock-parents = <0>, + <&rp1_clocks RP1_PLL_VIDEO_CORE>, + <&rp1_clocks RP1_PLL_VIDEO_SEC>; + }; + + rp1_dpi: dpi@148000 { + compatible = "raspberrypi,rp1dpi"; + status = "disabled"; + reg = <0xc0 0x40148000 0x0 0x1000>, // VIDEO_OUT DPI + <0xc0 0x40140000 0x0 0x1000>; // VIDEO_OUT_CFG + + interrupts = ; + + clocks = <&rp1_clocks RP1_CLK_DPI>, // DPI pixel clock + <&rp1_clocks RP1_PLL_VIDEO>, // PLL primary divider, and + <&rp1_clocks RP1_PLL_VIDEO_CORE>; // VCO, which we also control + clock-names = "dpiclk", "plldiv", "pllcore"; + + assigned-clocks = <&rp1_clocks RP1_CLK_DPI>; + assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>; + }; + }; +}; + +&clocks { + clk_xosc: clk_xosc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "xosc"; + clock-frequency = <50000000>; + }; + macb_pclk: macb_pclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "pclk"; + clock-frequency = <200000000>; + }; + macb_hclk: macb_hclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "hclk"; + clock-frequency = <200000000>; + }; + sdio_src: sdio_src { + // 400 MHz on FPGA. PLL sys VCO on asic + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "src"; + clock-frequency = <1000000000>; + }; + sdhci_core: sdhci_core { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "core"; + clock-frequency = <50000000>; + }; + clksrc_mipi0_dsi_byteclk: clksrc_mipi0_dsi_byteclk { + // This clock is synthesized by MIPI0 D-PHY, when DSI is running. + // Its frequency is not known a priori (until a panel driver attaches) + // so assign a made-up frequency of 72MHz so it can be divided for DPI. + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "clksrc_mipi0_dsi_byteclk"; + clock-frequency = <72000000>; + }; + clksrc_mipi1_dsi_byteclk: clksrc_mipi1_dsi_byteclk { + // This clock is synthesized by MIPI1 D-PHY, when DSI is running. + // Its frequency is not known a priori (until a panel driver attaches) + // so assign a made-up frequency of 72MHz so it can be divided for DPI. + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "clksrc_mipi1_dsi_byteclk"; + clock-frequency = <72000000>; + }; + /* GPIO derived clock sources. Each GPIO with a GPCLK function + * can drive its output from the respective GPCLK + * generator, and provide a clock source to other internal + * dividers. Add dummy sources here so that they can be overridden + * with overlays. + */ + clksrc_gp0: clksrc_gp0 { + status = "disabled"; + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + clocks = <&rp1_clocks RP1_CLK_GP0>; + clock-output-names = "clksrc_gp0"; + }; + clksrc_gp1: clksrc_gp1 { + status = "disabled"; + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + clocks = <&rp1_clocks RP1_CLK_GP1>; + clock-output-names = "clksrc_gp1"; + }; + clksrc_gp2: clksrc_gp2 { + status = "disabled"; + compatible = "fixed-factor-clock"; + clock-div = <1>; + clock-mult = <1>; + #clock-cells = <0>; + clocks = <&rp1_clocks RP1_CLK_GP2>; + clock-output-names = "clksrc_gp2"; + }; + clksrc_gp3: clksrc_gp3 { + status = "disabled"; + compatible = "fixed-factor-clock"; + clock-div = <1>; + clock-mult = <1>; + #clock-cells = <0>; + clocks = <&rp1_clocks RP1_CLK_GP3>; + clock-output-names = "clksrc_gp3"; + }; + clksrc_gp4: clksrc_gp4 { + status = "disabled"; + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + clocks = <&rp1_clocks RP1_CLK_GP4>; + clock-output-names = "clksrc_gp4"; + }; + clksrc_gp5: clksrc_gp5 { + status = "disabled"; + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + clocks = <&rp1_clocks RP1_CLK_GP5>; + clock-output-names = "clksrc_gp5"; + }; +}; + +/ { + rp1_vdd_3v3: rp1_vdd_3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/Makefile b/arch/arm64/boot/dts/broadcom/Makefile index b06d505e6a557..d11066547d9eb 100644 --- a/arch/arm64/boot/dts/broadcom/Makefile +++ b/arch/arm64/boot/dts/broadcom/Makefile @@ -1,4 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 + +# Enables support for device-tree overlays +DTC_FLAGS := -@ + dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-400.dtb \ bcm2711-rpi-4-b.dtb \ raspberrypi-uno-220.dtb \ @@ -9,6 +13,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-400.dtb \ bcm2837-rpi-cm3-io3.dtb \ bcm2837-rpi-zero-2-w.dtb +dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-5-b.dtb + subdir-y += bcmbca subdir-y += northstar2 subdir-y += stingray diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts new file mode 100644 index 0000000000000..1457e696f9687 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "arm/broadcom/bcm2712-rpi-5-b.dts" diff --git a/arch/arm64/configs/eve_defconfig b/arch/arm64/configs/eve_defconfig index 172c6cd217051..c29c5759f743a 100644 --- a/arch/arm64/configs/eve_defconfig +++ b/arch/arm64/configs/eve_defconfig @@ -555,6 +555,9 @@ CONFIG_HNS3=y CONFIG_HNS3_HCLGE=y CONFIG_HNS3_ENET=y CONFIG_NET_VENDOR_MEDIATEK=y +CONFIG_NET_VENDOR_CADENCE=y +CONFIG_MACB=y +CONFIG_MACB_USE_HWSTAMP=y CONFIG_MLX5_CORE=m CONFIG_MLX5_FPGA=y CONFIG_MLX5_CORE_EN=y @@ -572,6 +575,7 @@ CONFIG_NXP_TJA11XX_PHY=m CONFIG_AT803X_PHY=m CONFIG_ROCKCHIP_PHY=m CONFIG_DP83867_PHY=m +CONFIG_BROADCOM_PHY=y CONFIG_CAN_VCAN=y CONFIG_CAN_CAN327=m CONFIG_CAN_FLEXCAN=m @@ -731,6 +735,9 @@ CONFIG_PINCTRL_IMX8QM=y CONFIG_PINCTRL_IMX8QXP=y CONFIG_PINCTRL_IMX8DXL=y CONFIG_PINCTRL_SPRD_SC9860=y +CONFIG_PINCTRL_BCM2712=y +CONFIG_PINCTRL_RP1=y +CONFIG_BCM2712_MIP=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_DWAPB=y CONFIG_GPIO_FTGPIO010=y @@ -778,6 +785,10 @@ CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m CONFIG_BCM_NS_THERMAL=m CONFIG_BCM_SR_THERMAL=m +CONFIG_BCM2835_POWER=y +CONFIG_RASPBERRYPI_POWER=y +CONFIG_RESET_BRCMSTB=y +CONFIG_RESET_BRCMSTB_RESCAL=y CONFIG_TEGRA_BPMP_THERMAL=m CONFIG_WATCHDOG=y CONFIG_WATCHDOG_PRETIMEOUT_GOV=y @@ -803,6 +814,7 @@ CONFIG_MFD_SUN6I_PRCM=y CONFIG_MFD_LOCHNAGAR=y CONFIG_MFD_WM8994=y CONFIG_MFD_ROHM_BD718XX=y +CONFIG_MFD_RP1=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_AXP20X=y @@ -930,6 +942,7 @@ CONFIG_MMC_SDHCI_OF_ARASAN=y CONFIG_MMC_SDHCI_ESDHC_IMX=y CONFIG_MMC_SDHCI_TEGRA=y CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SDHCI_BRCMSTB=y CONFIG_MMC_DW=y CONFIG_MMC_DW_ROCKCHIP=y CONFIG_MMC_BCM2835=y @@ -1020,6 +1033,7 @@ CONFIG_CROS_EC_SPI=y CONFIG_MELLANOX_PLATFORM=y CONFIG_CLK_SP810=y CONFIG_COMMON_CLK_RK808=m +CONFIG_COMMON_CLK_RP1=y CONFIG_COMMON_CLK_SCPI=y CONFIG_COMMON_CLK_CS2000_CP=y CONFIG_COMMON_CLK_S2MPS11=y @@ -1070,6 +1084,7 @@ CONFIG_ARM_SMMU_V3=y CONFIG_MTK_IOMMU=y CONFIG_QCOM_IOMMU=y CONFIG_VIRTIO_IOMMU=y +CONFIG_BCM2712_IOMMU=y CONFIG_REMOTEPROC=y CONFIG_IMX_REMOTEPROC=y CONFIG_RPMSG_CHAR=m @@ -1130,6 +1145,7 @@ CONFIG_PHY_EXYNOS_PCIE=y CONFIG_PHY_SAMSUNG_UFS=y CONFIG_PHY_SAMSUNG_USB2=m CONFIG_PHY_TEGRA_XUSB=y +CONFIG_PHY_BRCM_USB=y CONFIG_ARM_CCI_PMU=y CONFIG_ARM_CCN=y CONFIG_ARM_SMMU_V3_PMU=m diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 5da82f2bdd211..a2490a26d1713 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -89,6 +89,13 @@ config COMMON_CLK_RK808 These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each. Clkout1 is always on, Clkout2 can off by control register. +config COMMON_CLK_RP1 + tristate "Raspberry Pi RP1-based clock support" + depends on PCI || COMPILE_TEST + depends on COMMON_CLK + help + Enable common clock framework support for Raspberry Pi RP1 + config COMMON_CLK_HI655X tristate "Clock driver for Hi655x" if EXPERT depends on (MFD_HI655X_PMIC || COMPILE_TEST) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e3ca0d058a256..cda4b7a0befbf 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o +obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c new file mode 100644 index 0000000000000..b4964babf84a8 --- /dev/null +++ b/drivers/clk/clk-rp1.c @@ -0,0 +1,2422 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Raspberry Pi Ltd. + * + * Clock driver for RP1 PCIe multifunction chip. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define PLL_SYS_CS 0x08000 +#define PLL_SYS_PWR 0x08004 +#define PLL_SYS_FBDIV_INT 0x08008 +#define PLL_SYS_FBDIV_FRAC 0x0800c +#define PLL_SYS_PRIM 0x08010 +#define PLL_SYS_SEC 0x08014 + +#define PLL_AUDIO_CS 0x0c000 +#define PLL_AUDIO_PWR 0x0c004 +#define PLL_AUDIO_FBDIV_INT 0x0c008 +#define PLL_AUDIO_FBDIV_FRAC 0x0c00c +#define PLL_AUDIO_PRIM 0x0c010 +#define PLL_AUDIO_SEC 0x0c014 +#define PLL_AUDIO_TERN 0x0c018 + +#define PLL_VIDEO_CS 0x10000 +#define PLL_VIDEO_PWR 0x10004 +#define PLL_VIDEO_FBDIV_INT 0x10008 +#define PLL_VIDEO_FBDIV_FRAC 0x1000c +#define PLL_VIDEO_PRIM 0x10010 +#define PLL_VIDEO_SEC 0x10014 + +#define GPCLK_OE_CTRL 0x00000 + +#define CLK_SYS_CTRL 0x00014 +#define CLK_SYS_DIV_INT 0x00018 +#define CLK_SYS_SEL 0x00020 + +#define CLK_SLOW_SYS_CTRL 0x00024 +#define CLK_SLOW_SYS_DIV_INT 0x00028 +#define CLK_SLOW_SYS_SEL 0x00030 + +#define CLK_DMA_CTRL 0x00044 +#define CLK_DMA_DIV_INT 0x00048 +#define CLK_DMA_SEL 0x00050 + +#define CLK_UART_CTRL 0x00054 +#define CLK_UART_DIV_INT 0x00058 +#define CLK_UART_SEL 0x00060 + +#define CLK_ETH_CTRL 0x00064 +#define CLK_ETH_DIV_INT 0x00068 +#define CLK_ETH_SEL 0x00070 + +#define CLK_PWM0_CTRL 0x00074 +#define CLK_PWM0_DIV_INT 0x00078 +#define CLK_PWM0_DIV_FRAC 0x0007c +#define CLK_PWM0_SEL 0x00080 + +#define CLK_PWM1_CTRL 0x00084 +#define CLK_PWM1_DIV_INT 0x00088 +#define CLK_PWM1_DIV_FRAC 0x0008c +#define CLK_PWM1_SEL 0x00090 + +#define CLK_AUDIO_IN_CTRL 0x00094 +#define CLK_AUDIO_IN_DIV_INT 0x00098 +#define CLK_AUDIO_IN_SEL 0x000a0 + +#define CLK_AUDIO_OUT_CTRL 0x000a4 +#define CLK_AUDIO_OUT_DIV_INT 0x000a8 +#define CLK_AUDIO_OUT_SEL 0x000b0 + +#define CLK_I2S_CTRL 0x000b4 +#define CLK_I2S_DIV_INT 0x000b8 +#define CLK_I2S_SEL 0x000c0 + +#define CLK_MIPI0_CFG_CTRL 0x000c4 +#define CLK_MIPI0_CFG_DIV_INT 0x000c8 +#define CLK_MIPI0_CFG_SEL 0x000d0 + +#define CLK_MIPI1_CFG_CTRL 0x000d4 +#define CLK_MIPI1_CFG_DIV_INT 0x000d8 +#define CLK_MIPI1_CFG_SEL 0x000e0 + +#define CLK_PCIE_AUX_CTRL 0x000e4 +#define CLK_PCIE_AUX_DIV_INT 0x000e8 +#define CLK_PCIE_AUX_SEL 0x000f0 + +#define CLK_USBH0_MICROFRAME_CTRL 0x000f4 +#define CLK_USBH0_MICROFRAME_DIV_INT 0x000f8 +#define CLK_USBH0_MICROFRAME_SEL 0x00100 + +#define CLK_USBH1_MICROFRAME_CTRL 0x00104 +#define CLK_USBH1_MICROFRAME_DIV_INT 0x00108 +#define CLK_USBH1_MICROFRAME_SEL 0x00110 + +#define CLK_USBH0_SUSPEND_CTRL 0x00114 +#define CLK_USBH0_SUSPEND_DIV_INT 0x00118 +#define CLK_USBH0_SUSPEND_SEL 0x00120 + +#define CLK_USBH1_SUSPEND_CTRL 0x00124 +#define CLK_USBH1_SUSPEND_DIV_INT 0x00128 +#define CLK_USBH1_SUSPEND_SEL 0x00130 + +#define CLK_ETH_TSU_CTRL 0x00134 +#define CLK_ETH_TSU_DIV_INT 0x00138 +#define CLK_ETH_TSU_SEL 0x00140 + +#define CLK_ADC_CTRL 0x00144 +#define CLK_ADC_DIV_INT 0x00148 +#define CLK_ADC_SEL 0x00150 + +#define CLK_SDIO_TIMER_CTRL 0x00154 +#define CLK_SDIO_TIMER_DIV_INT 0x00158 +#define CLK_SDIO_TIMER_SEL 0x00160 + +#define CLK_SDIO_ALT_SRC_CTRL 0x00164 +#define CLK_SDIO_ALT_SRC_DIV_INT 0x00168 +#define CLK_SDIO_ALT_SRC_SEL 0x00170 + +#define CLK_GP0_CTRL 0x00174 +#define CLK_GP0_DIV_INT 0x00178 +#define CLK_GP0_DIV_FRAC 0x0017c +#define CLK_GP0_SEL 0x00180 + +#define CLK_GP1_CTRL 0x00184 +#define CLK_GP1_DIV_INT 0x00188 +#define CLK_GP1_DIV_FRAC 0x0018c +#define CLK_GP1_SEL 0x00190 + +#define CLK_GP2_CTRL 0x00194 +#define CLK_GP2_DIV_INT 0x00198 +#define CLK_GP2_DIV_FRAC 0x0019c +#define CLK_GP2_SEL 0x001a0 + +#define CLK_GP3_CTRL 0x001a4 +#define CLK_GP3_DIV_INT 0x001a8 +#define CLK_GP3_DIV_FRAC 0x001ac +#define CLK_GP3_SEL 0x001b0 + +#define CLK_GP4_CTRL 0x001b4 +#define CLK_GP4_DIV_INT 0x001b8 +#define CLK_GP4_DIV_FRAC 0x001bc +#define CLK_GP4_SEL 0x001c0 + +#define CLK_GP5_CTRL 0x001c4 +#define CLK_GP5_DIV_INT 0x001c8 +#define CLK_GP5_DIV_FRAC 0x001cc +#define CLK_GP5_SEL 0x001d0 + +#define CLK_SYS_RESUS_CTRL 0x0020c + +#define CLK_SLOW_SYS_RESUS_CTRL 0x00214 + +#define FC0_REF_KHZ 0x0021c +#define FC0_MIN_KHZ 0x00220 +#define FC0_MAX_KHZ 0x00224 +#define FC0_DELAY 0x00228 +#define FC0_INTERVAL 0x0022c +#define FC0_SRC 0x00230 +#define FC0_STATUS 0x00234 +#define FC0_RESULT 0x00238 +#define FC_SIZE 0x20 +#define FC_COUNT 8 +#define FC_NUM(idx, off) ((idx) * 32 + (off)) + +#define AUX_SEL 1 + +#define VIDEO_CLOCKS_OFFSET 0x4000 +#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000) +#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004) +#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c) +#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010) +#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014) +#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c) +#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020) +#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024) +#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028) +#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c) +#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030) +#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034) +#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038) +#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c) + +#define DIV_INT_8BIT_MAX 0x000000ffu /* max divide for most clocks */ +#define DIV_INT_16BIT_MAX 0x0000ffffu /* max divide for GPx, PWM */ +#define DIV_INT_24BIT_MAX 0x00ffffffu /* max divide for CLK_SYS */ + +#define FC0_STATUS_DONE BIT(4) +#define FC0_STATUS_RUNNING BIT(8) +#define FC0_RESULT_FRAC_SHIFT 5 + +#define PLL_PRIM_DIV1_SHIFT 16 +#define PLL_PRIM_DIV1_MASK 0x00070000 +#define PLL_PRIM_DIV2_SHIFT 12 +#define PLL_PRIM_DIV2_MASK 0x00007000 + +#define PLL_SEC_DIV_SHIFT 8 +#define PLL_SEC_DIV_WIDTH 5 +#define PLL_SEC_DIV_MASK 0x00001f00 + +#define PLL_CS_LOCK BIT(31) +#define PLL_CS_REFDIV_SHIFT 0 + +#define PLL_PWR_PD BIT(0) +#define PLL_PWR_DACPD BIT(1) +#define PLL_PWR_DSMPD BIT(2) +#define PLL_PWR_POSTDIVPD BIT(3) +#define PLL_PWR_4PHASEPD BIT(4) +#define PLL_PWR_VCOPD BIT(5) +#define PLL_PWR_MASK 0x0000003f + +#define PLL_SEC_RST BIT(16) +#define PLL_SEC_IMPL BIT(31) + +/* PLL phase output for both PRI and SEC */ +#define PLL_PH_EN BIT(4) +#define PLL_PH_PHASE_SHIFT 0 + +#define RP1_PLL_PHASE_0 0 +#define RP1_PLL_PHASE_90 1 +#define RP1_PLL_PHASE_180 2 +#define RP1_PLL_PHASE_270 3 + +/* Clock fields for all clocks */ +#define CLK_CTRL_ENABLE BIT(11) +#define CLK_CTRL_AUXSRC_MASK 0x000003e0 +#define CLK_CTRL_AUXSRC_SHIFT 5 +#define CLK_CTRL_SRC_SHIFT 0 +#define CLK_DIV_FRAC_BITS 16 + +#define KHz 1000 +#define MHz (KHz * KHz) +#define LOCK_TIMEOUT_NS 100000000 +#define FC_TIMEOUT_NS 100000000 + +#define MAX_CLK_PARENTS 16 + +#define MEASURE_CLOCK_RATE +const char * const fc0_ref_clk_name = "clk_slow_sys"; + +#define ABS_DIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a)) +#define DIV_NEAREST(a, b) (((a) + ((b) >> 1)) / (b)) +#define DIV_U64_NEAREST(a, b) div_u64(((a) + ((b) >> 1)), (b)) + +/* + * Names of the reference clock for the pll cores. This name must match + * the DT reference clock-output-name. + */ +static const char *const ref_clock = "xosc"; + +/* + * Secondary PLL channel output divider table. + * Divider values range from 8 to 19. + * Invalid values default to 19 + */ +static const struct clk_div_table pll_sec_div_table[] = { + { 0x00, 19 }, + { 0x01, 19 }, + { 0x02, 19 }, + { 0x03, 19 }, + { 0x04, 19 }, + { 0x05, 19 }, + { 0x06, 19 }, + { 0x07, 19 }, + { 0x08, 8 }, + { 0x09, 9 }, + { 0x0a, 10 }, + { 0x0b, 11 }, + { 0x0c, 12 }, + { 0x0d, 13 }, + { 0x0e, 14 }, + { 0x0f, 15 }, + { 0x10, 16 }, + { 0x11, 17 }, + { 0x12, 18 }, + { 0x13, 19 }, + { 0x14, 19 }, + { 0x15, 19 }, + { 0x16, 19 }, + { 0x17, 19 }, + { 0x18, 19 }, + { 0x19, 19 }, + { 0x1a, 19 }, + { 0x1b, 19 }, + { 0x1c, 19 }, + { 0x1d, 19 }, + { 0x1e, 19 }, + { 0x1f, 19 }, + { 0 } +}; + +struct rp1_clockman { + struct device *dev; + void __iomem *regs; + spinlock_t regs_lock; /* spinlock for all clocks */ + + /* Must be last */ + struct clk_hw_onecell_data onecell; +}; + +struct rp1_pll_core_data { + const char *name; + u32 cs_reg; + u32 pwr_reg; + u32 fbdiv_int_reg; + u32 fbdiv_frac_reg; + unsigned long flags; + u32 fc0_src; +}; + +struct rp1_pll_data { + const char *name; + const char *source_pll; + u32 ctrl_reg; + unsigned long flags; + u32 fc0_src; +}; + +struct rp1_pll_ph_data { + const char *name; + const char *source_pll; + unsigned int phase; + unsigned int fixed_divider; + u32 ph_reg; + unsigned long flags; + u32 fc0_src; +}; + +struct rp1_pll_divider_data { + const char *name; + const char *source_pll; + u32 sec_reg; + unsigned long flags; + u32 fc0_src; +}; + +struct rp1_clock_data { + const char *name; + const char *const parents[MAX_CLK_PARENTS]; + int num_std_parents; + int num_aux_parents; + unsigned long flags; + u32 oe_mask; + u32 clk_src_mask; + u32 ctrl_reg; + u32 div_int_reg; + u32 div_frac_reg; + u32 sel_reg; + u32 div_int_max; + unsigned long max_freq; + u32 fc0_src; +}; + +struct rp1_pll_core { + struct clk_hw hw; + struct rp1_clockman *clockman; + const struct rp1_pll_core_data *data; + unsigned long cached_rate; +}; + +struct rp1_pll { + struct clk_hw hw; + struct clk_divider div; + struct rp1_clockman *clockman; + const struct rp1_pll_data *data; + unsigned long cached_rate; +}; + +struct rp1_pll_ph { + struct clk_hw hw; + struct rp1_clockman *clockman; + const struct rp1_pll_ph_data *data; +}; + +struct rp1_clock { + struct clk_hw hw; + struct rp1_clockman *clockman; + const struct rp1_clock_data *data; + unsigned long cached_rate; +}; + + +struct rp1_clk_change { + struct clk_hw *hw; + unsigned long new_rate; +}; + +struct rp1_clk_change rp1_clk_chg_tree[3]; + +static struct clk_hw *clk_xosc; +static struct clk_hw *clk_audio; +static struct clk_hw *clk_i2s; + +static void rp1_debugfs_regset(struct rp1_clockman *clockman, u32 base, + const struct debugfs_reg32 *regs, + size_t nregs, struct dentry *dentry) +{ + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(clockman->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = regs; + regset->nregs = nregs; + regset->base = clockman->regs + base; + + debugfs_create_regset32("regdump", 0444, dentry, regset); +} + +static inline u32 set_register_field(u32 reg, u32 val, u32 mask, u32 shift) +{ + reg &= ~mask; + reg |= (val << shift) & mask; + return reg; +} + +static inline +void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val) +{ + writel(val, clockman->regs + reg); +} + +static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg) +{ + return readl(clockman->regs + reg); +} + +#ifdef MEASURE_CLOCK_RATE +static unsigned long clockman_measure_clock(struct rp1_clockman *clockman, + const char *clk_name, + unsigned int fc0_src) +{ + struct clk *ref_clk = __clk_lookup(fc0_ref_clk_name); + unsigned long result; + ktime_t timeout; + unsigned int fc_idx, fc_offset, fc_src; + + fc_idx = fc0_src / 32; + fc_src = fc0_src % 32; + + /* fc_src == 0 is invalid. */ + if (!fc_src || fc_idx >= FC_COUNT) + return 0; + + fc_offset = fc_idx * FC_SIZE; + + /* Ensure the frequency counter is idle. */ + timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS); + while (clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_RUNNING) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(clockman->dev, "%s: FC0 busy timeout\n", + clk_name); + return 0; + } + cpu_relax(); + } + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, fc_offset + FC0_REF_KHZ, + clk_get_rate(ref_clk) / KHz); + clockman_write(clockman, fc_offset + FC0_MIN_KHZ, 0); + clockman_write(clockman, fc_offset + FC0_MAX_KHZ, 0x1ffffff); + clockman_write(clockman, fc_offset + FC0_INTERVAL, 8); + clockman_write(clockman, fc_offset + FC0_DELAY, 7); + clockman_write(clockman, fc_offset + FC0_SRC, fc_src); + spin_unlock(&clockman->regs_lock); + + /* Ensure the frequency counter is idle. */ + timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS); + while (!(clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_DONE)) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(clockman->dev, "%s: FC0 wait timeout\n", + clk_name); + return 0; + } + cpu_relax(); + } + + result = clockman_read(clockman, fc_offset + FC0_RESULT); + + /* Disable FC0 */ + spin_lock(&clockman->regs_lock); + clockman_write(clockman, fc_offset + FC0_SRC, 0); + spin_unlock(&clockman->regs_lock); + + return result; +} +#endif + +static int rp1_pll_core_is_on(struct clk_hw *hw) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 pwr = clockman_read(clockman, data->pwr_reg); + + return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD); +} + +static int rp1_pll_core_on(struct clk_hw *hw) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_frac; + ktime_t timeout; + + spin_lock(&clockman->regs_lock); + + if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) { + /* Reset to a known state. */ + clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK); + clockman_write(clockman, data->fbdiv_int_reg, 20); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + clockman_write(clockman, data->cs_reg, 1 << PLL_CS_REFDIV_SHIFT); + } + + /* Come out of reset. */ + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + spin_unlock(&clockman->regs_lock); + + /* Wait for the PLL to lock. */ + timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + while (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(clockman->dev, "%s: can't lock PLL\n", + clk_hw_get_name(hw)); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return 0; +} + +static void rp1_pll_core_off(struct clk_hw *hw) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, 0); + spin_unlock(&clockman->regs_lock); +} + +static inline unsigned long get_pll_core_divider(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u32 *div_int, u32 *div_frac) +{ + unsigned long calc_rate; + u32 fbdiv_int, fbdiv_frac; + u64 div_fp64; /* 32.32 fixed point fraction. */ + + /* Factor of reference clock to VCO frequency. */ + div_fp64 = (u64)(rate) << 32; + div_fp64 = DIV_U64_NEAREST(div_fp64, parent_rate); + + /* Round the fractional component at 24 bits. */ + div_fp64 += 1 << (32 - 24 - 1); + + fbdiv_int = div_fp64 >> 32; + fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff; + + calc_rate = + ((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24; + + *div_int = fbdiv_int; + *div_frac = fbdiv_frac; + + return calc_rate; +} + +static int rp1_pll_core_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + unsigned long calc_rate; + u32 fbdiv_int, fbdiv_frac; + + // todo: is this needed?? + //rp1_pll_off(hw); + + /* Disable dividers to start with. */ + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->fbdiv_int_reg, 0); + clockman_write(clockman, data->fbdiv_frac_reg, 0); + spin_unlock(&clockman->regs_lock); + + calc_rate = get_pll_core_divider(hw, rate, parent_rate, + &fbdiv_int, &fbdiv_frac); + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD); + clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int); + clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac); + spin_unlock(&clockman->regs_lock); + + /* Check that reference frequency is no greater than VCO / 16. */ + BUG_ON(parent_rate > (rate / 16)); + + pll_core->cached_rate = calc_rate; + + spin_lock(&clockman->regs_lock); + /* Don't need to divide ref unless parent_rate > (output freq / 16) */ + clockman_write(clockman, data->cs_reg, + clockman_read(clockman, data->cs_reg) | + (1 << PLL_CS_REFDIV_SHIFT)); + spin_unlock(&clockman->regs_lock); + + return 0; +} + +static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + u32 fbdiv_int, fbdiv_frac; + unsigned long calc_rate; + + fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg); + fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg); + calc_rate = + ((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24; + + return calc_rate; +} + +static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 fbdiv_int, fbdiv_frac; + long calc_rate; + + calc_rate = get_pll_core_divider(hw, rate, *parent_rate, + &fbdiv_int, &fbdiv_frac); + return calc_rate; +} + +static void rp1_pll_core_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw); + struct rp1_clockman *clockman = pll_core->clockman; + const struct rp1_pll_core_data *data = pll_core->data; + struct debugfs_reg32 *regs; + + regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = "cs"; + regs[0].offset = data->cs_reg; + regs[1].name = "pwr"; + regs[1].offset = data->pwr_reg; + regs[2].name = "fbdiv_int"; + regs[2].offset = data->fbdiv_int_reg; + regs[3].name = "fbdiv_frac"; + regs[3].offset = data->fbdiv_frac_reg; + + rp1_debugfs_regset(clockman, 0, regs, 4, dentry); +} + +static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate, + u32 *divider1, u32 *divider2) +{ + unsigned int div1, div2; + unsigned int best_div1 = 7, best_div2 = 7; + unsigned long best_rate_diff = + ABS_DIFF(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate); + long rate_diff, calc_rate; + + for (div1 = 1; div1 <= 7; div1++) { + for (div2 = 1; div2 <= div1; div2++) { + calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2); + rate_diff = ABS_DIFF(calc_rate, rate); + + if (calc_rate == rate) { + best_div1 = div1; + best_div2 = div2; + goto done; + } else if (rate_diff < best_rate_diff) { + best_div1 = div1; + best_div2 = div2; + best_rate_diff = rate_diff; + } + } + } + +done: + *divider1 = best_div1; + *divider2 = best_div2; +} + +static int rp1_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + u32 prim, prim_div1, prim_div2; + + get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2); + + spin_lock(&clockman->regs_lock); + prim = clockman_read(clockman, data->ctrl_reg); + prim = set_register_field(prim, prim_div1, PLL_PRIM_DIV1_MASK, + PLL_PRIM_DIV1_SHIFT); + prim = set_register_field(prim, prim_div2, PLL_PRIM_DIV2_MASK, + PLL_PRIM_DIV2_SHIFT); + clockman_write(clockman, data->ctrl_reg, prim); + spin_unlock(&clockman->regs_lock); + +#ifdef MEASURE_CLOCK_RATE + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + u32 prim, prim_div1, prim_div2; + + prim = clockman_read(clockman, data->ctrl_reg); + prim_div1 = (prim & PLL_PRIM_DIV1_MASK) >> PLL_PRIM_DIV1_SHIFT; + prim_div2 = (prim & PLL_PRIM_DIV2_MASK) >> PLL_PRIM_DIV2_SHIFT; + + if (!prim_div1 || !prim_div2) { + dev_err(clockman->dev, "%s: (%s) zero divider value\n", + __func__, data->name); + return 0; + } + + return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2); +} + +static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + const struct rp1_clk_change *chg = &rp1_clk_chg_tree[1]; + u32 div1, div2; + + if (chg->hw == hw && chg->new_rate == rate) + *parent_rate = chg[1].new_rate; + + get_pll_prim_dividers(rate, *parent_rate, &div1, &div2); + + return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2); +} + +static void rp1_pll_debug_init(struct clk_hw *hw, + struct dentry *dentry) +{ + struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_data *data = pll->data; + struct debugfs_reg32 *regs; + + regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = "prim"; + regs[0].offset = data->ctrl_reg; + + rp1_debugfs_regset(clockman, 0, regs, 1, dentry); +} + +static int rp1_pll_ph_is_on(struct clk_hw *hw) +{ + struct rp1_pll_ph *pll = container_of(hw, struct rp1_pll_ph, hw); + struct rp1_clockman *clockman = pll->clockman; + const struct rp1_pll_ph_data *data = pll->data; + + return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN); +} + +static int rp1_pll_ph_on(struct clk_hw *hw) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + u32 ph_reg; + + /* todo: ensure pri/sec is enabled! */ + spin_lock(&clockman->regs_lock); + ph_reg = clockman_read(clockman, data->ph_reg); + ph_reg |= data->phase << PLL_PH_PHASE_SHIFT; + ph_reg |= PLL_PH_EN; + clockman_write(clockman, data->ph_reg, ph_reg); + spin_unlock(&clockman->regs_lock); + +#ifdef MEASURE_CLOCK_RATE + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static void rp1_pll_ph_off(struct clk_hw *hw) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + struct rp1_clockman *clockman = pll_ph->clockman; + const struct rp1_pll_ph_data *data = pll_ph->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ph_reg, + clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN); + spin_unlock(&clockman->regs_lock); +} + +static int rp1_pll_ph_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + struct rp1_clockman *clockman = pll_ph->clockman; + + /* Nothing really to do here! */ + WARN_ON(data->fixed_divider != 1 && data->fixed_divider != 2); + WARN_ON(rate != parent_rate / data->fixed_divider); + +#ifdef MEASURE_CLOCK_RATE + if (rp1_pll_ph_is_on(hw)) + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return parent_rate / data->fixed_divider; +} + +static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + + return *parent_rate / data->fixed_divider; +} + +static void rp1_pll_ph_debug_init(struct clk_hw *hw, + struct dentry *dentry) +{ + struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw); + const struct rp1_pll_ph_data *data = pll_ph->data; + struct rp1_clockman *clockman = pll_ph->clockman; + struct debugfs_reg32 *regs; + + regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = "ph_reg"; + regs[0].offset = data->ph_reg; + + rp1_debugfs_regset(clockman, 0, regs, 1, dentry); +} + +static int rp1_pll_divider_is_on(struct clk_hw *hw) +{ + struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST); +} + +static int rp1_pll_divider_on(struct clk_hw *hw) +{ + struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + /* Check the implementation bit is set! */ + WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL)); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); + +#ifdef MEASURE_CLOCK_RATE + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static void rp1_pll_divider_off(struct clk_hw *hw) +{ + struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); +} + +static int rp1_pll_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + u32 div, sec; + + div = DIV_ROUND_UP_ULL(parent_rate, rate); + div = clamp(div, 8u, 19u); + + spin_lock(&clockman->regs_lock); + sec = clockman_read(clockman, data->ctrl_reg); + sec = set_register_field(sec, div, PLL_SEC_DIV_MASK, PLL_SEC_DIV_SHIFT); + + /* Must keep the divider in reset to change the value. */ + sec |= PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + + // todo: must sleep 10 pll vco cycles + sec &= ~PLL_SEC_RST; + clockman_write(clockman, data->ctrl_reg, sec); + spin_unlock(&clockman->regs_lock); + +#ifdef MEASURE_CLOCK_RATE + if (rp1_pll_divider_is_on(hw)) + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long rp1_pll_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return clk_divider_ops.round_rate(hw, rate, parent_rate); +} + +static void rp1_pll_divider_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw); + struct rp1_clockman *clockman = divider->clockman; + const struct rp1_pll_data *data = divider->data; + struct debugfs_reg32 *regs; + + regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = "sec"; + regs[0].offset = data->ctrl_reg; + + rp1_debugfs_regset(clockman, 0, regs, 1, dentry); +} + +static int rp1_clock_is_on(struct clk_hw *hw) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE); +} + +static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u64 calc_rate; + u64 div; + + u32 frac; + + div = clockman_read(clockman, data->div_int_reg); + frac = (data->div_frac_reg != 0) ? + clockman_read(clockman, data->div_frac_reg) : 0; + + /* If the integer portion of the divider is 0, treat it as 2^16 */ + if (!div) + div = 1 << 16; + + div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS)); + + calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS; + calc_rate = div64_u64(calc_rate, div); + + return calc_rate; +} + +static int rp1_clock_on(struct clk_hw *hw) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn on the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) | data->oe_mask); + spin_unlock(&clockman->regs_lock); + +#ifdef MEASURE_CLOCK_RATE + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static void rp1_clock_off(struct clk_hw *hw) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + + spin_lock(&clockman->regs_lock); + clockman_write(clockman, data->ctrl_reg, + clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE); + /* If this is a GPCLK, turn off the output-enable */ + if (data->oe_mask) + clockman_write(clockman, GPCLK_OE_CTRL, + clockman_read(clockman, GPCLK_OE_CTRL) & ~data->oe_mask); + spin_unlock(&clockman->regs_lock); +} + +static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate, + const struct rp1_clock_data *data) +{ + u64 div; + + /* + * Due to earlier rounding, calculated parent_rate may differ from + * expected value. Don't fail on a small discrepancy near unity divide. + */ + if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS)) + return 0; + + /* + * Always express div in fixed-point format for fractional division; + * If no fractional divider is present, the fraction part will be zero. + */ + if (data->div_frac_reg) { + div = (u64)parent_rate << CLK_DIV_FRAC_BITS; + div = DIV_U64_NEAREST(div, rate); + } else { + div = DIV_U64_NEAREST(parent_rate, rate); + div <<= CLK_DIV_FRAC_BITS; + } + + div = clamp(div, + 1ull << CLK_DIV_FRAC_BITS, + (u64)data->div_int_max << CLK_DIV_FRAC_BITS); + + return div; +} + +static u8 rp1_clock_get_parent(struct clk_hw *hw) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 sel, ctrl; + u8 parent; + + /* Sel is one-hot, so find the first bit set */ + sel = clockman_read(clockman, data->sel_reg); + parent = ffs(sel) - 1; + + /* sel == 0 implies the parent clock is not enabled yet. */ + if (!sel) { + /* Read the clock src from the CTRL register instead */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT; + } + + if (parent >= data->num_std_parents) + parent = AUX_SEL; + + if (parent == AUX_SEL) { + /* + * Clock parent is an auxiliary source, so get the parent from + * the AUXSRC register field. + */ + ctrl = clockman_read(clockman, data->ctrl_reg); + parent = (ctrl & CLK_CTRL_AUXSRC_MASK) >> CLK_CTRL_AUXSRC_SHIFT; + parent += data->num_std_parents; + } + + return parent; +} + +static int rp1_clock_set_parent(struct clk_hw *hw, u8 index) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 ctrl, sel; + + spin_lock(&clockman->regs_lock); + ctrl = clockman_read(clockman, data->ctrl_reg); + + if (index >= data->num_std_parents) { + /* This is an aux source request */ + if (index >= data->num_std_parents + data->num_aux_parents) + return -EINVAL; + + /* Select parent from aux list */ + ctrl = set_register_field(ctrl, index - data->num_std_parents, + CLK_CTRL_AUXSRC_MASK, + CLK_CTRL_AUXSRC_SHIFT); + /* Set src to aux list */ + ctrl = set_register_field(ctrl, AUX_SEL, data->clk_src_mask, + CLK_CTRL_SRC_SHIFT); + } else { + ctrl = set_register_field(ctrl, index, data->clk_src_mask, + CLK_CTRL_SRC_SHIFT); + } + + clockman_write(clockman, data->ctrl_reg, ctrl); + spin_unlock(&clockman->regs_lock); + + sel = rp1_clock_get_parent(hw); + WARN(sel != index, "(%s): Parent index req %u returned back %u\n", + data->name, index, sel); + + return 0; +} + +static int rp1_clock_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 parent) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + u32 div = rp1_clock_choose_div(rate, parent_rate, data); + + WARN(rate > 4000000000ll, "rate is -ve (%d)\n", (int)rate); + + if (WARN(!div, + "clk divider calculated as 0! (%s, rate %ld, parent rate %ld)\n", + data->name, rate, parent_rate)) + div = 1 << CLK_DIV_FRAC_BITS; + + spin_lock(&clockman->regs_lock); + + clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS); + if (data->div_frac_reg) + clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS)); + + spin_unlock(&clockman->regs_lock); + + if (parent != 0xff) + rp1_clock_set_parent(hw, parent); + +#ifdef MEASURE_CLOCK_RATE + if (rp1_clock_is_on(hw)) + clockman_measure_clock(clockman, data->name, data->fc0_src); +#endif + return 0; +} + +static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff); +} + +static unsigned long calc_core_pll_rate(struct clk_hw *pll_hw, + unsigned long target_rate, + int *pdiv_prim, int *pdiv_clk) +{ + static const int prim_divs[] = { + 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, + 18, 20, 21, 24, 25, 28, 30, 35, 36, 42, 49, + }; + const unsigned long xosc_rate = clk_hw_get_rate(clk_xosc); + const unsigned long core_max = 2400000000; + const unsigned long core_min = xosc_rate * 16; + unsigned long best_rate = core_max + 1; + int best_div_prim = 1, best_div_clk = 1; + unsigned long core_rate = 0; + int div_int, div_frac; + u64 div; + int i; + + /* Given the target rate, choose a set of divisors/multipliers */ + for (i = 0; i < ARRAY_SIZE(prim_divs); i++) { + int div_prim = prim_divs[i]; + int div_clk; + + for (div_clk = 1; div_clk <= 256; div_clk++) { + core_rate = target_rate * div_clk * div_prim; + if (core_rate >= core_min) { + if (core_rate < best_rate) { + best_rate = core_rate; + best_div_prim = div_prim; + best_div_clk = div_clk; + } + break; + } + } + } + + if (best_rate < core_max) { + div = ((best_rate << 24) + xosc_rate / 2) / xosc_rate; + div_int = div >> 24; + div_frac = div % (1 << 24); + core_rate = (xosc_rate * ((div_int << 24) + div_frac) + (1 << 23)) >> 24; + } else { + core_rate = 0; + } + + if (pdiv_prim) + *pdiv_prim = best_div_prim; + if (pdiv_clk) + *pdiv_clk = best_div_clk; + + return core_rate; +} + +static void rp1_clock_choose_div_and_prate(struct clk_hw *hw, + int parent_idx, + unsigned long rate, + unsigned long *prate, + unsigned long *calc_rate) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + const struct rp1_clock_data *data = clock->data; + struct clk_hw *parent; + u32 div; + u64 tmp; + int i; + + parent = clk_hw_get_parent_by_index(hw, parent_idx); + + for (i = 0; i < ARRAY_SIZE(rp1_clk_chg_tree); i++) { + const struct rp1_clk_change *chg = &rp1_clk_chg_tree[i]; + + if (chg->hw == hw && chg->new_rate == rate) { + if (i == 2) + *prate = clk_hw_get_rate(clk_xosc); + else if (parent == rp1_clk_chg_tree[i + 1].hw) + *prate = rp1_clk_chg_tree[i + 1].new_rate; + else + continue; + *calc_rate = chg->new_rate; + return; + } + } + + if (hw == clk_i2s && parent == clk_audio) { + unsigned long core_rate, audio_rate, i2s_rate; + int div_prim, div_clk; + + core_rate = calc_core_pll_rate(parent, rate, &div_prim, &div_clk); + audio_rate = DIV_NEAREST(core_rate, div_prim); + i2s_rate = DIV_NEAREST(audio_rate, div_clk); + rp1_clk_chg_tree[2].hw = clk_hw_get_parent(parent); + rp1_clk_chg_tree[2].new_rate = core_rate; + rp1_clk_chg_tree[1].hw = clk_audio; + rp1_clk_chg_tree[1].new_rate = audio_rate; + rp1_clk_chg_tree[0].hw = clk_i2s; + rp1_clk_chg_tree[0].new_rate = i2s_rate; + *prate = audio_rate; + *calc_rate = i2s_rate; + return; + } + + *prate = clk_hw_get_rate(parent); + div = rp1_clock_choose_div(rate, *prate, data); + + if (!div) { + *calc_rate = 0; + return; + } + + /* Recalculate to account for rounding errors */ + tmp = (u64)*prate << CLK_DIV_FRAC_BITS; + tmp = div_u64(tmp, div); + /* + * Prevent overclocks - if all parent choices result in + * a downstream clock in excess of the maximum, then the + * call to set the clock will fail. + */ + if (tmp > clock->data->max_freq) + *calc_rate = 0; + else + *calc_rate = tmp; +} + +static int rp1_clock_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *parent, *best_parent = NULL; + unsigned long best_rate = 0; + unsigned long best_prate = 0; + unsigned long best_rate_diff = ULONG_MAX; + unsigned long prate, calc_rate; + size_t i; + + /* + * If the NO_REPARENT flag is set, try to use existing parent. + */ + if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) { + i = rp1_clock_get_parent(hw); + parent = clk_hw_get_parent_by_index(hw, i); + if (parent) { + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + if (calc_rate > 0) { + req->best_parent_hw = parent; + req->best_parent_rate = prate; + req->rate = calc_rate; + return 0; + } + } + } + + /* + * Select parent clock that results in the closest rate (lower or + * higher) + */ + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate, + &calc_rate); + + if (ABS_DIFF(calc_rate, req->rate) < best_rate_diff) { + best_parent = parent; + best_prate = prate; + best_rate = calc_rate; + best_rate_diff = ABS_DIFF(calc_rate, req->rate); + + if (best_rate_diff == 0) + break; + } + } + + if (best_rate == 0) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = best_prate; + req->rate = best_rate; + + return 0; +} + +static void rp1_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw); + struct rp1_clockman *clockman = clock->clockman; + const struct rp1_clock_data *data = clock->data; + struct debugfs_reg32 *regs; + int i; + + regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + i = 0; + regs[i].name = "ctrl"; + regs[i++].offset = data->ctrl_reg; + regs[i].name = "div_int"; + regs[i++].offset = data->div_int_reg; + regs[i].name = "div_frac"; + regs[i++].offset = data->div_frac_reg; + regs[i].name = "sel"; + regs[i++].offset = data->sel_reg; + + rp1_debugfs_regset(clockman, 0, regs, i, dentry); +} + +static const struct clk_ops rp1_pll_core_ops = { + .is_prepared = rp1_pll_core_is_on, + .prepare = rp1_pll_core_on, + .unprepare = rp1_pll_core_off, + .set_rate = rp1_pll_core_set_rate, + .recalc_rate = rp1_pll_core_recalc_rate, + .round_rate = rp1_pll_core_round_rate, + .debug_init = rp1_pll_core_debug_init, +}; + +static const struct clk_ops rp1_pll_ops = { + .set_rate = rp1_pll_set_rate, + .recalc_rate = rp1_pll_recalc_rate, + .round_rate = rp1_pll_round_rate, + .debug_init = rp1_pll_debug_init, +}; + +static const struct clk_ops rp1_pll_ph_ops = { + .is_prepared = rp1_pll_ph_is_on, + .prepare = rp1_pll_ph_on, + .unprepare = rp1_pll_ph_off, + .set_rate = rp1_pll_ph_set_rate, + .recalc_rate = rp1_pll_ph_recalc_rate, + .round_rate = rp1_pll_ph_round_rate, + .debug_init = rp1_pll_ph_debug_init, +}; + +static const struct clk_ops rp1_pll_divider_ops = { + .is_prepared = rp1_pll_divider_is_on, + .prepare = rp1_pll_divider_on, + .unprepare = rp1_pll_divider_off, + .set_rate = rp1_pll_divider_set_rate, + .recalc_rate = rp1_pll_divider_recalc_rate, + .round_rate = rp1_pll_divider_round_rate, + .debug_init = rp1_pll_divider_debug_init, +}; + +static const struct clk_ops rp1_clk_ops = { + .is_prepared = rp1_clock_is_on, + .prepare = rp1_clock_on, + .unprepare = rp1_clock_off, + .recalc_rate = rp1_clock_recalc_rate, + .get_parent = rp1_clock_get_parent, + .set_parent = rp1_clock_set_parent, + .set_rate_and_parent = rp1_clock_set_rate_and_parent, + .set_rate = rp1_clock_set_rate, + .determine_rate = rp1_clock_determine_rate, + .debug_init = rp1_clk_debug_init, +}; + +static bool rp1_clk_is_claimed(const char *name); + +static struct clk_hw *rp1_register_pll_core(struct rp1_clockman *clockman, + const void *data) +{ + const struct rp1_pll_core_data *pll_core_data = data; + struct rp1_pll_core *pll_core; + struct clk_init_data init; + int ret; + + memset(&init, 0, sizeof(init)); + + /* All of the PLL cores derive from the external oscillator. */ + init.parent_names = &ref_clock; + init.num_parents = 1; + init.name = pll_core_data->name; + init.ops = &rp1_pll_core_ops; + init.flags = pll_core_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; + + pll_core = kzalloc(sizeof(*pll_core), GFP_KERNEL); + if (!pll_core) + return NULL; + + pll_core->clockman = clockman; + pll_core->data = pll_core_data; + pll_core->hw.init = &init; + + ret = devm_clk_hw_register(clockman->dev, &pll_core->hw); + if (ret) { + kfree(pll_core); + return NULL; + } + + return &pll_core->hw; +} + +static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, + const void *data) +{ + const struct rp1_pll_data *pll_data = data; + struct rp1_pll *pll; + struct clk_init_data init; + int ret; + + memset(&init, 0, sizeof(init)); + + init.parent_names = &pll_data->source_pll; + init.num_parents = 1; + init.name = pll_data->name; + init.ops = &rp1_pll_ops; + init.flags = pll_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return NULL; + + pll->clockman = clockman; + pll->data = pll_data; + pll->hw.init = &init; + + ret = devm_clk_hw_register(clockman->dev, &pll->hw); + if (ret) { + kfree(pll); + return NULL; + } + + return &pll->hw; +} + +static struct clk_hw *rp1_register_pll_ph(struct rp1_clockman *clockman, + const void *data) +{ + const struct rp1_pll_ph_data *ph_data = data; + struct rp1_pll_ph *ph; + struct clk_init_data init; + int ret; + + memset(&init, 0, sizeof(init)); + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = &ph_data->source_pll; + init.num_parents = 1; + init.name = ph_data->name; + init.ops = &rp1_pll_ph_ops; + init.flags = ph_data->flags | CLK_IGNORE_UNUSED; + + ph = kzalloc(sizeof(*ph), GFP_KERNEL); + if (!ph) + return NULL; + + ph->clockman = clockman; + ph->data = ph_data; + ph->hw.init = &init; + + ret = devm_clk_hw_register(clockman->dev, &ph->hw); + if (ret) { + kfree(ph); + return NULL; + } + + return &ph->hw; +} + +static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman, + const void *data) +{ + const struct rp1_pll_data *divider_data = data; + struct rp1_pll *divider; + struct clk_init_data init; + int ret; + + memset(&init, 0, sizeof(init)); + + init.parent_names = ÷r_data->source_pll; + init.num_parents = 1; + init.name = divider_data->name; + init.ops = &rp1_pll_divider_ops; + init.flags = divider_data->flags | CLK_IGNORE_UNUSED; + + divider = devm_kzalloc(clockman->dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return NULL; + + divider->div.reg = clockman->regs + divider_data->ctrl_reg; + divider->div.shift = PLL_SEC_DIV_SHIFT; + divider->div.width = PLL_SEC_DIV_WIDTH; + divider->div.flags = CLK_DIVIDER_ROUND_CLOSEST; + divider->div.lock = &clockman->regs_lock; + divider->div.hw.init = &init; + divider->div.table = pll_sec_div_table; + + if (!rp1_clk_is_claimed(divider_data->source_pll)) + init.flags |= CLK_IS_CRITICAL; + if (!rp1_clk_is_claimed(divider_data->name)) + divider->div.flags |= CLK_IS_CRITICAL; + + divider->clockman = clockman; + divider->data = divider_data; + + ret = devm_clk_hw_register(clockman->dev, ÷r->div.hw); + if (ret) + return ERR_PTR(ret); + + return ÷r->div.hw; +} + +static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman, + const void *data) +{ + const struct rp1_clock_data *clock_data = data; + struct rp1_clock *clock; + struct clk_init_data init; + int ret; + + BUG_ON(MAX_CLK_PARENTS < + clock_data->num_std_parents + clock_data->num_aux_parents); + /* There must be a gap for the AUX selector */ + BUG_ON((clock_data->num_std_parents > AUX_SEL) && + strcmp("-", clock_data->parents[AUX_SEL])); + + memset(&init, 0, sizeof(init)); + init.parent_names = clock_data->parents; + init.num_parents = + clock_data->num_std_parents + clock_data->num_aux_parents; + init.name = clock_data->name; + init.flags = clock_data->flags | CLK_IGNORE_UNUSED; + init.ops = &rp1_clk_ops; + + clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL); + if (!clock) + return NULL; + + clock->clockman = clockman; + clock->data = clock_data; + clock->hw.init = &init; + + ret = devm_clk_hw_register(clockman->dev, &clock->hw); + if (ret) + return ERR_PTR(ret); + + return &clock->hw; +} + +struct rp1_clk_desc { + struct clk_hw *(*clk_register)(struct rp1_clockman *clockman, + const void *data); + const void *data; +}; + +/* Assignment helper macros for different clock types. */ +#define _REGISTER(f, ...) { .clk_register = f, .data = __VA_ARGS__ } + +#define REGISTER_PLL_CORE(...) _REGISTER(&rp1_register_pll_core, \ + &(struct rp1_pll_core_data) \ + {__VA_ARGS__}) + +#define REGISTER_PLL(...) _REGISTER(&rp1_register_pll, \ + &(struct rp1_pll_data) \ + {__VA_ARGS__}) + +#define REGISTER_PLL_PH(...) _REGISTER(&rp1_register_pll_ph, \ + &(struct rp1_pll_ph_data) \ + {__VA_ARGS__}) + +#define REGISTER_PLL_DIV(...) _REGISTER(&rp1_register_pll_divider, \ + &(struct rp1_pll_data) \ + {__VA_ARGS__}) + +#define REGISTER_CLK(...) _REGISTER(&rp1_register_clock, \ + &(struct rp1_clock_data) \ + {__VA_ARGS__}) + +static const struct rp1_clk_desc clk_desc_array[] = { + [RP1_PLL_SYS_CORE] = REGISTER_PLL_CORE( + .name = "pll_sys_core", + .cs_reg = PLL_SYS_CS, + .pwr_reg = PLL_SYS_PWR, + .fbdiv_int_reg = PLL_SYS_FBDIV_INT, + .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC, + ), + + [RP1_PLL_AUDIO_CORE] = REGISTER_PLL_CORE( + .name = "pll_audio_core", + .cs_reg = PLL_AUDIO_CS, + .pwr_reg = PLL_AUDIO_PWR, + .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT, + .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC, + ), + + [RP1_PLL_VIDEO_CORE] = REGISTER_PLL_CORE( + .name = "pll_video_core", + .cs_reg = PLL_VIDEO_CS, + .pwr_reg = PLL_VIDEO_PWR, + .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT, + .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC, + ), + + [RP1_PLL_SYS] = REGISTER_PLL( + .name = "pll_sys", + .source_pll = "pll_sys_core", + .ctrl_reg = PLL_SYS_PRIM, + .fc0_src = FC_NUM(0, 2), + ), + + [RP1_PLL_AUDIO] = REGISTER_PLL( + .name = "pll_audio", + .source_pll = "pll_audio_core", + .ctrl_reg = PLL_AUDIO_PRIM, + .fc0_src = FC_NUM(4, 2), + .flags = CLK_SET_RATE_PARENT, + ), + + [RP1_PLL_VIDEO] = REGISTER_PLL( + .name = "pll_video", + .source_pll = "pll_video_core", + .ctrl_reg = PLL_VIDEO_PRIM, + .fc0_src = FC_NUM(3, 2), + ), + + [RP1_PLL_SYS_PRI_PH] = REGISTER_PLL_PH( + .name = "pll_sys_pri_ph", + .source_pll = "pll_sys", + .ph_reg = PLL_SYS_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(1, 2), + ), + + [RP1_PLL_AUDIO_PRI_PH] = REGISTER_PLL_PH( + .name = "pll_audio_pri_ph", + .source_pll = "pll_audio", + .ph_reg = PLL_AUDIO_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(5, 1), + ), + + [RP1_PLL_VIDEO_PRI_PH] = REGISTER_PLL_PH( + .name = "pll_video_pri_ph", + .source_pll = "pll_video", + .ph_reg = PLL_VIDEO_PRIM, + .fixed_divider = 2, + .phase = RP1_PLL_PHASE_0, + .fc0_src = FC_NUM(4, 3), + ), + + [RP1_PLL_SYS_SEC] = REGISTER_PLL_DIV( + .name = "pll_sys_sec", + .source_pll = "pll_sys_core", + .ctrl_reg = PLL_SYS_SEC, + .fc0_src = FC_NUM(2, 2), + ), + + [RP1_PLL_AUDIO_SEC] = REGISTER_PLL_DIV( + .name = "pll_audio_sec", + .source_pll = "pll_audio_core", + .ctrl_reg = PLL_AUDIO_SEC, + .fc0_src = FC_NUM(6, 2), + ), + + [RP1_PLL_VIDEO_SEC] = REGISTER_PLL_DIV( + .name = "pll_video_sec", + .source_pll = "pll_video_core", + .ctrl_reg = PLL_VIDEO_SEC, + .fc0_src = FC_NUM(5, 3), + ), + + [RP1_PLL_AUDIO_TERN] = REGISTER_PLL_DIV( + .name = "pll_audio_tern", + .source_pll = "pll_audio_core", + .ctrl_reg = PLL_AUDIO_TERN, + .fc0_src = FC_NUM(6, 2), + ), + + [RP1_CLK_SYS] = REGISTER_CLK( + .name = "clk_sys", + .parents = {"xosc", "-", "pll_sys"}, + .num_std_parents = 3, + .num_aux_parents = 0, + .ctrl_reg = CLK_SYS_CTRL, + .div_int_reg = CLK_SYS_DIV_INT, + .sel_reg = CLK_SYS_SEL, + .div_int_max = DIV_INT_24BIT_MAX, + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(0, 4), + .clk_src_mask = 0x3, + ), + + [RP1_CLK_SLOW_SYS] = REGISTER_CLK( + .name = "clk_slow_sys", + .parents = {"xosc"}, + .num_std_parents = 1, + .num_aux_parents = 0, + .ctrl_reg = CLK_SLOW_SYS_CTRL, + .div_int_reg = CLK_SLOW_SYS_DIV_INT, + .sel_reg = CLK_SLOW_SYS_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(1, 4), + .clk_src_mask = 0x1, + ), + + [RP1_CLK_UART] = REGISTER_CLK( + .name = "clk_uart", + .parents = {"pll_sys_pri_ph", + "pll_video", + "xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 9, + .ctrl_reg = CLK_UART_CTRL, + .div_int_reg = CLK_UART_DIV_INT, + .sel_reg = CLK_UART_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(6, 7), + ), + + [RP1_CLK_ETH] = REGISTER_CLK( + .name = "clk_eth", + .parents = {"pll_sys_sec", + "pll_sys", + "pll_video_sec", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 9, + .ctrl_reg = CLK_ETH_CTRL, + .div_int_reg = CLK_ETH_DIV_INT, + .sel_reg = CLK_ETH_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 125 * MHz, + .fc0_src = FC_NUM(4, 6), + ), + + [RP1_CLK_PWM0] = REGISTER_CLK( + .name = "clk_pwm0", + .parents = {"", // "pll_audio_pri_ph", + "pll_video_sec", + "xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 9, + .ctrl_reg = CLK_PWM0_CTRL, + .div_int_reg = CLK_PWM0_DIV_INT, + .div_frac_reg = CLK_PWM0_DIV_FRAC, + .sel_reg = CLK_PWM0_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 76800 * KHz, + .fc0_src = FC_NUM(0, 5), + ), + + [RP1_CLK_PWM1] = REGISTER_CLK( + .name = "clk_pwm1", + .parents = {"", // "pll_audio_pri_ph", + "pll_video_sec", + "xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 9, + .ctrl_reg = CLK_PWM1_CTRL, + .div_int_reg = CLK_PWM1_DIV_INT, + .div_frac_reg = CLK_PWM1_DIV_FRAC, + .sel_reg = CLK_PWM1_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 76800 * KHz, + .fc0_src = FC_NUM(1, 5), + ), + + [RP1_CLK_AUDIO_IN] = REGISTER_CLK( + .name = "clk_audio_in", + .parents = {"", //"pll_audio", + "", //"pll_audio_pri_ph", + "", //"pll_audio_sec", + "pll_video_sec", + "xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 11, + .ctrl_reg = CLK_AUDIO_IN_CTRL, + .div_int_reg = CLK_AUDIO_IN_DIV_INT, + .sel_reg = CLK_AUDIO_IN_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 76800 * KHz, + .fc0_src = FC_NUM(2, 5), + ), + + [RP1_CLK_AUDIO_OUT] = REGISTER_CLK( + .name = "clk_audio_out", + .parents = {"", //"pll_audio", + "", //"pll_audio_sec", + "pll_video_sec", + "xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 10, + .ctrl_reg = CLK_AUDIO_OUT_CTRL, + .div_int_reg = CLK_AUDIO_OUT_DIV_INT, + .sel_reg = CLK_AUDIO_OUT_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 153600 * KHz, + .fc0_src = FC_NUM(3, 5), + ), + + [RP1_CLK_I2S] = REGISTER_CLK( + .name = "clk_i2s", + .parents = {"xosc", + "pll_audio", + "pll_audio_sec", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 9, + .ctrl_reg = CLK_I2S_CTRL, + .div_int_reg = CLK_I2S_DIV_INT, + .sel_reg = CLK_I2S_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(4, 4), + .flags = CLK_SET_RATE_PARENT, + ), + + [RP1_CLK_MIPI0_CFG] = REGISTER_CLK( + .name = "clk_mipi0_cfg", + .parents = {"xosc"}, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_MIPI0_CFG_CTRL, + .div_int_reg = CLK_MIPI0_CFG_DIV_INT, + .sel_reg = CLK_MIPI0_CFG_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(4, 5), + ), + + [RP1_CLK_MIPI1_CFG] = REGISTER_CLK( + .name = "clk_mipi1_cfg", + .parents = {"xosc"}, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_MIPI1_CFG_CTRL, + .div_int_reg = CLK_MIPI1_CFG_DIV_INT, + .sel_reg = CLK_MIPI1_CFG_SEL, + .clk_src_mask = 1, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(5, 6), + ), + + [RP1_CLK_ETH_TSU] = REGISTER_CLK( + .name = "clk_eth_tsu", + .parents = {"xosc", + "pll_video_sec", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = CLK_ETH_TSU_CTRL, + .div_int_reg = CLK_ETH_TSU_DIV_INT, + .sel_reg = CLK_ETH_TSU_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(5, 7), + ), + + [RP1_CLK_ADC] = REGISTER_CLK( + .name = "clk_adc", + .parents = {"xosc", + "", //"pll_audio_tern", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5"}, + .num_std_parents = 0, + .num_aux_parents = 8, + .ctrl_reg = CLK_ADC_CTRL, + .div_int_reg = CLK_ADC_DIV_INT, + .sel_reg = CLK_ADC_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(5, 5), + ), + + [RP1_CLK_SDIO_TIMER] = REGISTER_CLK( + .name = "clk_sdio_timer", + .parents = {"xosc"}, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_SDIO_TIMER_CTRL, + .div_int_reg = CLK_SDIO_TIMER_DIV_INT, + .sel_reg = CLK_SDIO_TIMER_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(3, 4), + ), + + [RP1_CLK_SDIO_ALT_SRC] = REGISTER_CLK( + .name = "clk_sdio_alt_src", + .parents = {"pll_sys"}, + .num_std_parents = 0, + .num_aux_parents = 1, + .ctrl_reg = CLK_SDIO_ALT_SRC_CTRL, + .div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT, + .sel_reg = CLK_SDIO_ALT_SRC_SEL, + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(5, 4), + ), + + [RP1_CLK_GP0] = REGISTER_CLK( + .name = "clk_gp0", + .parents = {"xosc", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5", + "pll_sys", + "", //"pll_audio", + "", + "", + "clk_i2s", + "clk_adc", + "", + "", + "", + "clk_sys"}, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(0), + .ctrl_reg = CLK_GP0_CTRL, + .div_int_reg = CLK_GP0_DIV_INT, + .div_frac_reg = CLK_GP0_DIV_FRAC, + .sel_reg = CLK_GP0_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(0, 1), + ), + + [RP1_CLK_GP1] = REGISTER_CLK( + .name = "clk_gp1", + .parents = {"clk_sdio_timer", + "clksrc_gp0", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5", + "pll_sys_pri_ph", + "", //"pll_audio_pri_ph", + "", + "", + "clk_adc", + "clk_dpi", + "clk_pwm0", + "", + "", + ""}, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(1), + .ctrl_reg = CLK_GP1_CTRL, + .div_int_reg = CLK_GP1_DIV_INT, + .div_frac_reg = CLK_GP1_DIV_FRAC, + .sel_reg = CLK_GP1_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(1, 1), + ), + + [RP1_CLK_GP2] = REGISTER_CLK( + .name = "clk_gp2", + .parents = {"clk_sdio_alt_src", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp3", + "clksrc_gp4", + "clksrc_gp5", + "pll_sys_sec", + "", //"pll_audio_sec", + "pll_video", + "clk_audio_in", + "clk_dpi", + "clk_pwm0", + "clk_pwm1", + "clk_mipi0_dpi", + "clk_mipi1_cfg", + "clk_sys"}, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(2), + .ctrl_reg = CLK_GP2_CTRL, + .div_int_reg = CLK_GP2_DIV_INT, + .div_frac_reg = CLK_GP2_DIV_FRAC, + .sel_reg = CLK_GP2_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(2, 1), + ), + + [RP1_CLK_GP3] = REGISTER_CLK( + .name = "clk_gp3", + .parents = {"xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp4", + "clksrc_gp5", + "", + "", + "pll_video_pri_ph", + "clk_audio_out", + "", + "", + "clk_mipi1_dpi", + "", + "", + ""}, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(3), + .ctrl_reg = CLK_GP3_CTRL, + .div_int_reg = CLK_GP3_DIV_INT, + .div_frac_reg = CLK_GP3_DIV_FRAC, + .sel_reg = CLK_GP3_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(3, 1), + ), + + [RP1_CLK_GP4] = REGISTER_CLK( + .name = "clk_gp4", + .parents = {"xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp5", + "", //"pll_audio_tern", + "pll_video_sec", + "", + "", + "", + "clk_mipi0_cfg", + "clk_uart", + "", + "", + "clk_sys", + }, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(4), + .ctrl_reg = CLK_GP4_CTRL, + .div_int_reg = CLK_GP4_DIV_INT, + .div_frac_reg = CLK_GP4_DIV_FRAC, + .sel_reg = CLK_GP4_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(4, 1), + ), + + [RP1_CLK_GP5] = REGISTER_CLK( + .name = "clk_gp5", + .parents = {"xosc", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4", + "", //"pll_audio_tern", + "pll_video_sec", + "clk_eth_tsu", + "", + "clk_vec", + "", + "", + "", + "", + ""}, + .num_std_parents = 0, + .num_aux_parents = 16, + .oe_mask = BIT(5), + .ctrl_reg = CLK_GP5_CTRL, + .div_int_reg = CLK_GP5_DIV_INT, + .div_frac_reg = CLK_GP5_DIV_FRAC, + .sel_reg = CLK_GP5_SEL, + .div_int_max = DIV_INT_16BIT_MAX, + .max_freq = 100 * MHz, + .fc0_src = FC_NUM(5, 1), + ), + + [RP1_CLK_VEC] = REGISTER_CLK( + .name = "clk_vec", + .parents = {"pll_sys_pri_ph", + "pll_video_sec", + "pll_video", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4"}, + .num_std_parents = 0, + .num_aux_parents = 8, /* XXX in fact there are more than 8 */ + .ctrl_reg = VIDEO_CLK_VEC_CTRL, + .div_int_reg = VIDEO_CLK_VEC_DIV_INT, + .sel_reg = VIDEO_CLK_VEC_SEL, + .flags = CLK_SET_RATE_NO_REPARENT, /* Let VEC driver set parent */ + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 108 * MHz, + .fc0_src = FC_NUM(0, 6), + ), + + [RP1_CLK_DPI] = REGISTER_CLK( + .name = "clk_dpi", + .parents = {"pll_sys", + "pll_video_sec", + "pll_video", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3", + "clksrc_gp4"}, + .num_std_parents = 0, + .num_aux_parents = 8, /* XXX in fact there are more than 8 */ + .ctrl_reg = VIDEO_CLK_DPI_CTRL, + .div_int_reg = VIDEO_CLK_DPI_DIV_INT, + .sel_reg = VIDEO_CLK_DPI_SEL, + .flags = CLK_SET_RATE_NO_REPARENT, /* Let DPI driver set parent */ + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(1, 6), + ), + + [RP1_CLK_MIPI0_DPI] = REGISTER_CLK( + .name = "clk_mipi0_dpi", + .parents = {"pll_sys", + "pll_video_sec", + "pll_video", + "clksrc_mipi0_dsi_byteclk", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3"}, + .num_std_parents = 0, + .num_aux_parents = 8, /* XXX in fact there are more than 8 */ + .ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL, + .div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT, + .div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC, + .sel_reg = VIDEO_CLK_MIPI0_DPI_SEL, + .flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */ + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(2, 6), + ), + + [RP1_CLK_MIPI1_DPI] = REGISTER_CLK( + .name = "clk_mipi1_dpi", + .parents = {"pll_sys", + "pll_video_sec", + "pll_video", + "clksrc_mipi1_dsi_byteclk", + "clksrc_gp0", + "clksrc_gp1", + "clksrc_gp2", + "clksrc_gp3"}, + .num_std_parents = 0, + .num_aux_parents = 8, /* XXX in fact there are more than 8 */ + .ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL, + .div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT, + .div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC, + .sel_reg = VIDEO_CLK_MIPI1_DPI_SEL, + .flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */ + .div_int_max = DIV_INT_8BIT_MAX, + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(3, 6), + ), +}; + +static bool rp1_clk_claimed[ARRAY_SIZE(clk_desc_array)]; + +static bool rp1_clk_is_claimed(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) { + if (clk_desc_array[i].data) { + const char *clk_name = *(const char **)(clk_desc_array[i].data); + + if (!strcmp(name, clk_name)) + return rp1_clk_claimed[i]; + } + } + + return false; +} + +static int rp1_clk_probe(struct platform_device *pdev) +{ + const struct rp1_clk_desc *desc; + struct device *dev = &pdev->dev; + struct rp1_clockman *clockman; + struct resource *res; + struct clk_hw **hws; + const size_t asize = ARRAY_SIZE(clk_desc_array); + u32 chip_id, platform; + unsigned int i; + u32 clk_id; + int ret; + + clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize), + GFP_KERNEL); + if (!clockman) + return -ENOMEM; + + rp1_get_platform(&chip_id, &platform); + + spin_lock_init(&clockman->regs_lock); + clockman->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + clockman->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(clockman->regs)) + return PTR_ERR(clockman->regs); + + memset(rp1_clk_claimed, 0, sizeof(rp1_clk_claimed)); + for (i = 0; + !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks", + i, &clk_id); + i++) + rp1_clk_claimed[clk_id] = true; + + platform_set_drvdata(pdev, clockman); + + clockman->onecell.num = asize; + hws = clockman->onecell.hws; + + for (i = 0; i < asize; i++) { + desc = &clk_desc_array[i]; + if (desc->clk_register && desc->data) { + hws[i] = desc->clk_register(clockman, desc->data); + if (!strcmp(clk_hw_get_name(hws[i]), "clk_i2s")) { + clk_i2s = hws[i]; + clk_xosc = clk_hw_get_parent_by_index(clk_i2s, 0); + clk_audio = clk_hw_get_parent_by_index(clk_i2s, 1); + } + } + } + + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + &clockman->onecell); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id rp1_clk_of_match[] = { + { .compatible = "raspberrypi,rp1-clocks" }, + {} +}; +MODULE_DEVICE_TABLE(of, rp1_clk_of_match); + +static struct platform_driver rp1_clk_driver = { + .driver = { + .name = "rp1-clk", + .of_match_table = rp1_clk_of_match, + }, + .probe = rp1_clk_probe, +}; + +static int __init __rp1_clk_driver_init(void) +{ + return platform_driver_register(&rp1_clk_driver); +} +postcore_initcall(__rp1_clk_driver_init); + +MODULE_AUTHOR("Naushir Patuck "); +MODULE_DESCRIPTION("RP1 clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 630dfbb01a40e..e159f976a6b1e 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -18,6 +18,7 @@ * Copyright 2012 Marvell International Ltd. */ #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +38,13 @@ #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14 #define BCM2835_DMA_CHAN_NAME_SIZE 8 +#define BCM2835_DMA_BULK_MASK BIT(0) +#define BCM2711_DMA_MEMCPY_CHAN 14 + +struct bcm2835_dma_cfg_data { + u64 dma_mask; + u32 chan_40bit_mask; +}; /** * struct bcm2835_dmadev - BCM2835 DMA controller @@ -48,6 +57,7 @@ struct bcm2835_dmadev { struct dma_device ddev; void __iomem *base; dma_addr_t zero_page; + const struct bcm2835_dma_cfg_data *cfg_data; }; struct bcm2835_dma_cb { @@ -60,6 +70,17 @@ struct bcm2835_dma_cb { uint32_t pad[2]; }; +struct bcm2711_dma40_scb { + uint32_t ti; + uint32_t src; + uint32_t srci; + uint32_t dst; + uint32_t dsti; + uint32_t len; + uint32_t next_cb; + uint32_t rsvd; +}; + struct bcm2835_cb_entry { struct bcm2835_dma_cb *cb; dma_addr_t paddr; @@ -80,6 +101,8 @@ struct bcm2835_chan { unsigned int irq_flags; bool is_lite_channel; + bool is_40bit_channel; + bool is_2712; }; struct bcm2835_desc { @@ -136,11 +159,37 @@ struct bcm2835_desc { #define BCM2835_DMA_S_WIDTH BIT(9) /* 128bit writes if set */ #define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */ #define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */ -#define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12) +#define BCM2835_DMA_BURST_LENGTH(x) (((x) & 15) << 12) +#define BCM2835_DMA_GET_BURST_LENGTH(x) (((x) >> 12) & 15) +#define BCM2835_DMA_CS_FLAGS(x) (x & (BCM2835_DMA_PRIORITY(15) | \ + BCM2835_DMA_PANIC_PRIORITY(15) | \ + BCM2835_DMA_WAIT_FOR_WRITES | \ + BCM2835_DMA_DIS_DEBUG)) #define BCM2835_DMA_PER_MAP(x) ((x & 31) << 16) /* REQ source */ #define BCM2835_DMA_WAIT(x) ((x & 31) << 21) /* add DMA-wait cycles */ #define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */ +/* A fake bit to request that the driver doesn't set the WAIT_RESP bit. */ +#define BCM2835_DMA_NO_WAIT_RESP BIT(27) +#define WAIT_RESP(x) ((x & BCM2835_DMA_NO_WAIT_RESP) ? \ + 0 : BCM2835_DMA_WAIT_RESP) + +/* A fake bit to request that the driver requires wide reads */ +#define BCM2835_DMA_WIDE_SOURCE BIT(24) +#define WIDE_SOURCE(x) ((x & BCM2835_DMA_WIDE_SOURCE) ? \ + BCM2835_DMA_S_WIDTH : 0) + +/* A fake bit to request that the driver requires wide writes */ +#define BCM2835_DMA_WIDE_DEST BIT(25) +#define WIDE_DEST(x) ((x & BCM2835_DMA_WIDE_DEST) ? \ + BCM2835_DMA_D_WIDTH : 0) + +/* A fake bit to request that the driver requires multi-beat burst */ +#define BCM2835_DMA_BURST BIT(30) +#define BURST_LENGTH(x) ((x & BCM2835_DMA_BURST) ? \ + BCM2835_DMA_BURST_LENGTH(3) : 0) + + /* debug register bits */ #define BCM2835_DMA_DEBUG_LAST_NOT_SET_ERR BIT(0) #define BCM2835_DMA_DEBUG_FIFO_ERR BIT(1) @@ -165,13 +214,130 @@ struct bcm2835_desc { #define BCM2835_DMA_DATA_TYPE_S128 16 /* Valid only for channels 0 - 14, 15 has its own base address */ -#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ +#define BCM2835_DMA_CHAN_SIZE 0x100 +#define BCM2835_DMA_CHAN(n) ((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */ #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) /* the max dma length for different channels */ #define MAX_DMA_LEN SZ_1G #define MAX_LITE_DMA_LEN (SZ_64K - 4) +/* 40-bit DMA support */ +#define BCM2711_DMA40_CS 0x00 +#define BCM2711_DMA40_CB 0x04 +#define BCM2711_DMA40_DEBUG 0x0c +#define BCM2711_DMA40_TI 0x10 +#define BCM2711_DMA40_SRC 0x14 +#define BCM2711_DMA40_SRCI 0x18 +#define BCM2711_DMA40_DEST 0x1c +#define BCM2711_DMA40_DESTI 0x20 +#define BCM2711_DMA40_LEN 0x24 +#define BCM2711_DMA40_NEXT_CB 0x28 +#define BCM2711_DMA40_DEBUG2 0x2c + +#define BCM2711_DMA40_ACTIVE BIT(0) +#define BCM2711_DMA40_END BIT(1) +#define BCM2711_DMA40_INT BIT(2) +#define BCM2711_DMA40_DREQ BIT(3) /* DREQ state */ +#define BCM2711_DMA40_RD_PAUSED BIT(4) /* Reading is paused */ +#define BCM2711_DMA40_WR_PAUSED BIT(5) /* Writing is paused */ +#define BCM2711_DMA40_DREQ_PAUSED BIT(6) /* Is paused by DREQ flow control */ +#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7) /* Waiting for last write */ +// we always want to run in supervisor mode +#define BCM2711_DMA40_PROT (BIT(8)|BIT(9)) +#define BCM2711_DMA40_ERR BIT(10) +#define BCM2711_DMA40_QOS(x) (((x) & 0x1f) << 16) +#define BCM2711_DMA40_PANIC_QOS(x) (((x) & 0x1f) << 20) +#define BCM2711_DMA40_TRANSACTIONS BIT(25) +#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28) +#define BCM2711_DMA40_DISDEBUG BIT(29) +#define BCM2711_DMA40_ABORT BIT(30) +#define BCM2711_DMA40_HALT BIT(31) + +#define BCM2711_DMA40_CS_FLAGS(x) (x & (BCM2711_DMA40_QOS(15) | \ + BCM2711_DMA40_PANIC_QOS(15) | \ + BCM2711_DMA40_WAIT_FOR_WRITES | \ + BCM2711_DMA40_DISDEBUG)) + +/* Transfer information bits */ +#define BCM2711_DMA40_INTEN BIT(0) +#define BCM2711_DMA40_TDMODE BIT(1) /* 2D-Mode */ +#define BCM2711_DMA40_WAIT_RESP BIT(2) /* wait for AXI write to be acked */ +#define BCM2711_DMA40_WAIT_RD_RESP BIT(3) /* wait for AXI read to complete */ +#define BCM2711_DMA40_PER_MAP(x) ((x & 31) << 9) /* REQ source */ +#define BCM2711_DMA40_S_DREQ BIT(14) /* enable SREQ for source */ +#define BCM2711_DMA40_D_DREQ BIT(15) /* enable DREQ for destination */ +#define BCM2711_DMA40_S_WAIT(x) ((x & 0xff) << 16) /* add DMA read-wait cycles */ +#define BCM2711_DMA40_D_WAIT(x) ((x & 0xff) << 24) /* add DMA write-wait cycles */ + +/* debug register bits */ +#define BCM2711_DMA40_DEBUG_WRITE_ERR BIT(0) +#define BCM2711_DMA40_DEBUG_FIFO_ERR BIT(1) +#define BCM2711_DMA40_DEBUG_READ_ERR BIT(2) +#define BCM2711_DMA40_DEBUG_READ_CB_ERR BIT(3) +#define BCM2711_DMA40_DEBUG_IN_ON_ERR BIT(8) +#define BCM2711_DMA40_DEBUG_ABORT_ON_ERR BIT(9) +#define BCM2711_DMA40_DEBUG_HALT_ON_ERR BIT(10) +#define BCM2711_DMA40_DEBUG_DISABLE_CLK_GATE BIT(11) +#define BCM2711_DMA40_DEBUG_RSTATE_SHIFT 14 +#define BCM2711_DMA40_DEBUG_RSTATE_BITS 4 +#define BCM2711_DMA40_DEBUG_WSTATE_SHIFT 18 +#define BCM2711_DMA40_DEBUG_WSTATE_BITS 4 +#define BCM2711_DMA40_DEBUG_RESET BIT(23) +#define BCM2711_DMA40_DEBUG_ID_SHIFT 24 +#define BCM2711_DMA40_DEBUG_ID_BITS 4 +#define BCM2711_DMA40_DEBUG_VERSION_SHIFT 28 +#define BCM2711_DMA40_DEBUG_VERSION_BITS 4 + +/* Valid only for channels 0 - 3 (11 - 14) */ +#define BCM2711_DMA40_CHAN(n) (((n) + 11) << 8) /* Base address */ +#define BCM2711_DMA40_CHANIO(base, n) ((base) + BCM2711_DMA_CHAN(n)) + +/* the max dma length for different channels */ +#define MAX_DMA40_LEN SZ_1G + +#define BCM2711_DMA40_BURST_LEN(x) (((x) & 15) << 8) +#define BCM2711_DMA40_INC BIT(12) +#define BCM2711_DMA40_SIZE_32 (0 << 13) +#define BCM2711_DMA40_SIZE_64 (1 << 13) +#define BCM2711_DMA40_SIZE_128 (2 << 13) +#define BCM2711_DMA40_SIZE_256 (3 << 13) +#define BCM2711_DMA40_IGNORE BIT(15) +#define BCM2711_DMA40_STRIDE(x) ((x) << 16) /* For 2D mode */ + +#define BCM2711_DMA40_MEMCPY_FLAGS \ + (BCM2711_DMA40_QOS(0) | \ + BCM2711_DMA40_PANIC_QOS(0) | \ + BCM2711_DMA40_WAIT_FOR_WRITES | \ + BCM2711_DMA40_DISDEBUG) + +#define BCM2711_DMA40_MEMCPY_XFER_INFO \ + (BCM2711_DMA40_SIZE_128 | \ + BCM2711_DMA40_INC | \ + BCM2711_DMA40_BURST_LEN(16)) + +struct bcm2835_dmadev *memcpy_parent; +static void __iomem *memcpy_chan; +static struct bcm2711_dma40_scb *memcpy_scb; +static dma_addr_t memcpy_scb_dma; +DEFINE_SPINLOCK(memcpy_lock); + +static const struct bcm2835_dma_cfg_data bcm2835_dma_cfg = { + .chan_40bit_mask = 0, + .dma_mask = DMA_BIT_MASK(32), +}; + +static const struct bcm2835_dma_cfg_data bcm2711_dma_cfg = { + .chan_40bit_mask = BIT(11) | BIT(12) | BIT(13) | BIT(14), + .dma_mask = DMA_BIT_MASK(36), +}; + +static const struct bcm2835_dma_cfg_data bcm2712_dma_cfg = { + .chan_40bit_mask = BIT(6) | BIT(7) | BIT(8) | BIT(9) | + BIT(10) | BIT(11), + .dma_mask = DMA_BIT_MASK(40), +}; + static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c) { /* lite and normal channels have different max frame length */ @@ -201,6 +367,36 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc( return container_of(t, struct bcm2835_desc, vd.tx); } +static inline uint32_t to_bcm2711_ti(uint32_t info) +{ + return ((info & BCM2835_DMA_INT_EN) ? BCM2711_DMA40_INTEN : 0) | + ((info & BCM2835_DMA_WAIT_RESP) ? BCM2711_DMA40_WAIT_RESP : 0) | + ((info & BCM2835_DMA_S_DREQ) ? + (BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP) : 0) | + ((info & BCM2835_DMA_D_DREQ) ? BCM2711_DMA40_D_DREQ : 0) | + BCM2711_DMA40_PER_MAP((info >> 16) & 0x1f); +} + +static inline uint32_t to_bcm2711_srci(uint32_t info) +{ + return ((info & BCM2835_DMA_S_INC) ? BCM2711_DMA40_INC : 0) | + ((info & BCM2835_DMA_S_WIDTH) ? BCM2711_DMA40_SIZE_128 : 0) | + BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info)); +} + +static inline uint32_t to_bcm2711_dsti(uint32_t info) +{ + return ((info & BCM2835_DMA_D_INC) ? BCM2711_DMA40_INC : 0) | + ((info & BCM2835_DMA_D_WIDTH) ? BCM2711_DMA40_SIZE_128 : 0) | + BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info)); +} + +static inline uint32_t to_40bit_cbaddr(dma_addr_t addr) +{ + BUG_ON(addr & 0x1f); + return (addr >> 5); +} + static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc) { size_t i; @@ -219,45 +415,53 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) } static void bcm2835_dma_create_cb_set_length( - struct bcm2835_chan *chan, + struct bcm2835_chan *c, struct bcm2835_dma_cb *control_block, size_t len, size_t period_len, size_t *total_len, u32 finalextrainfo) { - size_t max_len = bcm2835_dma_max_frame_length(chan); + size_t max_len = bcm2835_dma_max_frame_length(c); + uint32_t cb_len; /* set the length taking lite-channel limitations into account */ - control_block->length = min_t(u32, len, max_len); + cb_len = min_t(u32, len, max_len); - /* finished if we have no period_length */ - if (!period_len) - return; + if (period_len) { + /* + * period_len means: that we need to generate + * transfers that are terminating at every + * multiple of period_len - this is typically + * used to set the interrupt flag in info + * which is required during cyclic transfers + */ - /* - * period_len means: that we need to generate - * transfers that are terminating at every - * multiple of period_len - this is typically - * used to set the interrupt flag in info - * which is required during cyclic transfers - */ + /* have we filled in period_length yet? */ + if (*total_len + cb_len < period_len) { + /* update number of bytes in this period so far */ + *total_len += cb_len; + } else { + /* calculate the length that remains to reach period_len */ + cb_len = period_len - *total_len; - /* have we filled in period_length yet? */ - if (*total_len + control_block->length < period_len) { - /* update number of bytes in this period so far */ - *total_len += control_block->length; - return; + /* reset total_length for next period */ + *total_len = 0; + } } - /* calculate the length that remains to reach period_length */ - control_block->length = period_len - *total_len; + if (c->is_40bit_channel) { + struct bcm2711_dma40_scb *scb = + (struct bcm2711_dma40_scb *)control_block; - /* reset total_length for next period */ - *total_len = 0; - - /* add extrainfo bits in info */ - control_block->info |= finalextrainfo; + scb->len = cb_len; + /* add extrainfo bits to ti */ + scb->ti |= to_bcm2711_ti(finalextrainfo); + } else { + control_block->length = cb_len; + /* add extrainfo bits to info */ + control_block->info |= finalextrainfo; + } } static inline size_t bcm2835_dma_count_frames_for_sg( @@ -280,7 +484,7 @@ static inline size_t bcm2835_dma_count_frames_for_sg( /** * bcm2835_dma_create_cb_chain - create a control block and fills data in * - * @chan: the @dma_chan for which we run this + * @c: the @bcm2835_chan for which we run this * @direction: the direction in which we transfer * @cyclic: it is a cyclic transfer * @info: the default info bits to apply per controlblock @@ -298,12 +502,11 @@ static inline size_t bcm2835_dma_count_frames_for_sg( * @gfp: the GFP flag to use for allocation */ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( - struct dma_chan *chan, enum dma_transfer_direction direction, + struct bcm2835_chan *c, enum dma_transfer_direction direction, bool cyclic, u32 info, u32 finalextrainfo, size_t frames, dma_addr_t src, dma_addr_t dst, size_t buf_len, size_t period_len, gfp_t gfp) { - struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); size_t len = buf_len, total_len; size_t frame; struct bcm2835_desc *d; @@ -335,11 +538,27 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( /* fill in the control block */ control_block = cb_entry->cb; - control_block->info = info; - control_block->src = src; - control_block->dst = dst; - control_block->stride = 0; - control_block->next = 0; + if (c->is_40bit_channel) { + struct bcm2711_dma40_scb *scb = + (struct bcm2711_dma40_scb *)control_block; + scb->ti = to_bcm2711_ti(info); + scb->src = lower_32_bits(src); + scb->srci= upper_32_bits(src) | to_bcm2711_srci(info); + scb->dst = lower_32_bits(dst); + scb->dsti = upper_32_bits(dst) | to_bcm2711_dsti(info); + scb->next_cb = 0; + } else { + control_block->info = info; + control_block->src = src; + control_block->dst = dst; + if (c->is_2712) + control_block->stride = (upper_32_bits(dst) << 8) | + upper_32_bits(src); + else + control_block->stride = 0; + control_block->next = 0; + } + /* set up length in control_block if requested */ if (buf_len) { /* calculate length honoring period_length */ @@ -349,25 +568,52 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( cyclic ? finalextrainfo : 0); /* calculate new remaining length */ - len -= control_block->length; + if (c->is_40bit_channel) + len -= ((struct bcm2711_dma40_scb *)control_block)->len; + else + len -= control_block->length; } /* link this the last controlblock */ - if (frame) - d->cb_list[frame - 1].cb->next = cb_entry->paddr; + if (frame && c->is_40bit_channel) + ((struct bcm2711_dma40_scb *) + d->cb_list[frame - 1].cb)->next_cb = + to_40bit_cbaddr(cb_entry->paddr); + if (frame && !c->is_40bit_channel) + d->cb_list[frame - 1].cb->next = c->is_2712 ? + to_40bit_cbaddr(cb_entry->paddr) : cb_entry->paddr; /* update src and dst and length */ - if (src && (info & BCM2835_DMA_S_INC)) - src += control_block->length; - if (dst && (info & BCM2835_DMA_D_INC)) - dst += control_block->length; + if (src && (info & BCM2835_DMA_S_INC)) { + if (c->is_40bit_channel) + src += ((struct bcm2711_dma40_scb *)control_block)->len; + else + src += control_block->length; + } + + if (dst && (info & BCM2835_DMA_D_INC)) { + if (c->is_40bit_channel) + dst += ((struct bcm2711_dma40_scb *)control_block)->len; + else + dst += control_block->length; + } /* Length of total transfer */ - d->size += control_block->length; + if (c->is_40bit_channel) + d->size += ((struct bcm2711_dma40_scb *)control_block)->len; + else + d->size += control_block->length; } /* the last frame requires extra flags */ - d->cb_list[d->frames - 1].cb->info |= finalextrainfo; + if (c->is_40bit_channel) { + struct bcm2711_dma40_scb *scb = + (struct bcm2711_dma40_scb *)d->cb_list[d->frames-1].cb; + + scb->ti |= to_bcm2711_ti(finalextrainfo); + } else { + d->cb_list[d->frames - 1].cb->info |= finalextrainfo; + } /* detect a size missmatch */ if (buf_len && (d->size != buf_len)) @@ -381,13 +627,12 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( } static void bcm2835_dma_fill_cb_chain_with_sg( - struct dma_chan *chan, + struct bcm2835_chan *c, enum dma_transfer_direction direction, struct bcm2835_cb_entry *cb, struct scatterlist *sgl, unsigned int sg_len) { - struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); size_t len, max_len; unsigned int i; dma_addr_t addr; @@ -395,14 +640,35 @@ static void bcm2835_dma_fill_cb_chain_with_sg( max_len = bcm2835_dma_max_frame_length(c); for_each_sg(sgl, sgent, sg_len, i) { - for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent); - len > 0; - addr += cb->cb->length, len -= cb->cb->length, cb++) { - if (direction == DMA_DEV_TO_MEM) - cb->cb->dst = addr; - else - cb->cb->src = addr; - cb->cb->length = min(len, max_len); + if (c->is_40bit_channel) { + struct bcm2711_dma40_scb *scb; + + for (addr = sg_dma_address(sgent), + len = sg_dma_len(sgent); + len > 0; + addr += scb->len, len -= scb->len, cb++) { + scb = (struct bcm2711_dma40_scb *)cb->cb; + if (direction == DMA_DEV_TO_MEM) { + scb->dst = lower_32_bits(addr); + scb->dsti = upper_32_bits(addr) | BCM2711_DMA40_INC; + } else { + scb->src = lower_32_bits(addr); + scb->srci = upper_32_bits(addr) | BCM2711_DMA40_INC; + } + scb->len = min(len, max_len); + } + } else { + for (addr = sg_dma_address(sgent), + len = sg_dma_len(sgent); + len > 0; + addr += cb->cb->length, len -= cb->cb->length, + cb++) { + if (direction == DMA_DEV_TO_MEM) + cb->cb->dst = addr; + else + cb->cb->src = addr; + cb->cb->length = min(len, max_len); + } } } } @@ -410,29 +676,74 @@ static void bcm2835_dma_fill_cb_chain_with_sg( static void bcm2835_dma_abort(struct bcm2835_chan *c) { void __iomem *chan_base = c->chan_base; - long int timeout = 10000; + long timeout = 100; - /* - * A zero control block address means the channel is idle. - * (The ACTIVE flag in the CS register is not a reliable indicator.) - */ - if (!readl(chan_base + BCM2835_DMA_ADDR)) - return; + if (c->is_40bit_channel) { + /* + * A zero control block address means the channel is idle. + * (The ACTIVE flag in the CS register is not a reliable indicator.) + */ + if (!readl(chan_base + BCM2711_DMA40_CB)) + return; + + /* Pause the current DMA */ + writel(readl(chan_base + BCM2711_DMA40_CS) & ~BCM2711_DMA40_ACTIVE, + chan_base + BCM2711_DMA40_CS); + + /* wait for outstanding transactions to complete */ + while ((readl(chan_base + BCM2711_DMA40_CS) & BCM2711_DMA40_TRANSACTIONS) && + --timeout) + cpu_relax(); + + /* Peripheral might be stuck and fail to complete */ + if (!timeout) + dev_err(c->vc.chan.device->dev, + "failed to complete pause on dma %d (CS:%08x)\n", c->ch, + readl(chan_base + BCM2711_DMA40_CS)); + + /* Set CS back to default state */ + writel(BCM2711_DMA40_PROT, chan_base + BCM2711_DMA40_CS); + + /* Reset the DMA */ + writel(readl(chan_base + BCM2711_DMA40_DEBUG) | BCM2711_DMA40_DEBUG_RESET, + chan_base + BCM2711_DMA40_DEBUG); + } else { + /* + * A zero control block address means the channel is idle. + * (The ACTIVE flag in the CS register is not a reliable indicator.) + */ + if (!readl(chan_base + BCM2835_DMA_ADDR)) + return; - /* Write 0 to the active bit - Pause the DMA */ - writel(0, chan_base + BCM2835_DMA_CS); + /* We need to clear the next DMA block pending */ + writel(0, chan_base + BCM2835_DMA_NEXTCB); - /* Wait for any current AXI transfer to complete */ - while ((readl(chan_base + BCM2835_DMA_CS) & - BCM2835_DMA_WAITING_FOR_WRITES) && --timeout) - cpu_relax(); + /* Abort the DMA, which needs to be enabled to complete */ + writel(readl(chan_base + BCM2835_DMA_CS) | BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, + chan_base + BCM2835_DMA_CS); + + /* wait for DMA to be aborted */ + while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout) + cpu_relax(); - /* Peripheral might be stuck and fail to signal AXI write responses */ - if (!timeout) - dev_err(c->vc.chan.device->dev, - "failed to complete outstanding writes\n"); + /* Write 0 to the active bit - Pause the DMA */ + writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE, + chan_base + BCM2835_DMA_CS); - writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS); + /* + * Peripheral might be stuck and fail to complete + * This is expected when dreqs are enabled but not asserted + * so only report error in non dreq case + */ + if (!timeout && !(readl(chan_base + BCM2835_DMA_TI) & + (BCM2835_DMA_S_DREQ | BCM2835_DMA_D_DREQ))) + dev_err(c->vc.chan.device->dev, + "failed to complete pause on dma %d (CS:%08x)\n", c->ch, + readl(chan_base + BCM2835_DMA_CS)); + + /* Set CS back to default state and reset the DMA */ + writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS); + } } static void bcm2835_dma_start_desc(struct bcm2835_chan *c) @@ -449,8 +760,19 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c) c->desc = d = to_bcm2835_dma_desc(&vd->tx); - writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR); - writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); + if (c->is_40bit_channel) { + writel(to_40bit_cbaddr(d->cb_list[0].paddr), + c->chan_base + BCM2711_DMA40_CB); + writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | BCM2711_DMA40_CS_FLAGS(c->dreq), + c->chan_base + BCM2711_DMA40_CS); + } else { + writel(BIT(31), c->chan_base + BCM2835_DMA_CS); + + writel(c->is_2712 ? to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr, + c->chan_base + BCM2835_DMA_ADDR); + writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq), + c->chan_base + BCM2835_DMA_CS); + } } static irqreturn_t bcm2835_dma_callback(int irq, void *data) @@ -477,8 +799,13 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data) * if this IRQ handler is threaded.) If the channel is finished, it * will remain idle despite the ACTIVE flag being set. */ - writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE, - c->chan_base + BCM2835_DMA_CS); + if (c->is_40bit_channel) + writel(BCM2835_DMA_INT | BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | + BCM2711_DMA40_CS_FLAGS(c->dreq), + c->chan_base + BCM2711_DMA40_CS); + else + writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq), + c->chan_base + BCM2835_DMA_CS); d = c->desc; @@ -540,20 +867,39 @@ static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) unsigned int i; size_t size; - for (size = i = 0; i < d->frames; i++) { - struct bcm2835_dma_cb *control_block = d->cb_list[i].cb; - size_t this_size = control_block->length; - dma_addr_t dma; + if (d->c->is_40bit_channel) { + for (size = i = 0; i < d->frames; i++) { + struct bcm2711_dma40_scb *control_block = + (struct bcm2711_dma40_scb *)d->cb_list[i].cb; + size_t this_size = control_block->len; + dma_addr_t dma; - if (d->dir == DMA_DEV_TO_MEM) - dma = control_block->dst; - else - dma = control_block->src; + if (d->dir == DMA_DEV_TO_MEM) + dma = control_block->dst; + else + dma = control_block->src; - if (size) - size += this_size; - else if (addr >= dma && addr < dma + this_size) - size += dma + this_size - addr; + if (size) + size += this_size; + else if (addr >= dma && addr < dma + this_size) + size += dma + this_size - addr; + } + } else { + for (size = i = 0; i < d->frames; i++) { + struct bcm2835_dma_cb *control_block = d->cb_list[i].cb; + size_t this_size = control_block->length; + dma_addr_t dma; + + if (d->dir == DMA_DEV_TO_MEM) + dma = control_block->dst; + else + dma = control_block->src; + + if (size) + size += this_size; + else if (addr >= dma && addr < dma + this_size) + size += dma + this_size - addr; + } } return size; @@ -580,12 +926,25 @@ static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, struct bcm2835_desc *d = c->desc; dma_addr_t pos; - if (d->dir == DMA_MEM_TO_DEV) + if (d->dir == DMA_MEM_TO_DEV && c->is_40bit_channel) { + u64 lo_bits, hi_bits; + + lo_bits = readl(c->chan_base + BCM2711_DMA40_SRC); + hi_bits = readl(c->chan_base + BCM2711_DMA40_SRCI) & 0xff; + pos = (hi_bits << 32) | lo_bits; + } else if (d->dir == DMA_MEM_TO_DEV && !c->is_40bit_channel) { pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); - else if (d->dir == DMA_DEV_TO_MEM) + } else if (d->dir == DMA_DEV_TO_MEM && c->is_40bit_channel) { + u64 lo_bits, hi_bits; + + lo_bits = readl(c->chan_base + BCM2711_DMA40_DEST); + hi_bits = readl(c->chan_base + BCM2711_DMA40_DESTI) & 0xff; + pos = (hi_bits << 32) | lo_bits; + } else if (d->dir == DMA_DEV_TO_MEM && !c->is_40bit_channel) { pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); - else + } else { pos = 0; + } txstate->residue = bcm2835_dma_desc_size_pos(d, pos); } else { @@ -615,8 +974,10 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy( { struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); struct bcm2835_desc *d; - u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC; - u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP; + u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC | + WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | + WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq); + u32 extra = BCM2835_DMA_INT_EN; size_t max_len = bcm2835_dma_max_frame_length(c); size_t frames; @@ -628,7 +989,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy( frames = bcm2835_dma_frames_for_length(len, max_len); /* allocate the CB chain - this also fills in the pointers */ - d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false, + d = bcm2835_dma_create_cb_chain(c, DMA_MEM_TO_MEM, false, info, extra, frames, src, dst, len, 0, GFP_KERNEL); if (!d) @@ -646,7 +1007,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); struct bcm2835_desc *d; dma_addr_t src = 0, dst = 0; - u32 info = BCM2835_DMA_WAIT_RESP; + u32 info = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | + WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq); u32 extra = BCM2835_DMA_INT_EN; size_t frames; @@ -662,12 +1024,12 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( if (direction == DMA_DEV_TO_MEM) { if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL; - src = c->cfg.src_addr; + src = phys_to_dma(chan->device->dev, c->cfg.src_addr); info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC; } else { if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL; - dst = c->cfg.dst_addr; + dst = phys_to_dma(chan->device->dev, c->cfg.dst_addr); info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; } @@ -675,7 +1037,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len); /* allocate the CB chain */ - d = bcm2835_dma_create_cb_chain(chan, direction, false, + d = bcm2835_dma_create_cb_chain(c, direction, false, info, extra, frames, src, dst, 0, 0, GFP_NOWAIT); @@ -683,7 +1045,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( return NULL; /* fill in frames with scatterlist pointers */ - bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list, + bcm2835_dma_fill_cb_chain_with_sg(c, direction, d->cb_list, sgl, sg_len); return vchan_tx_prep(&c->vc, &d->vd, flags); @@ -698,7 +1060,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); struct bcm2835_desc *d; dma_addr_t src, dst; - u32 info = BCM2835_DMA_WAIT_RESP; + u32 info = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | + WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq); u32 extra = 0; size_t max_len = bcm2835_dma_max_frame_length(c); size_t frames; @@ -736,13 +1099,13 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( if (direction == DMA_DEV_TO_MEM) { if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL; - src = c->cfg.src_addr; + src = phys_to_dma(chan->device->dev, c->cfg.src_addr); dst = buf_addr; info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC; } else { if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL; - dst = c->cfg.dst_addr; + dst = phys_to_dma(chan->device->dev, c->cfg.dst_addr); src = buf_addr; info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; @@ -762,7 +1125,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine * implementation calls prep_dma_cyclic with interrupts disabled. */ - d = bcm2835_dma_create_cb_chain(chan, direction, true, + d = bcm2835_dma_create_cb_chain(c, direction, true, info, extra, frames, src, dst, buf_len, period_len, GFP_NOWAIT); @@ -770,7 +1133,13 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( return NULL; /* wrap around into a loop */ - d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr; + if (c->is_40bit_channel) + ((struct bcm2711_dma40_scb *) + d->cb_list[frames - 1].cb)->next_cb = + to_40bit_cbaddr(d->cb_list[0].paddr); + else + d->cb_list[d->frames - 1].cb->next = c->is_2712 ? + to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr; return vchan_tx_prep(&c->vc, &d->vd, flags); } @@ -831,10 +1200,14 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, c->irq_number = irq; c->irq_flags = irq_flags; - /* check in DEBUG register if this is a LITE channel */ - if (readl(c->chan_base + BCM2835_DMA_DEBUG) & - BCM2835_DMA_DEBUG_LITE) + /* check for 40bit and lite channels */ + if (d->cfg_data->chan_40bit_mask & BIT(chan_id)) + c->is_40bit_channel = true; + else if (readl(c->chan_base + BCM2835_DMA_DEBUG) & + BCM2835_DMA_DEBUG_LITE) c->is_lite_channel = true; + if (d->cfg_data->dma_mask == DMA_BIT_MASK(40)) + c->is_2712 = true; return 0; } @@ -853,8 +1226,59 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od) DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); } +int bcm2711_dma40_memcpy_init(void) +{ + if (!memcpy_parent) + return -EPROBE_DEFER; + + if (!memcpy_chan) + return -EINVAL; + + if (!memcpy_scb) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(bcm2711_dma40_memcpy_init); + +void bcm2711_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size) +{ + struct bcm2711_dma40_scb *scb = memcpy_scb; + unsigned long flags; + + if (!scb) { + pr_err("bcm2711_dma40_memcpy not initialised!\n"); + return; + } + + spin_lock_irqsave(&memcpy_lock, flags); + + scb->ti = 0; + scb->src = lower_32_bits(src); + scb->srci = upper_32_bits(src) | BCM2711_DMA40_MEMCPY_XFER_INFO; + scb->dst = lower_32_bits(dst); + scb->dsti = upper_32_bits(dst) | BCM2711_DMA40_MEMCPY_XFER_INFO; + scb->len = size; + scb->next_cb = 0; + + writel(to_40bit_cbaddr(memcpy_scb_dma), memcpy_chan + BCM2711_DMA40_CB); + writel(BCM2711_DMA40_MEMCPY_FLAGS | BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT, + memcpy_chan + BCM2711_DMA40_CS); + + /* Poll for completion */ + while (!(readl(memcpy_chan + BCM2711_DMA40_CS) & BCM2711_DMA40_END)) + cpu_relax(); + + writel(BCM2711_DMA40_END | BCM2711_DMA40_PROT, memcpy_chan + BCM2711_DMA40_CS); + + spin_unlock_irqrestore(&memcpy_lock, flags); +} +EXPORT_SYMBOL(bcm2711_dma40_memcpy); + static const struct of_device_id bcm2835_dma_of_match[] = { - { .compatible = "brcm,bcm2835-dma", }, + { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg }, + { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg }, + { .compatible = "brcm,bcm2712-dma", .data = &bcm2712_dma_cfg }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); @@ -877,6 +1301,8 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, static int bcm2835_dma_probe(struct platform_device *pdev) { + const struct bcm2835_dma_cfg_data *cfg_data; + const struct of_device_id *of_id; struct bcm2835_dmadev *od; struct resource *res; void __iomem *base; @@ -886,11 +1312,20 @@ static int bcm2835_dma_probe(struct platform_device *pdev) int irq_flags; uint32_t chans_available; char chan_name[BCM2835_DMA_CHAN_NAME_SIZE]; + int chan_count, chan_start, chan_end; + + of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node); + if (!of_id) { + dev_err(&pdev->dev, "Failed to match compatible string\n"); + return -EINVAL; + } + + cfg_data = of_id->data; if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + rc = dma_set_mask_and_coherent(&pdev->dev, cfg_data->dma_mask); if (rc) { dev_err(&pdev->dev, "Unable to set DMA mask\n"); return rc; @@ -902,11 +1337,17 @@ static int bcm2835_dma_probe(struct platform_device *pdev) dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base); + /* The set of channels can be split across multiple instances. */ + chan_start = ((u32)(uintptr_t)base / BCM2835_DMA_CHAN_SIZE) & 0xf; + base -= BCM2835_DMA_CHAN(chan_start); + chan_count = resource_size(res) / BCM2835_DMA_CHAN_SIZE; + chan_end = min(chan_start + chan_count, + BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1); + od->base = base; dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); @@ -942,6 +1383,14 @@ static int bcm2835_dma_probe(struct platform_device *pdev) return -ENOMEM; } + of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node); + if (!of_id) { + dev_err(&pdev->dev, "Failed to match compatible string\n"); + return -EINVAL; + } + + od->cfg_data = cfg_data; + /* Request DMA channel mask from device tree */ if (of_property_read_u32(pdev->dev.of_node, "brcm,dma-channel-mask", @@ -951,8 +1400,36 @@ static int bcm2835_dma_probe(struct platform_device *pdev) goto err_no_dma; } +#ifdef CONFIG_DMA_BCM2708 + /* One channel is reserved for the legacy API */ + if (chans_available & BCM2835_DMA_BULK_MASK) { + rc = bcm_dmaman_probe(pdev, base, + chans_available & BCM2835_DMA_BULK_MASK); + if (rc) + dev_err(&pdev->dev, + "Failed to initialize the legacy API\n"); + + chans_available &= ~BCM2835_DMA_BULK_MASK; + } +#endif + + /* And possibly one for the 40-bit DMA memcpy API */ + if (chans_available & od->cfg_data->chan_40bit_mask & + BIT(BCM2711_DMA_MEMCPY_CHAN)) { + memcpy_parent = od; + memcpy_chan = BCM2835_DMA_CHANIO(base, BCM2711_DMA_MEMCPY_CHAN); + memcpy_scb = dma_alloc_coherent(memcpy_parent->ddev.dev, + sizeof(*memcpy_scb), + &memcpy_scb_dma, GFP_KERNEL); + if (!memcpy_scb) + dev_warn(&pdev->dev, + "Failed to allocated memcpy scb\n"); + + chans_available &= ~BIT(BCM2711_DMA_MEMCPY_CHAN); + } + /* get irqs for each channel that we support */ - for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { + for (i = chan_start; i < chan_end; i++) { /* skip masked out channels */ if (!(chans_available & (1 << i))) { irq[i] = -1; @@ -975,13 +1452,17 @@ static int bcm2835_dma_probe(struct platform_device *pdev) irq[i] = platform_get_irq(pdev, i < 11 ? i : 11); } + chan_count = 0; + /* get irqs for each channel */ - for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { + for (i = chan_start; i < chan_end; i++) { /* skip channels without irq */ if (irq[i] < 0) continue; /* check if there are other channels that also use this irq */ + /* FIXME: This will fail if interrupts are shared across + instances */ irq_flags = 0; for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++) if ((i != j) && (irq[j] == irq[i])) { @@ -993,9 +1474,10 @@ static int bcm2835_dma_probe(struct platform_device *pdev) rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags); if (rc) goto err_no_dma; + chan_count++; } - dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); + dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", chan_count); /* Device-tree DMA controller registration */ rc = of_dma_controller_register(pdev->dev.of_node, @@ -1025,7 +1507,15 @@ static int bcm2835_dma_remove(struct platform_device *pdev) { struct bcm2835_dmadev *od = platform_get_drvdata(pdev); + bcm_dmaman_remove(pdev); dma_async_device_unregister(&od->ddev); + if (memcpy_parent == od) { + dma_free_coherent(&pdev->dev, sizeof(*memcpy_scb), memcpy_scb, + memcpy_scb_dma); + memcpy_parent = NULL; + memcpy_scb = NULL; + memcpy_chan = NULL; + } bcm2835_dma_free(od); return 0; @@ -1040,7 +1530,22 @@ static struct platform_driver bcm2835_dma_driver = { }, }; -module_platform_driver(bcm2835_dma_driver); +static int bcm2835_dma_init(void) +{ + return platform_driver_register(&bcm2835_dma_driver); +} + +static void bcm2835_dma_exit(void) +{ + platform_driver_unregister(&bcm2835_dma_driver); +} + +/* + * Load after serial driver (arch_initcall) so we see the messages if it fails, + * but before drivers (module_init) that need a DMA channel. + */ +subsys_initcall(bcm2835_dma_init); +module_exit(bcm2835_dma_exit); MODULE_ALIAS("platform:bcm2835-dma"); MODULE_DESCRIPTION("BCM2835 DMA engine driver"); diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index dc5f7a156ff5e..338b3b81f4aaa 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -506,4 +506,11 @@ config SPRD_IOMMU Say Y here if you want to use the multimedia devices listed above. +config BCM2712_IOMMU + bool "BCM2712 IOMMU driver" + depends on ARM64 && ARCH_BCM + select IOMMU_API + help + IOMMU driver for BCM2712 + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index cc9f381013c35..be13b555dda80 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva-lib.o io-pgfault.o obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o obj-$(CONFIG_APPLE_DART) += apple-dart.o +obj-$(CONFIG_BCM2712_IOMMU) += bcm2712-iommu.o bcm2712-iommu-cache.o diff --git a/drivers/iommu/bcm2712-iommu-cache.c b/drivers/iommu/bcm2712-iommu-cache.c new file mode 100644 index 0000000000000..fdea69f5370b1 --- /dev/null +++ b/drivers/iommu/bcm2712-iommu-cache.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IOMMU driver for BCM2712 + * + * Copyright (c) 2023 Raspberry Pi Ltd. + */ + +#include "bcm2712-iommu.h" + +#include +#include +#include +#include + +#define MMUC_CONTROL_ENABLE 1 +#define MMUC_CONTROL_FLUSH 2 +#define MMUC_CONTROL_FLUSHING 4 + +void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&cache->hw_lock, flags); + if (cache->reg_base) { + /* Enable and flush the TLB cache */ + writel(MMUC_CONTROL_ENABLE | MMUC_CONTROL_FLUSH, + cache->reg_base); + + /* Wait for flush to complete: it should be very quick */ + for (i = 0; i < 1024; i++) { + if (!(MMUC_CONTROL_FLUSHING & readl(cache->reg_base))) + break; + cpu_relax(); + } + } + spin_unlock_irqrestore(&cache->hw_lock, flags); +} + +static int bcm2712_iommu_cache_probe(struct platform_device *pdev) +{ + struct bcm2712_iommu_cache *cache; + + dev_info(&pdev->dev, __func__); + cache = devm_kzalloc(&pdev->dev, sizeof(*cache), GFP_KERNEL); + if (!cache) + return -ENOMEM; + + cache->dev = &pdev->dev; + platform_set_drvdata(pdev, cache); + spin_lock_init(&cache->hw_lock); + + /* Get IOMMUC registers; we only use the first register (IOMMUC_CTRL) */ + cache->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(cache->reg_base)) { + dev_err(&pdev->dev, "Failed to get IOMMU Cache registers address\n"); + cache->reg_base = NULL; + } + return 0; +} + +static const struct of_device_id bcm2712_iommu_cache_of_match[] = { + { + . compatible = "brcm,bcm2712-iommuc" + }, + { /* sentinel */ }, +}; + +static struct platform_driver bcm2712_iommu_cache_driver = { + .probe = bcm2712_iommu_cache_probe, + .driver = { + .name = "bcm2712-iommu-cache", + .of_match_table = bcm2712_iommu_cache_of_match + }, +}; + +builtin_platform_driver(bcm2712_iommu_cache_driver); diff --git a/drivers/iommu/bcm2712-iommu.c b/drivers/iommu/bcm2712-iommu.c new file mode 100644 index 0000000000000..576550669f6fa --- /dev/null +++ b/drivers/iommu/bcm2712-iommu.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IOMMU driver for BCM2712 + * + * Copyright (c) 2023 Raspberry Pi Ltd. + */ + +#include "bcm2712-iommu.h" + +#include +#include +#include +#include +#include +#include + +#define MMU_WR(off, val) writel(val, mmu->reg_base + (off)) +#define MMU_RD(off) readl(mmu->reg_base + (off)) + +#define domain_to_mmu(d) (container_of(d, struct bcm2712_iommu_domain, base)->mmu) + +#define MMMU_CTRL_OFFSET 0x00 +#define MMMU_CTRL_CAP_EXCEEDED BIT(27) +#define MMMU_CTRL_CAP_EXCEEDED_ABORT_EN BIT(26) +#define MMMU_CTRL_CAP_EXCEEDED_INT_EN BIT(25) +#define MMMU_CTRL_CAP_EXCEEDED_EXCEPTION_EN BIT(24) +#define MMMU_CTRL_PT_INVALID BIT(20) +#define MMMU_CTRL_PT_INVALID_ABORT_EN BIT(19) +#define MMMU_CTRL_PT_INVALID_EXCEPTION_EN BIT(18) +#define MMMU_CTRL_PT_INVALID_EN BIT(17) +#define MMMU_CTRL_WRITE_VIOLATION BIT(12) +#define MMMU_CTRL_WRITE_VIOLATION_ABORT_EN BIT(11) +#define MMMU_CTRL_WRITE_VIOLATION_INT_EN BIT(10) +#define MMMU_CTRL_WRITE_VIOLATION_EXCEPTION_EN BIT(9) +#define MMMU_CTRL_BYPASS BIT(8) +#define MMMU_CTRL_TLB_CLEARING BIT(7) +#define MMMU_CTRL_STATS_CLEAR BIT(3) +#define MMMU_CTRL_TLB_CLEAR BIT(2) +#define MMMU_CTRL_STATS_ENABLE BIT(1) +#define MMMU_CTRL_ENABLE BIT(0) + +#define MMMU_PT_PA_BASE_OFFSET 0x04 + +#define MMMU_HIT_OFFSET 0x08 +#define MMMU_MISS_OFFSET 0x0C +#define MMMU_STALL_OFFSET 0x10 + +#define MMMU_ADDR_CAP_OFFSET 0x14 +#define MMMU_ADDR_CAP_ENABLE BIT(31) +#define ADDR_CAP_SHIFT 28 /* ADDR_CAP is defined to be in 256 MByte units */ + +#define MMMU_SHOOT_DOWN_OFFSET 0x18 +#define MMMU_SHOOT_DOWN_SHOOTING BIT(31) +#define MMMU_SHOOT_DOWN_SHOOT BIT(30) + +#define MMMU_BYPASS_START_OFFSET 0x1C +#define MMMU_BYPASS_START_ENABLE BIT(31) +#define MMMU_BYPASS_START_INVERT BIT(30) + +#define MMMU_BYPASS_END_OFFSET 0x20 +#define MMMU_BYPASS_END_ENABLE BIT(31) + +#define MMMU_MISC_OFFSET 0x24 +#define MMMU_MISC_SINGLE_TABLE BIT(31) + +#define MMMU_ILLEGAL_ADR_OFFSET 0x30 +#define MMMU_ILLEGAL_ADR_ENABLE BIT(31) + +#define MMMU_DEBUG_INFO_OFFSET 0x38 +#define MMMU_DEBUG_INFO_VERSION_MASK 0x0000000Fu +#define MMMU_DEBUG_INFO_VA_WIDTH_MASK 0x000000F0u +#define MMMU_DEBUG_INFO_PA_WIDTH_MASK 0x00000F00u +#define MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK 0x000FF000u +#define MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK 0x0FF00000u +#define MMMU_DEBUG_INFO_BYPASS_4M BIT(28) +#define MMMU_DEBUG_INFO_BYPASS BIT(29) + +#define MMMU_PTE_PAGESIZE_MASK 0xC0000000u +#define MMMU_PTE_WRITEABLE BIT(29) +#define MMMU_PTE_VALID BIT(28) + +/* + * BCM2712 IOMMU is organized around 4Kbyte pages (MMU_PAGE_SIZE). + * Linux PAGE_SIZE must not be smaller but may be larger (e.g. 4K, 16K). + * + * Unlike many larger MMUs, this one uses a 4-byte word size, allowing + * 1024 entries within each 4K table page, and two-level translation. + * + * Let's allocate enough table space for 2GB of translated memory (IOVA). + * This requires 512 4K pages (2MB) of level-2 tables, one page of + * top-level table (only half-filled in this particular configuration), + * plus one "default" page to catch illegal requests. + * + * The translated virtual address region is between 40GB and 42GB; + * addresses below this range pass straight through to the SDRAM. + * + * Currently we assume a 1:1:1 correspondence of IOMMU, group and domain. + */ + +#define MMU_PAGE_SHIFT 12 +#define MMU_PAGE_SIZE BIT(MMU_PAGE_SHIFT) + +#define PAGEWORDS_SHIFT (MMU_PAGE_SHIFT - 2) +#define HUGEPAGE_SHIFT (MMU_PAGE_SHIFT + PAGEWORDS_SHIFT) +#define L1_CHUNK_SHIFT (MMU_PAGE_SHIFT + 2 * PAGEWORDS_SHIFT) + +#define APERTURE_BASE (40ul << 30) +#define APERTURE_SIZE (2ul << 30) +#define APERTURE_TOP (APERTURE_BASE + APERTURE_SIZE) +#define TRANSLATED_PAGES (APERTURE_SIZE >> MMU_PAGE_SHIFT) +#define L2_PAGES (TRANSLATED_PAGES >> PAGEWORDS_SHIFT) +#define TABLES_ALLOC_SIZE (L2_PAGES * MMU_PAGE_SIZE + 2 * PAGE_SIZE) + +static void bcm2712_iommu_init(struct bcm2712_iommu *mmu) +{ + unsigned int i, bypass_shift; + struct sg_dma_page_iter it; + u32 u = MMU_RD(MMMU_DEBUG_INFO_OFFSET); + + /* + * Check IOMMU version and hardware configuration. + * This driver is for VC IOMMU version >= 4 (with 2-level tables) + * and assumes at least 36 bits of virtual and physical address space. + * Bigpage and superpage sizes are typically 64K and 1M, but may vary + * (hugepage size is fixed at 4M, the range covered by an L2 page). + */ + dev_info(mmu->dev, "%s: DEBUG_INFO = 0x%08x\n", __func__, u); + WARN_ON(FIELD_GET(MMMU_DEBUG_INFO_VERSION_MASK, u) < 4 || + FIELD_GET(MMMU_DEBUG_INFO_VA_WIDTH_MASK, u) < 6 || + FIELD_GET(MMMU_DEBUG_INFO_PA_WIDTH_MASK, u) < 6 || + !(u & MMMU_DEBUG_INFO_BYPASS)); + + mmu->bigpage_mask = + ((1u << FIELD_GET(MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT; + mmu->superpage_mask = + ((1u << FIELD_GET(MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT; + bypass_shift = (u & MMMU_DEBUG_INFO_BYPASS_4M) ? + HUGEPAGE_SHIFT : ADDR_CAP_SHIFT; + + /* Disable MMU and clear sticky flags; meanwhile flush the TLB */ + MMU_WR(MMMU_CTRL_OFFSET, + MMMU_CTRL_CAP_EXCEEDED | + MMMU_CTRL_PT_INVALID | + MMMU_CTRL_WRITE_VIOLATION | + MMMU_CTRL_STATS_CLEAR | + MMMU_CTRL_TLB_CLEAR); + + /* + * Put MMU into 2-level mode; set address cap and "bypass" range + * (note that some of these registers have unintuitive off-by-ones). + * Addresses below APERTURE_BASE are passed unchanged: this is + * useful for blocks which share an IOMMU with other blocks + * whose drivers are not IOMMU-aware. + */ + MMU_WR(MMMU_MISC_OFFSET, + MMU_RD(MMMU_MISC_OFFSET) & ~MMMU_MISC_SINGLE_TABLE); + MMU_WR(MMMU_ADDR_CAP_OFFSET, + MMMU_ADDR_CAP_ENABLE + + (APERTURE_TOP >> ADDR_CAP_SHIFT) - 1); + if (APERTURE_BASE > 0) { + MMU_WR(MMMU_BYPASS_START_OFFSET, + MMMU_BYPASS_START_ENABLE + MMMU_BYPASS_START_INVERT + + (APERTURE_BASE >> bypass_shift) - 1); + MMU_WR(MMMU_BYPASS_END_OFFSET, + MMMU_BYPASS_END_ENABLE + + (APERTURE_TOP >> bypass_shift)); + } else { + MMU_WR(MMMU_BYPASS_START_OFFSET, 0); + MMU_WR(MMMU_BYPASS_END_OFFSET, 0); + } + + /* Ensure tables are zeroed (which marks all pages as invalid) */ + dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE); + memset(mmu->tables, 0, TABLES_ALLOC_SIZE); + mmu->nmapped_pages = 0; + + /* Initialize the high-level table to point to the low-level pages */ + __sg_page_iter_start(&it.base, mmu->sgt->sgl, mmu->sgt->nents, 0); + for (i = 0; i < L2_PAGES; i++) { + if (!(i % (PAGE_SIZE / MMU_PAGE_SIZE))) { + __sg_page_iter_dma_next(&it); + u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT); + } else { + u++; + } + mmu->tables[TRANSLATED_PAGES + i] = MMMU_PTE_VALID + u; + } + + /* + * Configure the addresses of the top-level table (offset because + * the aperture does not start from zero), and of the default page. + * For simplicity, both these regions are whole Linux pages. + */ + __sg_page_iter_dma_next(&it); + u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT); + MMU_WR(MMMU_PT_PA_BASE_OFFSET, u - (APERTURE_BASE >> L1_CHUNK_SHIFT)); + __sg_page_iter_dma_next(&it); + u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT); + MMU_WR(MMMU_ILLEGAL_ADR_OFFSET, MMMU_ILLEGAL_ADR_ENABLE + u); + dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE); + mmu->dirty = false; + + /* Flush (and enable) the shared TLB cache; enable this MMU. */ + if (mmu->cache) + bcm2712_iommu_cache_flush(mmu->cache); + MMU_WR(MMMU_CTRL_OFFSET, + MMMU_CTRL_CAP_EXCEEDED_ABORT_EN | + MMMU_CTRL_PT_INVALID_ABORT_EN | + MMMU_CTRL_WRITE_VIOLATION_ABORT_EN | + MMMU_CTRL_STATS_ENABLE | + MMMU_CTRL_ENABLE); +} + +static int bcm2712_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct bcm2712_iommu *mmu = dev ? dev_iommu_priv_get(dev) : 0; + struct bcm2712_iommu_domain *mydomain = + container_of(domain, struct bcm2712_iommu_domain, base); + + dev_info(dev, "%s: MMU %s\n", + __func__, mmu ? dev_name(mmu->dev) : ""); + + if (mmu) { + mydomain->mmu = mmu; + mmu->domain = mydomain; + + if (mmu->dma_iova_offset) { + domain->geometry.aperture_start = + mmu->dma_iova_offset + APERTURE_BASE; + domain->geometry.aperture_end = + mmu->dma_iova_offset + APERTURE_TOP - 1ul; + } + + return 0; + } + return -EINVAL; +} + +static int bcm2712_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t pa, size_t bytes, int prot, gfp_t gfp) +{ + struct bcm2712_iommu *mmu = domain_to_mmu(domain); + + (void)gfp; + iova -= mmu->dma_iova_offset; + if (iova >= APERTURE_BASE && iova + bytes <= APERTURE_TOP) { + unsigned int p; + u32 entry = MMMU_PTE_VALID | (pa >> MMU_PAGE_SHIFT); + u32 align = (u32)(iova | pa | bytes); + + /* large page and write enable flags */ + if (!(align & ((1 << HUGEPAGE_SHIFT) - 1))) + entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 3); + else if (!(align & mmu->superpage_mask) && mmu->superpage_mask) + entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 2); + else if (!(align & mmu->bigpage_mask) && mmu->bigpage_mask) + entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 1); + if (prot & IOMMU_WRITE) + entry |= MMMU_PTE_WRITEABLE; + + /* Ensure tables are cache-coherent with CPU */ + if (!mmu->dirty) { + dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE); + mmu->dirty = true; + } + + iova -= APERTURE_BASE; + for (p = iova >> MMU_PAGE_SHIFT; + p < (iova + bytes) >> MMU_PAGE_SHIFT; p++) { + mmu->nmapped_pages += !(mmu->tables[p]); + mmu->tables[p] = entry++; + } + } else if (iova + bytes > APERTURE_BASE || iova != pa) { + dev_warn(mmu->dev, "%s: iova=0x%lx pa=0x%llx size=0x%llx OUT OF RANGE!\n", + __func__, iova, + (unsigned long long)pa, (unsigned long long)bytes); + return -EINVAL; + } + + return 0; +} + +static size_t bcm2712_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t bytes, struct iommu_iotlb_gather *gather) +{ + struct bcm2712_iommu *mmu = domain_to_mmu(domain); + + if (iova >= mmu->dma_iova_offset + APERTURE_BASE && + iova + bytes <= mmu->dma_iova_offset + APERTURE_TOP) { + unsigned int p; + + /* Record just the lower and upper bounds in "gather" */ + if (gather) { + bool empty = (gather->end <= gather->start); + + if (empty || gather->start < iova) + gather->start = iova; + if (empty || gather->end < iova + bytes) + gather->end = iova + bytes; + } + + /* Ensure tables are cache-coherent with CPU */ + if (!mmu->dirty) { + dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE); + mmu->dirty = true; + } + + /* Clear table entries, this marks the addresses as illegal */ + iova -= (mmu->dma_iova_offset + APERTURE_BASE); + for (p = iova >> MMU_PAGE_SHIFT; + p < (iova + bytes) >> MMU_PAGE_SHIFT; + p++) { + mmu->nmapped_pages -= !!(mmu->tables[p]); + mmu->tables[p] = 0; + } + } + + return bytes; +} + +static void bcm2712_iommu_sync_range(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct bcm2712_iommu *mmu = domain_to_mmu(domain); + unsigned long iova_end; + unsigned int i, p4; + + if (!mmu || !mmu->dirty) + return; + + /* Ensure tables are cleaned from CPU cache or write-buffer */ + dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE); + mmu->dirty = false; + + /* Flush the shared TLB cache */ + if (mmu->cache) + bcm2712_iommu_cache_flush(mmu->cache); + + /* + * When flushing a large range or when nothing needs to be kept, + * it's quicker to use the"TLB_CLEAR" flag. Otherwise, invalidate + * TLB entries in lines of 4 words each. Each flush/clear operation + * should complete almost instantaneously. + */ + iova -= mmu->dma_iova_offset; + iova_end = min(APERTURE_TOP, iova + size); + iova = max(APERTURE_BASE, iova); + if (mmu->nmapped_pages == 0 || iova_end - iova >= APERTURE_SIZE / 8) { + MMU_WR(MMMU_CTRL_OFFSET, + MMMU_CTRL_CAP_EXCEEDED_ABORT_EN | + MMMU_CTRL_PT_INVALID_ABORT_EN | + MMMU_CTRL_WRITE_VIOLATION_ABORT_EN | + MMMU_CTRL_TLB_CLEAR | + MMMU_CTRL_STATS_ENABLE | + MMMU_CTRL_ENABLE); + for (i = 0; i < 1024; i++) { + if (!(MMMU_CTRL_TLB_CLEARING & MMU_RD(MMMU_CTRL_OFFSET))) + break; + cpu_relax(); + } + } else { + for (p4 = iova >> (MMU_PAGE_SHIFT + 2); + p4 < (iova_end + 3 * MMU_PAGE_SIZE) >> (MMU_PAGE_SHIFT + 2); + p4++) { + MMU_WR(MMMU_SHOOT_DOWN_OFFSET, + MMMU_SHOOT_DOWN_SHOOT + (p4 << 2)); + for (i = 0; i < 1024; i++) { + if (!(MMMU_SHOOT_DOWN_SHOOTING & MMU_RD(MMMU_SHOOT_DOWN_OFFSET))) + break; + cpu_relax(); + } + } + } +} + +static void bcm2712_iommu_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) +{ + bcm2712_iommu_sync_range(domain, gather->start, + gather->end - gather->start); +} + +static void bcm2712_iommu_sync_all(struct iommu_domain *domain) +{ + bcm2712_iommu_sync_range(domain, APERTURE_BASE, APERTURE_SIZE); +} + +static phys_addr_t bcm2712_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) +{ + struct bcm2712_iommu *mmu = domain_to_mmu(domain); + u32 p; + + iova -= mmu->dma_iova_offset; + if (iova >= APERTURE_BASE && iova < APERTURE_TOP) { + p = (iova - APERTURE_BASE) >> MMU_PAGE_SHIFT; + p = mmu->tables[p] & 0x0FFFFFFFu; + return (((phys_addr_t)p) << MMU_PAGE_SHIFT) + (iova & (MMU_PAGE_SIZE - 1u)); + } else if (iova < APERTURE_BASE) { + return (phys_addr_t)iova; + } else { + return (phys_addr_t)-EINVAL; + } +} + +static void bcm2712_iommu_domain_free(struct iommu_domain *domain) +{ + struct bcm2712_iommu_domain *mydomain = + container_of(domain, struct bcm2712_iommu_domain, base); + + kfree(mydomain); +} + +static const struct iommu_domain_ops bcm2712_iommu_domain_ops = { + .attach_dev = bcm2712_iommu_attach_dev, + .map = bcm2712_iommu_map, + .unmap = bcm2712_iommu_unmap, + .iotlb_sync = bcm2712_iommu_sync, + .iotlb_sync_map = bcm2712_iommu_sync_range, + .flush_iotlb_all = bcm2712_iommu_sync_all, + .iova_to_phys = bcm2712_iommu_iova_to_phys, + .free = bcm2712_iommu_domain_free, +}; + +static struct iommu_domain *bcm2712_iommu_domain_alloc(unsigned int type) +{ + struct bcm2712_iommu_domain *domain; + + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA) + return NULL; + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) + return NULL; + + domain->base.type = type; + domain->base.ops = &bcm2712_iommu_domain_ops; + domain->base.geometry.aperture_start = APERTURE_BASE; + domain->base.geometry.aperture_end = APERTURE_TOP - 1ul; + domain->base.geometry.force_aperture = true; + return &domain->base; +} + +static struct iommu_device *bcm2712_iommu_probe_device(struct device *dev) +{ + struct bcm2712_iommu *mmu; + + /* + * For reasons I don't fully understand, we need to try both + * cases (dev_iommu_priv_get() and platform_get_drvdata()) + * in order to get both GPU and ISP-BE to probe successfully. + */ + mmu = dev_iommu_priv_get(dev); + if (!mmu) { + struct device_node *np; + struct platform_device *pdev; + + /* Ignore devices that don't have an "iommus" property with exactly one phandle */ + if (!dev->of_node || + of_property_count_elems_of_size(dev->of_node, "iommus", sizeof(phandle)) != 1) + return ERR_PTR(-ENODEV); + + np = of_parse_phandle(dev->of_node, "iommus", 0); + if (!np) + return ERR_PTR(-EINVAL); + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (pdev) + mmu = platform_get_drvdata(pdev); + + if (!mmu) + return ERR_PTR(-ENODEV); + } + + dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev)); + dev_iommu_priv_set(dev, mmu); + return &mmu->iommu; +} + +static void bcm2712_iommu_release_device(struct device *dev) +{ + dev_iommu_priv_set(dev, NULL); +} + +static struct iommu_group *bcm2712_iommu_device_group(struct device *dev) +{ + struct bcm2712_iommu *mmu = dev_iommu_priv_get(dev); + + if (!mmu || !mmu->group) + return ERR_PTR(-EINVAL); + + dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev)); + return iommu_group_ref_get(mmu->group); +} + +static int bcm2712_iommu_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct platform_device *iommu_dev; + struct bcm2712_iommu *mmu; + + iommu_dev = of_find_device_by_node(args->np); + mmu = platform_get_drvdata(iommu_dev); + dev_iommu_priv_set(dev, mmu); + dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev)); + + return 0; +} + +static bool bcm2712_iommu_capable(struct device *dev, enum iommu_cap cap) +{ + return false; +} + +static const struct iommu_ops bcm2712_iommu_ops = { + .capable = bcm2712_iommu_capable, + .domain_alloc = bcm2712_iommu_domain_alloc, + .probe_device = bcm2712_iommu_probe_device, + .release_device = bcm2712_iommu_release_device, + .device_group = bcm2712_iommu_device_group, + /* Advertise native page sizes as well as 2M, 16K which Linux may prefer */ + .pgsize_bitmap = (SZ_4M | SZ_2M | SZ_1M | SZ_64K | SZ_16K | SZ_4K), + .default_domain_ops = &bcm2712_iommu_domain_ops, + .of_xlate = bcm2712_iommu_of_xlate, +}; + +static int bcm2712_iommu_probe(struct platform_device *pdev) +{ + struct bcm2712_iommu *mmu; + struct bcm2712_iommu_cache *cache = NULL; + int ret; + + /* First of all, check for an IOMMU shared cache */ + if (pdev->dev.of_node) { + struct device_node *cache_np; + struct platform_device *cache_pdev; + + cache_np = of_parse_phandle(pdev->dev.of_node, "cache", 0); + if (cache_np) { + cache_pdev = of_find_device_by_node(cache_np); + of_node_put(cache_np); + if (cache_pdev && !IS_ERR(cache_pdev)) + cache = platform_get_drvdata(cache_pdev); + if (!cache) + return -EPROBE_DEFER; + } + } + + /* Allocate private data */ + mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL); + if (!mmu) + return -ENOMEM; + + mmu->name = dev_name(&pdev->dev); + mmu->dev = &pdev->dev; + mmu->cache = cache; + platform_set_drvdata(pdev, mmu); + spin_lock_init(&mmu->hw_lock); + + /* + * XXX When an IOMMU is downstream of a PCIe RC or some other chip/bus + * and serves some of the masters thereon (others using pass-through), + * we seem to fumble and lose the "dma-ranges" address offset for + * masters using IOMMU. This property restores it, where needed. + */ + if (!pdev->dev.of_node || + of_property_read_u64(pdev->dev.of_node, "dma-iova-offset", + &mmu->dma_iova_offset)) + mmu->dma_iova_offset = 0; + + /* + * The IOMMU is itself a device that allocates DMA-able memory + * to hold its translation tables. Provided the IOVA aperture + * is no larger than 4 GBytes (so that the L1 table fits within + * a single 4K page), we don't need the tables to be contiguous. + * Assume we can address at least 36 bits (64 GB). + */ + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + WARN_ON(ret); + mmu->sgt = dma_alloc_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE, + DMA_TO_DEVICE, GFP_KERNEL, + DMA_ATTR_ALLOC_SINGLE_PAGES); + if (!mmu->sgt) { + ret = -ENOMEM; + goto done_err; + } + mmu->tables = dma_vmap_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE, + mmu->sgt); + if (!mmu->tables) { + ret = -ENOMEM; + goto done_err; + } + + /* Get IOMMU registers */ + mmu->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmu->reg_base)) { + dev_err(&pdev->dev, "Failed to get IOMMU registers address\n"); + ret = PTR_ERR(mmu->reg_base); + goto done_err; + } + + /* Stuff */ + mmu->group = iommu_group_alloc(); + if (IS_ERR(mmu->group)) { + ret = PTR_ERR(mmu->group); + mmu->group = NULL; + goto done_err; + } + ret = iommu_device_sysfs_add(&mmu->iommu, mmu->dev, NULL, mmu->name); + if (ret) + goto done_err; + + /* Initialize table and hardware */ + bcm2712_iommu_init(mmu); + ret = iommu_device_register(&mmu->iommu, &bcm2712_iommu_ops, &pdev->dev); + + dev_info(&pdev->dev, "%s: Success\n", __func__); + return 0; + +done_err: + dev_info(&pdev->dev, "%s: Failure %d\n", __func__, ret); + if (mmu->group) + iommu_group_put(mmu->group); + if (mmu->tables) + dma_vunmap_noncontiguous(&pdev->dev, + (void *)(mmu->tables)); + mmu->tables = NULL; + if (mmu->sgt) + dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE, + mmu->sgt, DMA_TO_DEVICE); + mmu->sgt = NULL; + kfree(mmu); + return ret; +} + +static int bcm2712_iommu_remove(struct platform_device *pdev) +{ + struct bcm2712_iommu *mmu = platform_get_drvdata(pdev); + + if (mmu->reg_base) + MMU_WR(MMMU_CTRL_OFFSET, 0); /* disable the MMU */ + if (mmu->sgt) + dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE, + mmu->sgt, DMA_TO_DEVICE); + + return 0; +} + +static const struct of_device_id bcm2712_iommu_of_match[] = { + { + . compatible = "brcm,bcm2712-iommu" + }, + { /* sentinel */ }, +}; + +static struct platform_driver bcm2712_iommu_driver = { + .probe = bcm2712_iommu_probe, + .remove = bcm2712_iommu_remove, + .driver = { + .name = "bcm2712-iommu", + .of_match_table = bcm2712_iommu_of_match + }, +}; + +builtin_platform_driver(bcm2712_iommu_driver); diff --git a/drivers/iommu/bcm2712-iommu.h b/drivers/iommu/bcm2712-iommu.h new file mode 100644 index 0000000000000..31b811e426ddb --- /dev/null +++ b/drivers/iommu/bcm2712-iommu.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * IOMMU driver for BCM2712 + * + * Copyright (c) 2023 Raspberry Pi Ltd. + */ + +#ifndef _BCM2712_IOMMU_H +#define _BCM2712_IOMMU_H + +#include +#include + +struct bcm2712_iommu_cache { + struct device *dev; + spinlock_t hw_lock; /* to protect HW registers */ + void __iomem *reg_base; +}; + +void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache); + +struct bcm2712_iommu { + struct device *dev; + struct iommu_device iommu; + struct iommu_group *group; + struct bcm2712_iommu_domain *domain; + char const *name; + struct sg_table *sgt; /* allocated memory for page tables */ + u32 *tables; /* kernel mapping for page tables */ + struct bcm2712_iommu_cache *cache; + spinlock_t hw_lock; /* to protect HW registers */ + void __iomem *reg_base; + u64 dma_iova_offset; /* Hack for IOMMU attached to PCIe RC */ + u32 bigpage_mask; + u32 superpage_mask; + unsigned int nmapped_pages; + bool dirty; /* true when tables are oriented towards CPU */ +}; + +struct bcm2712_iommu_domain { + struct iommu_domain base; + struct bcm2712_iommu *mmu; +}; + +#endif diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a29a426e4eed7..91110b4cad719 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -109,6 +109,14 @@ config I8259 bool select IRQ_DOMAIN +config BCM2712_MIP + bool "Broadcom 2712 MSI-X Interrupt Peripheral support" + depends on ARM_GIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + help + Enable support for the Broadcom BCM2712 MSI-X target peripheral. + config BCM6345_L1_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 87b49a10962c7..1d29cf89f3ffd 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o +obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c new file mode 100644 index 0000000000000..2eaa3ac10cb62 --- /dev/null +++ b/drivers/irqchip/irq-bcm2712-mip.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Raspberry Pi Ltd., All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define MIP_INT_RAISED 0x00 +#define MIP_INT_CLEARED 0x10 +#define MIP_INT_CFGL_HOST 0x20 +#define MIP_INT_CFGH_HOST 0x30 +#define MIP_INT_MASKL_HOST 0x40 +#define MIP_INT_MASKH_HOST 0x50 +#define MIP_INT_MASKL_VPU 0x60 +#define MIP_INT_MASKH_VPU 0x70 +#define MIP_INT_STATUSL_HOST 0x80 +#define MIP_INT_STATUSH_HOST 0x90 +#define MIP_INT_STATUSL_VPU 0xa0 +#define MIP_INT_STATUSH_VPU 0xb0 + +struct mip_priv { + spinlock_t msi_map_lock; + spinlock_t hw_lock; + void * __iomem base; + phys_addr_t msg_addr; + u32 msi_base; /* The SGI number that MSIs start */ + u32 num_msis; /* The number of SGIs for MSIs */ + u32 msi_offset; /* Shift the allocated msi up by N */ + unsigned long *msi_map; +}; + +static void mip_mask_msi_irq(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void mip_unmask_msi_irq(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct mip_priv *priv = irq_data_get_irq_chip_data(d); + + msg->address_hi = upper_32_bits(priv->msg_addr); + msg->address_lo = lower_32_bits(priv->msg_addr); + msg->data = d->hwirq; +} + +// The "bus-specific" irq_chip (the MIP doesn't _have_ to be used with PCIe) + +static struct irq_chip mip_msi_irq_chip = { + .name = "MIP-MSI", + .irq_unmask = mip_unmask_msi_irq, + .irq_mask = mip_mask_msi_irq, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static struct msi_domain_info mip_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &mip_msi_irq_chip, +}; + +// The "middle" irq_chip (the hardware control part) + +static struct irq_chip mip_irq_chip = { + .name = "MIP", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = mip_compose_msi_msg, +}; + + +// And a domain to connect it to its parent (the GIC) + +static int mip_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct mip_priv *priv = domain->host_data; + struct irq_fwspec fwspec; + struct irq_data *irqd; + int hwirq, ret, i; + + spin_lock(&priv->msi_map_lock); + + hwirq = bitmap_find_free_region(priv->msi_map, priv->num_msis, ilog2(nr_irqs)); + + spin_unlock(&priv->msi_map_lock); + + if (hwirq < 0) + return -ENOSPC; + + hwirq += priv->msi_offset; + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; + fwspec.param[1] = hwirq + priv->msi_base; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + irqd = irq_domain_get_irq_data(domain->parent, virq + i); + irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING); + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mip_irq_chip, priv); + irqd = irq_get_irq_data(virq + i); + irqd_set_single_target(irqd); + irqd_set_affinity_on_activate(irqd); + } + + return 0; +} + +static void mip_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct mip_priv *priv = irq_data_get_irq_chip_data(d); + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + d->hwirq -= priv->msi_offset; + + spin_lock(&priv->msi_map_lock); + + bitmap_release_region(priv->msi_map, d->hwirq, ilog2(nr_irqs)); + + spin_unlock(&priv->msi_map_lock); +} + +#if 0 +static int mip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool reserve) +{ + struct mip_priv *priv = irq_data_get_irq_chip_data(d); + unsigned long flags; + unsigned int irq = d->hwirq; + void *__iomem reg = priv->base + + ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST); + u32 val; + + spin_lock_irqsave(&priv->hw_lock, flags); + val = readl(reg); + val &= ~(1 << (irq % 32)); // Clear the mask + writel(val, reg); + spin_unlock_irqrestore(&priv->hw_lock, flags); + return 0; +} + +static void mip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *d) +{ + struct mip_priv *priv = irq_data_get_irq_chip_data(d); + unsigned long flags; + unsigned int irq = d->hwirq - priv->msi_base; + void *__iomem reg = priv->base + + ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST); + u32 val; + + spin_lock_irqsave(&priv->hw_lock, flags); + val = readl(reg); + val |= (1 << (irq % 32)); // Mask it out + writel(val, reg); + spin_unlock_irqrestore(&priv->hw_lock, flags); +} +#endif + +static const struct irq_domain_ops mip_irq_domain_ops = { + .alloc = mip_irq_domain_alloc, + .free = mip_irq_domain_free, + //.activate = mip_irq_domain_activate, + //.deactivate = mip_irq_domain_deactivate, +}; + +static int mip_init_domains(struct mip_priv *priv, + struct device_node *node) +{ + struct irq_domain *middle_domain, *msi_domain, *gic_domain; + struct device_node *gic_node; + + gic_node = of_irq_find_parent(node); + if (!gic_node) { + pr_err("Failed to find the GIC node\n"); + return -ENODEV; + } + + gic_domain = irq_find_host(gic_node); + if (!gic_domain) { + pr_err("Failed to find the GIC domain\n"); + return -ENXIO; + } + + middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL, + &mip_irq_domain_ops, + priv); + if (!middle_domain) { + pr_err("Failed to create the MIP middle domain\n"); + return -ENOMEM; + } + + msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), + &mip_msi_domain_info, + middle_domain); + if (!msi_domain) { + pr_err("Failed to create MSI domain\n"); + irq_domain_remove(middle_domain); + return -ENOMEM; + } + + return 0; +} + +static int __init mip_of_msi_init(struct device_node *node, + struct device_node *parent) +{ + struct mip_priv *priv; + struct resource res; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->msi_map_lock); + spin_lock_init(&priv->hw_lock); + + ret = of_address_to_resource(node, 0, &res); + if (ret) { + pr_err("Failed to allocate resource\n"); + goto err_priv; + } + + if (of_property_read_u32(node, "brcm,msi-base-spi", &priv->msi_base)) { + pr_err("Unable to parse MSI base\n"); + ret = -EINVAL; + goto err_priv; + } + + if (of_property_read_u32(node, "brcm,msi-num-spis", &priv->num_msis)) { + pr_err("Unable to parse MSI numbers\n"); + ret = -EINVAL; + goto err_priv; + } + + if (of_property_read_u32(node, "brcm,msi-offset", &priv->msi_offset)) + priv->msi_offset = 0; + + if (of_property_read_u64(node, "brcm,msi-pci-addr", &priv->msg_addr)) { + pr_err("Unable to parse MSI address\n"); + ret = -EINVAL; + goto err_priv; + } + + priv->base = ioremap(res.start, resource_size(&res)); + if (!priv->base) { + pr_err("Failed to ioremap regs\n"); + ret = -ENOMEM; + goto err_priv; + } + + priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_msis), + sizeof(*priv->msi_map), + GFP_KERNEL); + if (!priv->msi_map) { + ret = -ENOMEM; + goto err_base; + } + + pr_debug("Registering %d msixs, starting at %d\n", + priv->num_msis, priv->msi_base); + + /* + * Begin with all MSI-Xs masked in for the host, masked out for the + * VPU, and edge-triggered. + */ + writel(0, priv->base + MIP_INT_MASKL_HOST); + writel(0, priv->base + MIP_INT_MASKH_HOST); + writel(~0, priv->base + MIP_INT_MASKL_VPU); + writel(~0, priv->base + MIP_INT_MASKH_VPU); + writel(~0, priv->base + MIP_INT_CFGL_HOST); + writel(~0, priv->base + MIP_INT_CFGH_HOST); + + ret = mip_init_domains(priv, node); + if (ret) { + pr_err("Failed to allocate msi_map\n"); + goto err_map; + } + + return 0; + +err_map: + kfree(priv->msi_map); + +err_base: + iounmap(priv->base); + +err_priv: + kfree(priv); + + pr_err("%s: failed - err %d\n", __func__, ret); + + return ret; +} +IRQCHIP_DECLARE(bcm_mip, "brcm,bcm2712-mip-intc", mip_of_msi_init); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9da8235cb6900..3b6cc11b433c7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2234,6 +2234,17 @@ config MFD_INTEL_M10_BMC additional drivers must be enabled in order to use the functionality of the device. +config MFD_RP1 + tristate "RP1 MFD driver" + depends on PCI + select MFD_CORE + help + Support for the RP1 peripheral chip. + + This driver provides support for the Raspberry Pi RP1 peripheral chip. + It is responsible for enabling the Device Tree node once the PCIe endpoint + has been configured, and handling interrupts. + config MFD_RSMU_I2C tristate "Renesas Synchronization Management Unit with I2C" depends on I2C && OF diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7ed3ef4a698cf..588f9caadf1e0 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -280,3 +280,5 @@ rsmu-i2c-objs := rsmu_core.o rsmu_i2c.o rsmu-spi-objs := rsmu_core.o rsmu_spi.o obj-$(CONFIG_MFD_RSMU_I2C) += rsmu-i2c.o obj-$(CONFIG_MFD_RSMU_SPI) += rsmu-spi.o + +obj-$(CONFIG_MFD_RP1) += rp1.o diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c index 49cd1f03884af..a4f9b4454f1f7 100644 --- a/drivers/mfd/bcm2835-pm.c +++ b/drivers/mfd/bcm2835-pm.c @@ -28,7 +28,7 @@ static const struct mfd_cell bcm2835_power_devs[] = { static int bcm2835_pm_get_pdata(struct platform_device *pdev, struct bcm2835_pm *pm) { - if (of_find_property(pm->dev->of_node, "reg-names", NULL)) { + if (of_property_read_bool(pm->dev->of_node, "reg-names")) { struct resource *res; pm->base = devm_platform_ioremap_resource_byname(pdev, "pm"); @@ -69,12 +69,30 @@ static int bcm2835_pm_get_pdata(struct platform_device *pdev, return 0; } +static const struct of_device_id bcm2835_pm_of_match[] = { + { .compatible = "brcm,bcm2835-pm-wdt", }, + { .compatible = "brcm,bcm2835-pm", }, + { .compatible = "brcm,bcm2711-pm", }, + { .compatible = "brcm,bcm2712-pm", .data = (const void *)1}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); + static int bcm2835_pm_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; struct device *dev = &pdev->dev; struct bcm2835_pm *pm; + bool is_2712; int ret; + of_id = of_match_node(bcm2835_pm_of_match, pdev->dev.of_node); + if (!of_id) { + dev_err(&pdev->dev, "Failed to match compatible string\n"); + return -EINVAL; + } + is_2712 = !!of_id->data; + pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL); if (!pm) return -ENOMEM; @@ -97,21 +115,13 @@ static int bcm2835_pm_probe(struct platform_device *pdev) * bcm2835-pm binding as the key for whether we can reference * the full PM register range and support power domains. */ - if (pm->asb) + if (pm->asb || is_2712) return devm_mfd_add_devices(dev, -1, bcm2835_power_devs, ARRAY_SIZE(bcm2835_power_devs), NULL, 0, NULL); return 0; } -static const struct of_device_id bcm2835_pm_of_match[] = { - { .compatible = "brcm,bcm2835-pm-wdt", }, - { .compatible = "brcm,bcm2835-pm", }, - { .compatible = "brcm,bcm2711-pm", }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); - static struct platform_driver bcm2835_pm_driver = { .probe = bcm2835_pm_probe, .driver = { @@ -123,4 +133,3 @@ module_platform_driver(bcm2835_pm_driver); MODULE_AUTHOR("Eric Anholt "); MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM MFD"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rp1.c b/drivers/mfd/rp1.c new file mode 100644 index 0000000000000..0a498a670a817 --- /dev/null +++ b/drivers/mfd/rp1.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-22 Raspberry Pi Ltd. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* TO DO: + * 1. Occasional shutdown crash - RP1 being closed before its children? + * 2. DT mode interrupt handling. + */ + +#define RP1_DRIVER_NAME "rp1" + +#define PCI_VENDOR_ID_RPI 0x1de4 +#define PCI_DEVICE_ID_RP1_C0 0x0001 +#define PCI_DEVICE_REV_RP1_C0 2 + +#define RP1_ACTUAL_IRQS RP1_INT_END +#define RP1_IRQS RP1_ACTUAL_IRQS + +#define RP1_SYSCLK_RATE 200000000 +#define RP1_SYSCLK_FPGA_RATE 60000000 + +// Don't want to include the whole sysinfo reg header +#define SYSINFO_CHIP_ID_OFFSET 0x00000000 +#define SYSINFO_PLATFORM_OFFSET 0x00000004 + +#define REG_RW 0x000 +#define REG_SET 0x800 +#define REG_CLR 0xc00 + +// MSIX CFG registers start at 0x8 +#define MSIX_CFG(x) (0x8 + (4 * (x))) + +#define MSIX_CFG_IACK_EN BIT(3) +#define MSIX_CFG_IACK BIT(2) +#define MSIX_CFG_TEST BIT(1) +#define MSIX_CFG_ENABLE BIT(0) + +#define INTSTATL 0x108 +#define INTSTATH 0x10c + +struct rp1_dev { + struct pci_dev *pdev; + struct device *dev; + resource_size_t bar_start; + resource_size_t bar_end; + struct clk *sys_clk; + struct irq_domain *domain; + struct irq_data *pcie_irqds[64]; + void __iomem *msix_cfg_regs; +}; + +static bool rp1_level_triggered_irq[RP1_ACTUAL_IRQS] = { 0 }; + +static struct rp1_dev *g_rp1; +static u32 g_chip_id, g_platform; + +static void dump_bar(struct pci_dev *pdev, unsigned int bar) +{ + dev_info(&pdev->dev, + "bar%d len 0x%llx, start 0x%llx, end 0x%llx, flags, 0x%lx\n", + bar, + pci_resource_len(pdev, bar), + pci_resource_start(pdev, bar), + pci_resource_end(pdev, bar), + pci_resource_flags(pdev, bar)); +} + +static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + writel(value, rp1->msix_cfg_regs + REG_SET + MSIX_CFG(hwirq)); +} + +static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + writel(value, rp1->msix_cfg_regs + REG_CLR + MSIX_CFG(hwirq)); +} + +static void rp1_mask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_mask_irq(pcie_irqd); +} + +static void rp1_unmask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_unmask_irq(pcie_irqd); +} + +static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + unsigned int hwirq = (unsigned int)irqd->hwirq; + int ret = 0; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + dev_dbg(rp1->dev, "MSIX IACK EN for irq %d\n", hwirq); + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1_level_triggered_irq[hwirq] = true; + break; + case IRQ_TYPE_EDGE_RISING: + msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1_level_triggered_irq[hwirq] = false; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rp1_irq_set_affinity(struct irq_data *irqd, const struct cpumask *dest, bool force) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + return msi_domain_set_affinity(pcie_irqd, dest, force); +} + +static struct irq_chip rp1_irq_chip = { + .name = "rp1_irq_chip", + .irq_mask = rp1_mask_irq, + .irq_unmask = rp1_unmask_irq, + .irq_set_type = rp1_irq_set_type, + .irq_set_affinity = rp1_irq_set_affinity, +}; + +static void rp1_chained_handle_irq(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct rp1_dev *rp1 = desc->irq_data.chip_data; + unsigned int hwirq = desc->irq_data.hwirq & 0x3f; + int new_irq; + + rp1 = g_rp1; + + chained_irq_enter(chip, desc); + + new_irq = irq_linear_revmap(rp1->domain, hwirq); + generic_handle_irq(new_irq); + if (rp1_level_triggered_irq[hwirq]) + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK); + + chained_irq_exit(chip, desc); +} + +static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct rp1_dev *rp1 = d->host_data; + struct irq_data *pcie_irqd; + unsigned long hwirq; + int pcie_irq; + int ret; + + ret = irq_domain_xlate_twocell(d, node, intspec, intsize, + &hwirq, out_type); + if (!ret) { + pcie_irq = pci_irq_vector(rp1->pdev, hwirq); + pcie_irqd = irq_get_irq_data(pcie_irq); + rp1->pcie_irqds[hwirq] = pcie_irqd; + *out_hwirq = hwirq; + } + return ret; +} + +static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, + bool reserve) +{ + struct rp1_dev *rp1 = d->host_data; + struct irq_data *pcie_irqd; + + pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + return irq_domain_activate_irq(pcie_irqd, reserve); +} + +static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd) +{ + struct rp1_dev *rp1 = d->host_data; + struct irq_data *pcie_irqd; + + pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + return irq_domain_deactivate_irq(pcie_irqd); +} + +static const struct irq_domain_ops rp1_domain_ops = { + .xlate = rp1_irq_xlate, + .activate = rp1_irq_activate, + .deactivate = rp1_irq_deactivate, +}; + +static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int offset) +{ + return rp1->bar_start + offset; +} + +static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 offset) +{ + dma_addr_t phys = rp1_io_to_phys(rp1, base_addr); + void __iomem *regblock = ioremap(phys, 0x1000); + u32 value = readl(regblock + offset); + + iounmap(regblock); + return value; +} + +void rp1_get_platform(u32 *chip_id, u32 *platform) +{ + if (chip_id) + *chip_id = g_chip_id; + if (platform) + *platform = g_platform; +} +EXPORT_SYMBOL_GPL(rp1_get_platform); + +static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct reset_control *reset; + struct platform_device *pcie_pdev; + struct device_node *rp1_node; + struct rp1_dev *rp1; + int err = 0; + int i; + + reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(reset)) + return PTR_ERR(reset); + reset_control_reset(reset); + + dump_bar(pdev, 0); + dump_bar(pdev, 1); + + if (pci_resource_len(pdev, 1) <= 0x10000) { + dev_err(&pdev->dev, + "Not initialised - is the firmware running?\n"); + return -EINVAL; + } + + /* enable pci device */ + err = pcim_enable_device(pdev); + if (err < 0) { + dev_err(&pdev->dev, "Enabling PCI device has failed: %d", + err); + return err; + } + + pci_set_master(pdev); + + err = pci_alloc_irq_vectors(pdev, RP1_IRQS, RP1_IRQS, + PCI_IRQ_MSIX); + if (err != RP1_IRQS) { + dev_err(&pdev->dev, "pci_alloc_irq_vectors failed - %d\n", err); + return err; + } + + rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); + if (!rp1) + return -ENOMEM; + + rp1->pdev = pdev; + rp1->dev = &pdev->dev; + + pci_set_drvdata(pdev, rp1); + + rp1->bar_start = pci_resource_start(pdev, 1); + rp1->bar_end = pci_resource_end(pdev, 1); + + // Get chip id + g_chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET); + g_platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET); + dev_info(&pdev->dev, "chip_id 0x%x%s\n", g_chip_id, + (g_platform & RP1_PLATFORM_FPGA) ? " FPGA" : ""); + if (g_chip_id != RP1_C0_CHIP_ID) { + dev_err(&pdev->dev, "wrong chip id (%x)\n", g_chip_id); + return -EINVAL; + } + + rp1_node = of_find_node_by_name(NULL, "rp1"); + if (!rp1_node) { + dev_err(&pdev->dev, "failed to find RP1 DT node\n"); + return -EINVAL; + } + + pcie_pdev = of_find_device_by_node(rp1_node->parent); + rp1->domain = irq_domain_add_linear(rp1_node, RP1_IRQS, + &rp1_domain_ops, rp1); + + g_rp1 = rp1; + + /* TODO can this go in the rp1 device tree entry? */ + rp1->msix_cfg_regs = ioremap(rp1_io_to_phys(rp1, RP1_PCIE_APBS_BASE), 0x1000); + + for (i = 0; i < RP1_IRQS; i++) { + int irq = irq_create_mapping(rp1->domain, i); + + if (irq < 0) { + dev_err(&pdev->dev, "failed to create irq mapping\n"); + return irq; + } + + irq_set_chip_data(irq, rp1); + irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq); + irq_set_probe(irq); + irq_set_chained_handler(pci_irq_vector(pdev, i), + rp1_chained_handle_irq); + } + + if (rp1_node) + of_platform_populate(rp1_node, NULL, NULL, &pcie_pdev->dev); + + of_node_put(rp1_node); + + return 0; +} + +static void rp1_remove(struct pci_dev *pdev) +{ + struct rp1_dev *rp1 = pci_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + + clk_unregister(rp1->sys_clk); +} + +static const struct pci_device_id dev_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), }, + { 0, } +}; + +static struct pci_driver rp1_driver = { + .name = RP1_DRIVER_NAME, + .id_table = dev_id_table, + .probe = rp1_probe, + .remove = rp1_remove, +}; + +module_pci_driver(rp1_driver); + +MODULE_AUTHOR("Phil Elwell "); +MODULE_DESCRIPTION("RP1 wrapper"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 55d8bd232695c..ad55c59b34389 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "sdhci-cqhci.h" #include "sdhci-pltfm.h" @@ -26,18 +28,43 @@ #define BRCMSTB_PRIV_FLAGS_HAS_CQE BIT(0) #define BRCMSTB_PRIV_FLAGS_GATE_CLOCK BIT(1) +#define BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS BIT(2) #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 +#define SDIO_CFG_CTRL 0x0 +#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31) +#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30) + +#define SDIO_CFG_SD_PIN_SEL 0x44 +#define SDIO_CFG_SD_PIN_SEL_MASK 0x3 +#define SDIO_CFG_SD_PIN_SEL_SD BIT(1) +#define SDIO_CFG_SD_PIN_SEL_MMC BIT(0) + +#define SDIO_CFG_CQ_CAPABILITY 0x4c +#define SDIO_CFG_CQ_CAPABILITY_FMUL_SHIFT 12 + +#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac +#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31) +#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0) + struct sdhci_brcmstb_priv { void __iomem *cfg_regs; unsigned int flags; struct clk *base_clk; u32 base_freq_hz; + struct regulator *sde_1v8; + struct device_node *sde_pcie; + void *__iomem sde_ioaddr; + void *__iomem sde_ioaddr2; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sdex; }; struct brcmstb_match_priv { void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); + void (*cfginit)(struct sdhci_host *host); struct sdhci_ops *ops; const unsigned int flags; }; @@ -79,6 +106,42 @@ static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios) writel(reg, host->ioaddr + SDHCI_VENDOR); } +static void sdhci_bcm2712_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + u32 reg; + bool is_emmc_rate = false; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host); + + host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + switch (host->mmc->ios.timing) { + case MMC_TIMING_MMC_HS400: + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_MMC_DDR52: + case MMC_TIMING_MMC_HS: + is_emmc_rate = true; + break; + } + + reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL); + reg &= ~SDIO_CFG_SD_PIN_SEL_MASK; + if (is_emmc_rate) + reg |= SDIO_CFG_SD_PIN_SEL_MMC; + else + reg |= SDIO_CFG_SD_PIN_SEL_SD; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + sdhci_enable_clk(host, clk); +} + static void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock) { u16 clk; @@ -94,6 +157,17 @@ static void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_enable_clk(host, clk); } +static void sdhci_brcmstb_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + if (!IS_ERR(host->mmc->supply.vmmc)) { + struct mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + } + sdhci_set_power_noreg(host, mode, vdd); +} + static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { @@ -123,6 +197,155 @@ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host, sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } +static void sdhci_bcm2712_hs400_downgrade(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + /* + * The eMMC PHY and its internal controller parses and validates + * the uhs_mode, divisor, pin_sel, and sampling clock select + * output from the SD controller. It will refuse to update its + * config if HS timings are selected while the clock is >52MHz. + * so bump the clock down now before card/controller setup is + * performed. + */ + sdhci_bcm2712_set_clock(host, 52000000); +} + +static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host); + u32 uhs_mask = (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104); + u32 hsemmc_mask = (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR | + MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V); + u32 reg, base_clk_mhz; + + /* + * If we support a speed that requires tuning, + * then select the delay line PHY as the clock source. + */ + if ((host->mmc->caps & uhs_mask) || (host->mmc->caps2 & hsemmc_mask)) { + reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE); + reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE; + reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE); + + host->mmc_host_ops.hs400_downgrade = sdhci_bcm2712_hs400_downgrade; + } + + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + (host->mmc->caps & MMC_CAP_NEEDS_POLL)) { + /* Force presence */ + reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL); + reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV; + reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL); + } + + /* Guesstimate the timer frequency (controller base clock) */ + base_clk_mhz = max_t(u32, clk_get_rate(pltfm_host->clk) / (1000 * 1000), 1); + reg = (3 << SDIO_CFG_CQ_CAPABILITY_FMUL_SHIFT) | base_clk_mhz; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CQ_CAPABILITY); +} + +static int bcm2712_init_sd_express(struct sdhci_host *host, struct mmc_ios *ios) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host); + struct device *dev = host->mmc->parent; + u32 ctrl_val; + u32 present_state; + int ret; + + if (!brcmstb_priv->sde_ioaddr || !brcmstb_priv->sde_ioaddr2) + return -EINVAL; + + if (!brcmstb_priv->pinctrl) + return -EINVAL; + + /* Turn off the SD clock first */ + sdhci_set_clock(host, 0); + + /* Disable SD DAT0-3 pulls */ + pinctrl_select_state(brcmstb_priv->pinctrl, brcmstb_priv->pins_sdex); + + ctrl_val = readl(brcmstb_priv->sde_ioaddr); + dev_dbg(dev, "ctrl_val 1 %08x\n", ctrl_val); + + /* Tri-state the SD pins */ + ctrl_val |= 0x1ff8; + writel(ctrl_val, brcmstb_priv->sde_ioaddr); + dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr)); + /* Let voltages settle */ + udelay(100); + + /* Enable the PCIe sideband pins */ + ctrl_val &= ~0x6000; + writel(ctrl_val, brcmstb_priv->sde_ioaddr); + dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr)); + /* Let voltages settle */ + udelay(100); + + /* Turn on the 1v8 VDD2 regulator */ + ret = regulator_enable(brcmstb_priv->sde_1v8); + if (ret) + return ret; + + /* Wait for Tpvcrl */ + msleep(1); + + /* Sample DAT2 (CLKREQ#) - if low, card is in PCIe mode */ + present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); + present_state = (present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT; + dev_dbg(dev, "state = 0x%08x\n", present_state); + + if (present_state & BIT(2)) { + dev_err(dev, "DAT2 still high, abandoning SDex switch\n"); + return -ENODEV; + } + + /* Turn on the LCPLL PTEST mux */ + ctrl_val = readl(brcmstb_priv->sde_ioaddr2 + 20); // misc5 + ctrl_val &= ~(0x7 << 7); + ctrl_val |= 3 << 7; + writel(ctrl_val, brcmstb_priv->sde_ioaddr2 + 20); + dev_dbg(dev, "misc 5->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2 + 20)); + + /* PTEST diff driver enable */ + ctrl_val = readl(brcmstb_priv->sde_ioaddr2); + ctrl_val |= BIT(21); + writel(ctrl_val, brcmstb_priv->sde_ioaddr2); + + dev_dbg(dev, "misc 0->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2)); + + /* Wait for more than the minimum Tpvpgl time */ + msleep(100); + + if (brcmstb_priv->sde_pcie) { + struct of_changeset changeset; + static struct property okay_property = { + .name = "status", + .value = "okay", + .length = 5, + }; + + /* Enable the pcie controller */ + of_changeset_init(&changeset); + ret = of_changeset_update_property(&changeset, + brcmstb_priv->sde_pcie, + &okay_property); + if (ret) { + dev_err(dev, "%s: failed to update property - %d\n", __func__, + ret); + return -ENODEV; + } + ret = of_changeset_apply(&changeset); + } + + dev_dbg(dev, "%s -> %d\n", __func__, ret); + return ret; +} + static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) { sdhci_dumpregs(mmc_priv(mmc)); @@ -131,6 +354,7 @@ static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) static void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); + struct cqhci_host *cq_host = mmc->cqe_private; u32 reg; reg = sdhci_readl(host, SDHCI_PRESENT_STATE); @@ -140,6 +364,22 @@ static void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc) } sdhci_cqe_enable(mmc); + + /* + * The controller resets this register to a very short default interval + * whenever CQHCI is disabled. + * + * For removable cards CBC needs to be clear or card removal can hang + * the CQE. In polling mode, a CIT of 0x4000 "cycles" seems to produce the best + * throughput. + * + * For nonremovable cards, the specification default of CBC=1 CIT=0x1000 + * suffices. + */ + if (mmc->caps & MMC_CAP_NONREMOVABLE) + cqhci_writel(cq_host, 0x00011000, CQHCI_SSC1); + else + cqhci_writel(cq_host, 0x00004000, CQHCI_SSC1); } static const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops = { @@ -155,6 +395,15 @@ static struct sdhci_ops sdhci_brcmstb_ops = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; +static struct sdhci_ops sdhci_brcmstb_ops_2712 = { + .set_clock = sdhci_bcm2712_set_clock, + .set_power = sdhci_brcmstb_set_power, + .set_bus_width = sdhci_set_bus_width, + .reset = brcmstb_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .init_sd_express = bcm2712_init_sd_express, +}; + static struct sdhci_ops sdhci_brcmstb_ops_7216 = { .set_clock = sdhci_brcmstb_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -179,10 +428,18 @@ static const struct brcmstb_match_priv match_priv_7216 = { .ops = &sdhci_brcmstb_ops_7216, }; -static const struct of_device_id sdhci_brcm_of_match[] = { +static const struct brcmstb_match_priv match_priv_2712 = { + .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, + .hs400es = sdhci_brcmstb_hs400es, + .cfginit = sdhci_brcmstb_cfginit_2712, + .ops = &sdhci_brcmstb_ops_2712, +}; + +static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, + { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 }, {}, }; @@ -256,6 +513,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) u32 actual_clock_mhz; struct sdhci_host *host; struct resource *iomem; + bool no_pinctrl = false; struct clk *clk; struct clk *base_clk = NULL; int res; @@ -265,34 +523,34 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible); - clk = devm_clk_get_optional(&pdev->dev, NULL); + clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) return dev_err_probe(&pdev->dev, PTR_ERR(clk), - "Failed to get clock from Device Tree\n"); - - res = clk_prepare_enable(clk); - if (res) - return res; + "Failed to get and enable clock from Device Tree\n"); memset(&brcmstb_pdata, 0, sizeof(brcmstb_pdata)); brcmstb_pdata.ops = match_priv->ops; host = sdhci_pltfm_init(pdev, &brcmstb_pdata, sizeof(struct sdhci_brcmstb_priv)); - if (IS_ERR(host)) { - res = PTR_ERR(host); - goto err_clk; - } + if (IS_ERR(host)) + return PTR_ERR(host); pltfm_host = sdhci_priv(host); + pltfm_host->clk = clk; + priv = sdhci_pltfm_priv(pltfm_host); if (device_property_read_bool(&pdev->dev, "supports-cqe")) { priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; } + priv->sde_pcie = of_parse_phandle(pdev->dev.of_node, + "sde-pcie", 0); + if (priv->sde_pcie) + priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS; + /* Map in the non-standard CFG registers */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem); + priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); if (IS_ERR(priv->cfg_regs)) { res = PTR_ERR(priv->cfg_regs); goto err; @@ -303,6 +561,43 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (res) goto err; + priv->sde_1v8 = devm_regulator_get_optional(&pdev->dev, "sde-1v8"); + if (IS_ERR(priv->sde_1v8)) + priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS; + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (iomem) { + priv->sde_ioaddr = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(priv->sde_ioaddr)) + priv->sde_ioaddr = NULL; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (iomem) { + priv->sde_ioaddr2 = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(priv->sde_ioaddr2)) + priv->sde_ioaddr = NULL; + } + + priv->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(priv->pinctrl)) { + no_pinctrl = true; + } + priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default"); + if (IS_ERR(priv->pins_default)) { + dev_dbg(&pdev->dev, "No pinctrl default state\n"); + no_pinctrl = true; + } + priv->pins_sdex = pinctrl_lookup_state(priv->pinctrl, "sd-express"); + if (IS_ERR(priv->pins_sdex)) { + dev_dbg(&pdev->dev, "No pinctrl sd-express state\n"); + no_pinctrl = true; + } + if (no_pinctrl || !priv->sde_ioaddr || !priv->sde_ioaddr2) { + priv->pinctrl = NULL; + priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS; + } + /* * Automatic clock gating does not work for SD cards that may * voltage switch so only enable it for non-removable devices. @@ -319,18 +614,23 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) (host->mmc->caps2 & MMC_CAP2_HS400_ES)) host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es; + if (host->ops->init_sd_express && + (priv->flags & BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS)) + host->mmc->caps2 |= MMC_CAP2_SD_EXP; + + if(match_priv->cfginit) + match_priv->cfginit(host); + /* * Supply the existing CAPS, but clear the UHS modes. This * will allow these modes to be specified by device tree * properties through mmc_of_parse(). */ - host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + sdhci_read_caps(host); if (match_priv->flags & BRCMSTB_MATCH_FLAGS_NO_64BIT) host->caps &= ~SDHCI_CAN_64BIT; - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_DDR50); - host->quirks |= SDHCI_QUIRK_MISSING_CAPS; if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; @@ -368,14 +668,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (res) goto err; - pltfm_host->clk = clk; return res; err: sdhci_pltfm_free(pdev); -err_clk: clk_disable_unprepare(base_clk); - clk_disable_unprepare(clk); return res; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 632341911b6e7..83169b8b0613e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3071,6 +3071,15 @@ static void sdhci_card_event(struct mmc_host *mmc) spin_unlock_irqrestore(&host->lock, flags); } +static int sdhci_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (!host->ops->init_sd_express) + return -EOPNOTSUPP; + return host->ops->init_sd_express(host, ios); +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .post_req = sdhci_post_req, @@ -3086,6 +3095,7 @@ static const struct mmc_host_ops sdhci_ops = { .execute_tuning = sdhci_execute_tuning, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, + .init_sd_express = sdhci_init_sd_express, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5ce7cdcc192fd..63f36be19eae2 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -663,6 +663,7 @@ struct sdhci_ops { void (*request_done)(struct sdhci_host *host, struct mmc_request *mrq); void (*dump_vendor_regs)(struct sdhci_host *host); + int (*init_sd_express)(struct sdhci_host *host, struct mmc_ios *ios); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 9c410f93a1039..2e259d91b9363 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -82,8 +82,11 @@ #define GEM_NCFGR 0x0004 /* Network Config */ #define GEM_USRIO 0x000c /* User IO */ #define GEM_DMACFG 0x0010 /* DMA Configuration */ +#define GEM_PBUFRXCUT 0x0044 /* RX Partial Store and Forward */ #define GEM_JML 0x0048 /* Jumbo Max Length */ #define GEM_HS_MAC_CONFIG 0x0050 /* GEM high speed config */ +#define GEM_AMP 0x0054 /* AXI Max Pipeline */ +#define GEM_INTMOD 0x005c /* Interrupt moderation */ #define GEM_HRB 0x0080 /* Hash Bottom */ #define GEM_HRT 0x0084 /* Hash Top */ #define GEM_SA1B 0x0088 /* Specific1 Bottom */ @@ -95,6 +98,8 @@ #define GEM_SA4B 0x00A0 /* Specific4 Bottom */ #define GEM_SA4T 0x00A4 /* Specific4 Top */ #define GEM_WOL 0x00b8 /* Wake on LAN */ +#define GEM_RXPTPUNI 0x00D4 /* PTP RX Unicast address */ +#define GEM_TXPTPUNI 0x00D8 /* PTP TX Unicast address */ #define GEM_EFTSH 0x00e8 /* PTP Event Frame Transmitted Seconds Register 47:32 */ #define GEM_EFRSH 0x00ec /* PTP Event Frame Received Seconds Register 47:32 */ #define GEM_PEFTSH 0x00f0 /* PTP Peer Event Frame Transmitted Seconds Register 47:32 */ @@ -245,6 +250,8 @@ #define MACB_TZQ_OFFSET 12 /* Transmit zero quantum pause frame */ #define MACB_TZQ_SIZE 1 #define MACB_SRTSM_OFFSET 15 /* Store Receive Timestamp to Memory */ +#define MACB_PTPUNI_OFFSET 20 /* PTP Unicast packet enable */ +#define MACB_PTPUNI_SIZE 1 #define MACB_OSSMODE_OFFSET 24 /* Enable One Step Synchro Mode */ #define MACB_OSSMODE_SIZE 1 #define MACB_MIIONRGMII_OFFSET 28 /* MII Usage on RGMII Interface */ @@ -342,6 +349,25 @@ #define GEM_ADDR64_OFFSET 30 /* Address bus width - 64b or 32b */ #define GEM_ADDR64_SIZE 1 +/* Bitfields in AMP */ +#define GEM_AR2R_MAX_PIPE_OFFSET 0 /* Maximum number of outstanding AXI read requests */ +#define GEM_AR2R_MAX_PIPE_SIZE 8 +#define GEM_AW2W_MAX_PIPE_OFFSET 8 /* Maximum number of outstanding AXI write requests */ +#define GEM_AW2W_MAX_PIPE_SIZE 8 +#define GEM_AW2B_FILL_OFFSET 16 /* Select wether the max AW2W transactions operates between: */ +#define GEM_AW2B_FILL_AW2W 0 /* 0: the AW to W AXI channel */ +#define GEM_AW2B_FILL_AW2B 1 /* 1: AW to B channel */ +#define GEM_AW2B_FILL_SIZE 1 + +/* Bitfields in INTMOD */ +#define GEM_RX_MODERATION_OFFSET 0 /* RX interrupt moderation */ +#define GEM_RX_MODERATION_SIZE 8 +#define GEM_TX_MODERATION_OFFSET 16 /* TX interrupt moderation */ +#define GEM_TX_MODERATION_SIZE 8 + +/* Bitfields in PBUFRXCUT */ +#define GEM_ENCUTTHRU_OFFSET 31 /* Enable RX partial store and forward */ +#define GEM_ENCUTTHRU_SIZE 1 /* Bitfields in NSR */ #define MACB_NSR_LINK_OFFSET 0 /* pcs_link_state */ @@ -509,6 +535,8 @@ #define GEM_TX_PKT_BUFF_OFFSET 21 #define GEM_TX_PKT_BUFF_SIZE 1 +#define GEM_RX_PBUF_ADDR_OFFSET 22 +#define GEM_RX_PBUF_ADDR_SIZE 4 /* Bitfields in DCFG5. */ #define GEM_TSU_OFFSET 8 @@ -517,6 +545,8 @@ /* Bitfields in DCFG6. */ #define GEM_PBUF_LSO_OFFSET 27 #define GEM_PBUF_LSO_SIZE 1 +#define GEM_PBUF_CUTTHRU_OFFSET 25 +#define GEM_PBUF_CUTTHRU_SIZE 1 #define GEM_DAW64_OFFSET 23 #define GEM_DAW64_SIZE 1 @@ -692,6 +722,8 @@ #define GEM_CLK_DIV48 3 #define GEM_CLK_DIV64 4 #define GEM_CLK_DIV96 5 +#define GEM_CLK_DIV128 6 +#define GEM_CLK_DIV224 7 /* Constants for MAN register */ #define MACB_MAN_C22_SOF 1 @@ -768,8 +800,6 @@ #define gem_readl_n(port, reg, idx) (port)->macb_reg_readl((port), GEM_##reg + idx * 4) #define gem_writel_n(port, reg, idx, value) (port)->macb_reg_writel((port), GEM_##reg + idx * 4, (value)) -#define PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ - /* Conditional GEM/MACB macros. These perform the operation to the correct * register dependent on whether the device is a GEM or a MACB. For registers * and bitfields that are common across both devices, use macb_{read,write}l @@ -794,6 +824,7 @@ }) #define MACB_READ_NSR(bp) macb_readl(bp, NSR) +#define MACB_READ_TSR(bp) macb_readl(bp, TSR) /* struct macb_dma_desc - Hardware DMA descriptor * @addr: DMA address of data buffer @@ -819,11 +850,6 @@ struct macb_dma_desc_ptp { u32 ts_1; u32 ts_2; }; - -struct gem_tx_ts { - struct sk_buff *skb; - struct macb_dma_desc_ptp desc_ptp; -}; #endif /* DMA descriptor bitfields */ @@ -1182,6 +1208,7 @@ struct macb_config { struct clk **hclk, struct clk **tx_clk, struct clk **rx_clk, struct clk **tsu_clk); int (*init)(struct platform_device *pdev); + unsigned int max_tx_length; int jumbo_max_len; const struct macb_usrio_config *usrio; }; @@ -1213,6 +1240,7 @@ struct macb_queue { dma_addr_t tx_ring_dma; struct work_struct tx_error_task; bool txubr_pending; + bool tx_pending; struct napi_struct napi_tx; dma_addr_t rx_ring_dma; @@ -1224,12 +1252,6 @@ struct macb_queue { void *rx_buffers; struct napi_struct napi_rx; struct queue_stats stats; - -#ifdef CONFIG_MACB_USE_HWSTAMP - struct work_struct tx_ts_task; - unsigned int tx_ts_head, tx_ts_tail; - struct gem_tx_ts tx_timestamps[PTP_TS_BUFFER_SIZE]; -#endif }; struct ethtool_rx_fs_item { @@ -1282,9 +1304,15 @@ struct macb { u32 caps; unsigned int dma_burst_length; + u8 aw2w_max_pipe; + u8 ar2r_max_pipe; + bool use_aw2b_fill; phy_interface_t phy_interface; + struct gpio_desc *phy_reset_gpio; + int phy_reset_ms; + /* AT91RM9200 transmit queue (1 on wire + 1 queued) */ struct macb_tx_skb rm9200_txq[2]; unsigned int max_tx_length; @@ -1296,6 +1324,9 @@ struct macb { u32 wol; + /* holds value of rx watermark value for pbuf_rxcutthru register */ + u32 rx_watermark; + struct macb_ptp_info *ptp_info; /* macb-ptp interface */ struct phy *sgmii_phy; /* for ZynqMP SGMII mode */ @@ -1340,14 +1371,14 @@ enum macb_bd_control { void gem_ptp_init(struct net_device *ndev); void gem_ptp_remove(struct net_device *ndev); -int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *des); +void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc); void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc); -static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc) +static inline void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { - if (queue->bp->tstamp_config.tx_type == TSTAMP_DISABLED) - return -ENOTSUPP; + if (bp->tstamp_config.tx_type == TSTAMP_DISABLED) + return; - return gem_ptp_txstamp(queue, skb, desc); + gem_ptp_txstamp(bp, skb, desc); } static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) @@ -1363,11 +1394,7 @@ int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd); static inline void gem_ptp_init(struct net_device *ndev) { } static inline void gem_ptp_remove(struct net_device *ndev) { } -static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc) -{ - return -1; -} - +static inline void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { } static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { } #endif @@ -1378,7 +1405,7 @@ static inline bool macb_is_gem(struct macb *bp) static inline bool gem_has_ptp(struct macb *bp) { - return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); + return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) && (bp->caps & MACB_CAPS_GEM_HAS_PTP); } /** diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index abd6cc0cd641f..373043b3ac101 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -41,6 +40,9 @@ #include #include "macb.h" +static unsigned int txdelay = 35; +module_param(txdelay, uint, 0644); + /* This structure is only used for MACB on SiFive FU540 devices */ struct sifive_fu540_macb_mgmt { void __iomem *reg; @@ -94,8 +96,7 @@ struct sifive_fu540_macb_mgmt { /* Graceful stop timeouts in us. We should allow up to * 1 frame time (10 Mbits/s, full-duplex, ignoring collisions) */ -#define MACB_HALT_TIMEOUT 1230 - +#define MACB_HALT_TIMEOUT 14000 #define MACB_PM_TIMEOUT 100 /* ms */ #define MACB_MDIO_TIMEOUT 1000000 /* in usecs */ @@ -288,6 +289,11 @@ static void macb_set_hwaddr(struct macb *bp) top = cpu_to_le16(*((u16 *)(bp->dev->dev_addr + 4))); macb_or_gem_writel(bp, SA1T, top); + if (gem_has_ptp(bp)) { + gem_writel(bp, RXPTPUNI, bottom); + gem_writel(bp, TXPTPUNI, bottom); + } + /* Clear unused address register sets */ macb_or_gem_writel(bp, SA2B, 0); macb_or_gem_writel(bp, SA2T, 0); @@ -331,10 +337,10 @@ static int macb_mdio_wait_for_idle(struct macb *bp) u32 val; return readx_poll_timeout(MACB_READ_NSR, bp, val, val & MACB_BIT(IDLE), - 1, MACB_MDIO_TIMEOUT); + 100, MACB_MDIO_TIMEOUT); } -static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +static int macb_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) { struct macb *bp = bus->priv; int status; @@ -347,31 +353,59 @@ static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum) if (status < 0) goto mdio_read_exit; - if (regnum & MII_ADDR_C45) { - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) - | MACB_BF(RW, MACB_MAN_C45_ADDR) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, (regnum >> 16) & 0x1F) - | MACB_BF(DATA, regnum & 0xFFFF) - | MACB_BF(CODE, MACB_MAN_C45_CODE))); - - status = macb_mdio_wait_for_idle(bp); - if (status < 0) - goto mdio_read_exit; - - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) - | MACB_BF(RW, MACB_MAN_C45_READ) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, (regnum >> 16) & 0x1F) - | MACB_BF(CODE, MACB_MAN_C45_CODE))); - } else { - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C22_SOF) - | MACB_BF(RW, MACB_MAN_C22_READ) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, regnum) - | MACB_BF(CODE, MACB_MAN_C22_CODE))); + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C22_SOF) + | MACB_BF(RW, MACB_MAN_C22_READ) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, regnum) + | MACB_BF(CODE, MACB_MAN_C22_CODE))); + + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_read_exit; + + status = MACB_BFEXT(DATA, macb_readl(bp, MAN)); + +mdio_read_exit: + pm_runtime_mark_last_busy(&bp->pdev->dev); + pm_runtime_put_autosuspend(&bp->pdev->dev); +mdio_pm_exit: + return status; +} + +#if 0 +static int macb_mdio_read_c45(struct mii_bus *bus, int mii_id, int devad, + int regnum) +{ + struct macb *bp = bus->priv; + int status; + + status = pm_runtime_get_sync(&bp->pdev->dev); + if (status < 0) { + pm_runtime_put_noidle(&bp->pdev->dev); + goto mdio_pm_exit; } + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_read_exit; + + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) + | MACB_BF(RW, MACB_MAN_C45_ADDR) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, devad & 0x1F) + | MACB_BF(DATA, regnum & 0xFFFF) + | MACB_BF(CODE, MACB_MAN_C45_CODE))); + + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_read_exit; + + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) + | MACB_BF(RW, MACB_MAN_C45_READ) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, devad & 0x1F) + | MACB_BF(CODE, MACB_MAN_C45_CODE))); + status = macb_mdio_wait_for_idle(bp); if (status < 0) goto mdio_read_exit; @@ -384,9 +418,10 @@ static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum) mdio_pm_exit: return status; } +#endif -static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum, - u16 value) +static int macb_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, + u16 value) { struct macb *bp = bus->priv; int status; @@ -399,43 +434,84 @@ static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum, if (status < 0) goto mdio_write_exit; - if (regnum & MII_ADDR_C45) { - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) - | MACB_BF(RW, MACB_MAN_C45_ADDR) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, (regnum >> 16) & 0x1F) - | MACB_BF(DATA, regnum & 0xFFFF) - | MACB_BF(CODE, MACB_MAN_C45_CODE))); - - status = macb_mdio_wait_for_idle(bp); - if (status < 0) - goto mdio_write_exit; - - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) - | MACB_BF(RW, MACB_MAN_C45_WRITE) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, (regnum >> 16) & 0x1F) - | MACB_BF(CODE, MACB_MAN_C45_CODE) - | MACB_BF(DATA, value))); - } else { - macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C22_SOF) - | MACB_BF(RW, MACB_MAN_C22_WRITE) - | MACB_BF(PHYA, mii_id) - | MACB_BF(REGA, regnum) - | MACB_BF(CODE, MACB_MAN_C22_CODE) - | MACB_BF(DATA, value))); + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C22_SOF) + | MACB_BF(RW, MACB_MAN_C22_WRITE) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, regnum) + | MACB_BF(CODE, MACB_MAN_C22_CODE) + | MACB_BF(DATA, value))); + + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_write_exit; + +mdio_write_exit: + pm_runtime_mark_last_busy(&bp->pdev->dev); + pm_runtime_put_autosuspend(&bp->pdev->dev); +mdio_pm_exit: + return status; +} + +#if 0 +static int macb_mdio_write_c45(struct mii_bus *bus, int mii_id, + int devad, int regnum, + u16 value) +{ + struct macb *bp = bus->priv; + int status; + + status = pm_runtime_get_sync(&bp->pdev->dev); + if (status < 0) { + pm_runtime_put_noidle(&bp->pdev->dev); + goto mdio_pm_exit; } status = macb_mdio_wait_for_idle(bp); if (status < 0) goto mdio_write_exit; + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) + | MACB_BF(RW, MACB_MAN_C45_ADDR) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, devad & 0x1F) + | MACB_BF(DATA, regnum & 0xFFFF) + | MACB_BF(CODE, MACB_MAN_C45_CODE))); + + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_write_exit; + + macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_C45_SOF) + | MACB_BF(RW, MACB_MAN_C45_WRITE) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, devad & 0x1F) + | MACB_BF(CODE, MACB_MAN_C45_CODE) + | MACB_BF(DATA, value))); + + status = macb_mdio_wait_for_idle(bp); + if (status < 0) + goto mdio_write_exit; + mdio_write_exit: pm_runtime_mark_last_busy(&bp->pdev->dev); pm_runtime_put_autosuspend(&bp->pdev->dev); mdio_pm_exit: return status; } +#endif + +static int macb_mdio_reset(struct mii_bus *bus) +{ + struct macb *bp = bus->priv; + + if (bp->phy_reset_gpio) { + gpiod_set_value_cansleep(bp->phy_reset_gpio, 1); + msleep(bp->phy_reset_ms); + gpiod_set_value_cansleep(bp->phy_reset_gpio, 0); + } + + return 0; +} static void macb_init_buffers(struct macb *bp) { @@ -506,7 +582,7 @@ static void macb_set_tx_clk(struct macb *bp, int speed) netdev_err(bp->dev, "adjusting tx_clk failed.\n"); } -static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, int speed, int duplex) { @@ -539,7 +615,7 @@ static void macb_usx_pcs_get_state(struct phylink_pcs *pcs, } static int macb_usx_pcs_config(struct phylink_pcs *pcs, - unsigned int mode, + unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) @@ -564,7 +640,7 @@ static void macb_pcs_an_restart(struct phylink_pcs *pcs) } static int macb_pcs_config(struct phylink_pcs *pcs, - unsigned int mode, + unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) @@ -700,8 +776,6 @@ static void macb_mac_link_up(struct phylink_config *config, if (rx_pause) ctrl |= MACB_BIT(PAE); - macb_set_tx_clk(bp, speed); - /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ @@ -721,8 +795,15 @@ static void macb_mac_link_up(struct phylink_config *config, spin_unlock_irqrestore(&bp->lock, flags); - /* Enable Rx and Tx */ - macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(RE) | MACB_BIT(TE)); + if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) + macb_set_tx_clk(bp, speed); + + /* Enable Rx and Tx; Enable PTP unicast */ + ctrl = macb_readl(bp, NCR); + if (gem_has_ptp(bp)) + ctrl |= MACB_BIT(PTPUNI); + + macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE)); netif_tx_wake_all_queues(ndev); } @@ -802,7 +883,9 @@ static int macb_mii_probe(struct net_device *dev) struct macb *bp = netdev_priv(dev); bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops; + //bp->phylink_sgmii_pcs.neg_mode = true; bp->phylink_usx_pcs.ops = &macb_phylink_usx_pcs_ops; + //bp->phylink_usx_pcs.neg_mode = true; bp->phylink_config.dev = &dev->dev; bp->phylink_config.type = PHYLINK_NETDEV; @@ -903,8 +986,11 @@ static int macb_mii_init(struct macb *bp) } bp->mii_bus->name = "MACB_mii_bus"; - bp->mii_bus->read = &macb_mdio_read; - bp->mii_bus->write = &macb_mdio_write; + bp->mii_bus->read = &macb_mdio_read_c22; + bp->mii_bus->write = &macb_mdio_write_c22; + //bp->mii_bus->read_c45 = &macb_mdio_read_c45; + //bp->mii_bus->write_c45 = &macb_mdio_write_c45; + bp->mii_bus->reset = &macb_mdio_reset; snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", bp->pdev->name, bp->pdev->id); bp->mii_bus->priv = bp; @@ -1021,6 +1107,7 @@ static void macb_tx_error_task(struct work_struct *work) { struct macb_queue *queue = container_of(work, struct macb_queue, tx_error_task); + bool halt_timeout = false; struct macb *bp = queue->bp; struct macb_tx_skb *tx_skb; struct macb_dma_desc *desc; @@ -1048,9 +1135,11 @@ static void macb_tx_error_task(struct work_struct *work) * (in case we have just queued new packets) * macb/gem must be halted to write TBQP register */ - if (macb_halt_tx(bp)) - /* Just complain for now, reinitializing TX path can be good */ + if (macb_halt_tx(bp)) { netdev_err(bp->dev, "BUG: halt tx timed out\n"); + macb_writel(bp, NCR, macb_readl(bp, NCR) & (~MACB_BIT(TE))); + halt_timeout = true; + } /* Treat frames in TX queue including the ones that caused the error. * Free transmit buffers in upper layer. @@ -1121,6 +1210,9 @@ static void macb_tx_error_task(struct work_struct *work) macb_writel(bp, TSR, macb_readl(bp, TSR)); queue_writel(queue, IER, MACB_TX_INT_FLAGS); + if (halt_timeout) + macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TE)); + /* Now we are ready to start transmission again */ netif_tx_start_all_queues(bp->dev); macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); @@ -1196,13 +1288,9 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) /* First, update TX stats if needed */ if (skb) { if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && - !ptp_one_step_sync(skb) && - gem_ptp_do_txstamp(queue, skb, desc) == 0) { - /* skb now belongs to timestamp buffer - * and will be removed later - */ - tx_skb->skb = NULL; - } + !ptp_one_step_sync(skb)) + gem_ptp_do_txstamp(bp, skb, desc); + netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", macb_tx_ring_wrap(bp, tail), skb->data); @@ -1574,6 +1662,11 @@ static int macb_rx(struct macb_queue *queue, struct napi_struct *napi, macb_init_rx_ring(queue); queue_writel(queue, RBQP, queue->rx_ring_dma); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (bp->hw_dma_cap & HW_DMA_CAP_64B) + macb_writel(bp, RBQPH, + upper_32_bits(queue->rx_ring_dma)); +#endif macb_writel(bp, NCR, ctrl | MACB_BIT(RE)); @@ -1874,8 +1967,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) queue_writel(queue, ISR, MACB_BIT(TCOMP) | MACB_BIT(TXUBR)); - if (status & MACB_BIT(TXUBR)) { + if (status & MACB_BIT(TXUBR) || queue->tx_pending) { queue->txubr_pending = true; + queue->tx_pending = 0; wmb(); // ensure softirq can see update } @@ -2258,6 +2352,12 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } +#ifdef CONFIG_MACB_USE_HWSTAMP + if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + (bp->hw_dma_cap & HW_DMA_CAP_PTP)) + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; +#endif + is_lso = (skb_shinfo(skb)->gso_size != 0); if (is_lso) { @@ -2322,6 +2422,11 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); spin_lock_irq(&bp->lock); + + /* TSTART write might get dropped, so make the IRQ retrigger a buffer read */ + if (macb_readl(bp, TSR) & MACB_BIT(TGO)) + queue->tx_pending = 1; + macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); spin_unlock_irq(&bp->lock); @@ -2565,6 +2670,9 @@ static void macb_reset_hw(struct macb *bp) macb_writel(bp, TSR, -1); macb_writel(bp, RSR, -1); + /* Disable RX partial store and forward and reset watermark value */ + gem_writel(bp, PBUFRXCUT, 0); + /* Disable all interrupts */ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { queue_writel(queue, IDR, -1); @@ -2589,8 +2697,12 @@ static u32 gem_mdc_clk_div(struct macb *bp) config = GEM_BF(CLK, GEM_CLK_DIV48); else if (pclk_hz <= 160000000) config = GEM_BF(CLK, GEM_CLK_DIV64); - else + else if (pclk_hz <= 240000000) config = GEM_BF(CLK, GEM_CLK_DIV96); + else if (pclk_hz <= 320000000) + config = GEM_BF(CLK, GEM_CLK_DIV128); + else + config = GEM_BF(CLK, GEM_CLK_DIV224); return config; } @@ -2689,6 +2801,37 @@ static void macb_configure_dma(struct macb *bp) } } +static void gem_init_axi(struct macb *bp) +{ + u32 amp; + + /* AXI pipeline setup - don't touch values unless specified in device + * tree. Some hardware could have reset values > 1. + */ + amp = gem_readl(bp, AMP); + + if (bp->use_aw2b_fill) + amp = GEM_BFINS(AW2B_FILL, bp->use_aw2b_fill, amp); + if (bp->aw2w_max_pipe) + amp = GEM_BFINS(AW2W_MAX_PIPE, bp->aw2w_max_pipe, amp); + if (bp->ar2r_max_pipe) + amp = GEM_BFINS(AR2R_MAX_PIPE, bp->ar2r_max_pipe, amp); + + gem_writel(bp, AMP, amp); +} + +static void gem_init_intmod(struct macb *bp) +{ + unsigned int throttle; + u32 intmod = 0; + + /* Use sensible interrupt moderation thresholds (50us rx and tx) */ + throttle = (1000 * 50) / 800; + intmod = GEM_BFINS(TX_MODERATION, throttle, intmod); + intmod = GEM_BFINS(RX_MODERATION, throttle, intmod); + gem_writel(bp, INTMOD, intmod); +} + static void macb_init_hw(struct macb *bp) { u32 config; @@ -2717,7 +2860,16 @@ static void macb_init_hw(struct macb *bp) if (bp->caps & MACB_CAPS_JUMBO) bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK; + if (macb_is_gem(bp)) { + gem_init_axi(bp); + gem_init_intmod(bp); + } + macb_configure_dma(bp); + + /* Enable RX partial store and forward and set watermark */ + if (bp->rx_watermark) + gem_writel(bp, PBUFRXCUT, (bp->rx_watermark | GEM_BIT(ENCUTTHRU))); } /* The hash address register is 64 bits long and takes up two @@ -2944,6 +3096,18 @@ static int macb_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static int macb_set_mac_addr(struct net_device *dev, void *addr) +{ + int err; + + err = eth_mac_addr(dev, addr); + if (err < 0) + return err; + + macb_set_hwaddr(netdev_priv(dev)); + return 0; +} + static void gem_update_stats(struct macb *bp) { struct macb_queue *queue; @@ -3062,6 +3226,52 @@ static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) } } +static int gem_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct macb *bp = netdev_priv(dev); + unsigned int tx_throttle; + unsigned int rx_throttle; + u32 intmod = 0; + + /* GEM has simple IRQ throttling support. RX and TX interrupts + * are separately moderated on 800ns quantums, with no support + * for frame coalescing. + */ + + /* Max is 255 * 0.8us = 204us. Zero implies no moderation. */ + if (ec->rx_coalesce_usecs > 204 || ec->tx_coalesce_usecs > 204) + return -EINVAL; + + tx_throttle = (1000 * ec->tx_coalesce_usecs) / 800; + rx_throttle = (1000 * ec->rx_coalesce_usecs) / 800; + + intmod = GEM_BFINS(TX_MODERATION, tx_throttle, intmod); + intmod = GEM_BFINS(RX_MODERATION, rx_throttle, intmod); + + gem_writel(bp, INTMOD, intmod); + + return 0; +} + +static int gem_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct macb *bp = netdev_priv(dev); + u32 intmod; + + intmod = gem_readl(bp, INTMOD); + + ec->tx_coalesce_usecs = (GEM_BFEXT(TX_MODERATION, intmod) * 800) / 1000; + ec->rx_coalesce_usecs = (GEM_BFEXT(RX_MODERATION, intmod) * 800) / 1000; + + return 0; +} + static struct net_device_stats *macb_get_stats(struct net_device *dev) { struct macb *bp = netdev_priv(dev); @@ -3654,6 +3864,8 @@ static const struct ethtool_ops macb_ethtool_ops = { }; static const struct ethtool_ops gem_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | + ETHTOOL_COALESCE_TX_USECS, .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, .get_wol = macb_get_wol, @@ -3663,6 +3875,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .get_ethtool_stats = gem_get_ethtool_stats, .get_strings = gem_get_ethtool_strings, .get_sset_count = gem_get_sset_count, + .get_coalesce = gem_get_coalesce, + .set_coalesce = gem_set_coalesce, .get_link_ksettings = macb_get_link_ksettings, .set_link_ksettings = macb_set_link_ksettings, .get_ringparam = macb_get_ringparam, @@ -3783,7 +3997,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_eth_ioctl = macb_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = macb_change_mtu, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = macb_set_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = macb_poll_controller, #endif @@ -3816,17 +4030,17 @@ static void macb_configure_caps(struct macb *bp, dcfg = gem_readl(bp, DCFG2); if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0) bp->caps |= MACB_CAPS_FIFO_MODE; -#ifdef CONFIG_MACB_USE_HWSTAMP if (gem_has_ptp(bp)) { if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) dev_err(&bp->pdev->dev, "GEM doesn't support hardware ptp.\n"); else { +#ifdef CONFIG_MACB_USE_HWSTAMP bp->hw_dma_cap |= HW_DMA_CAP_PTP; bp->ptp_info = &gem_ptp_info; +#endif } } -#endif } dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); @@ -3875,6 +4089,7 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, struct macb_platform_data *pdata; int err; + pdata = dev_get_platdata(&pdev->dev); if (pdata) { *pclk = pdata->pclk; @@ -4031,14 +4246,12 @@ static int macb_init(struct platform_device *pdev) /* setup appropriated routines according to adapter type */ if (macb_is_gem(bp)) { - bp->max_tx_length = GEM_MAX_TX_LEN; bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; bp->macbgem_ops.mog_init_rings = gem_init_rings; bp->macbgem_ops.mog_rx = gem_rx; dev->ethtool_ops = &gem_ethtool_ops; } else { - bp->max_tx_length = MACB_MAX_TX_LEN; bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; bp->macbgem_ops.mog_init_rings = macb_init_rings; @@ -4046,6 +4259,8 @@ static int macb_init(struct platform_device *pdev) dev->ethtool_ops = &macb_ethtool_ops; } + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + /* Set features */ dev->hw_features = NETIF_F_SG; @@ -4773,12 +4988,13 @@ static const struct macb_config mpfs_config = { .clk_init = macb_clk_init, .init = init_reset_optional, .usrio = &macb_default_usrio, - .jumbo_max_len = 10240, + .max_tx_length = 4040, /* Cadence Erratum 1686 */ + .jumbo_max_len = 4040, }; static const struct macb_config sama7g5_gem_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | - MACB_CAPS_MIIONRGMII, + MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, @@ -4787,7 +5003,8 @@ static const struct macb_config sama7g5_gem_config = { static const struct macb_config sama7g5_emac_config = { .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | - MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_MIIONRGMII, + MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_MIIONRGMII | + MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, @@ -4858,10 +5075,12 @@ static int macb_probe(struct platform_device *pdev) phy_interface_t interface; struct net_device *dev; struct resource *regs; + u32 wtrmrk_rst_val; void __iomem *mem; struct macb *bp; int err, val; + mem = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(mem)) return PTR_ERR(mem); @@ -4923,13 +5142,44 @@ static int macb_probe(struct platform_device *pdev) if (macb_config) bp->jumbo_max_len = macb_config->jumbo_max_len; + if (!hw_is_gem(bp->regs, bp->native_io)) + bp->max_tx_length = MACB_MAX_TX_LEN; + else if (macb_config->max_tx_length) + bp->max_tx_length = macb_config->max_tx_length; + else + bp->max_tx_length = GEM_MAX_TX_LEN; + bp->wol = 0; - if (of_get_property(np, "magic-packet", NULL)) + if (of_property_read_bool(np, "magic-packet")) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_set_wakeup_capable(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); bp->usrio = macb_config->usrio; + /* By default we set to partial store and forward mode for zynqmp. + * Disable if not set in devicetree. + */ + if (GEM_BFEXT(PBUF_CUTTHRU, gem_readl(bp, DCFG6))) { + err = of_property_read_u32(bp->pdev->dev.of_node, + "cdns,rx-watermark", + &bp->rx_watermark); + + if (!err) { + /* Disable partial store and forward in case of error or + * invalid watermark value + */ + wtrmrk_rst_val = (1 << (GEM_BFEXT(RX_PBUF_ADDR, gem_readl(bp, DCFG2)))) - 1; + if (bp->rx_watermark > wtrmrk_rst_val || !bp->rx_watermark) { + dev_info(&bp->pdev->dev, "Invalid watermark value\n"); + bp->rx_watermark = 0; + } + } + } + + device_property_read_u8(&pdev->dev, "cdns,aw2w-max-pipe", &bp->aw2w_max_pipe); + device_property_read_u8(&pdev->dev, "cdns,ar2r-max-pipe", &bp->ar2r_max_pipe); + bp->use_aw2b_fill = device_property_read_bool(&pdev->dev, "cdns,use-aw2b-fill"); + spin_lock_init(&bp->lock); /* setup capabilities */ @@ -4985,6 +5235,21 @@ static int macb_probe(struct platform_device *pdev) else bp->phy_interface = interface; + /* optional PHY reset-related properties */ + bp->phy_reset_gpio = devm_gpiod_get_optional(&pdev->dev, "phy-reset", + GPIOD_OUT_LOW); + if (IS_ERR(bp->phy_reset_gpio)) { + dev_err(&pdev->dev, "Failed to obtain phy-reset gpio\n"); + err = PTR_ERR(bp->phy_reset_gpio); + goto err_out_free_netdev; + } + + bp->phy_reset_ms = 10; + of_property_read_u32(np, "phy-reset-duration", &bp->phy_reset_ms); + /* A sane reset duration should not be longer than 1s */ + if (bp->phy_reset_ms > 1000) + bp->phy_reset_ms = 1000; + /* IP specific init */ err = init(pdev); if (err) @@ -5061,6 +5326,19 @@ static int macb_remove(struct platform_device *pdev) return 0; } +static void macb_shutdown(struct platform_device *pdev) +{ + struct net_device *dev; + + dev = platform_get_drvdata(pdev); + + rtnl_lock(); + netif_device_detach(dev); + if (netif_running(dev)) + dev_close(dev); + rtnl_unlock(); +} + static int __maybe_unused macb_suspend(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); @@ -5070,6 +5348,9 @@ static int __maybe_unused macb_suspend(struct device *dev) unsigned int q; int err; + if (!device_may_wakeup(&bp->dev->dev)) + phy_exit(bp->sgmii_phy); + if (!netif_running(netdev)) return 0; @@ -5130,7 +5411,6 @@ static int __maybe_unused macb_suspend(struct device *dev) if (!(bp->wol & MACB_WOL_ENABLED)) { rtnl_lock(); phylink_stop(bp->phylink); - phy_exit(bp->sgmii_phy); rtnl_unlock(); spin_lock_irqsave(&bp->lock, flags); macb_reset_hw(bp); @@ -5160,6 +5440,9 @@ static int __maybe_unused macb_resume(struct device *dev) unsigned int q; int err; + if (!device_may_wakeup(&bp->dev->dev)) + phy_init(bp->sgmii_phy); + if (!netif_running(netdev)) return 0; @@ -5220,8 +5503,6 @@ static int __maybe_unused macb_resume(struct device *dev) macb_set_rx_mode(netdev); macb_restore_features(bp); rtnl_lock(); - if (!device_may_wakeup(&bp->dev->dev)) - phy_init(bp->sgmii_phy); phylink_start(bp->phylink); rtnl_unlock(); @@ -5272,6 +5553,7 @@ static const struct dev_pm_ops macb_pm_ops = { static struct platform_driver macb_driver = { .probe = macb_probe, .remove = macb_remove, + .shutdown = macb_shutdown, .driver = { .name = "macb", .of_match_table = of_match_ptr(macb_dt_ids), diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index e6cb20aaa76a8..51d26fa190d7d 100644 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -258,6 +258,8 @@ static int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, */ gem_tsu_get_time(&bp->ptp_clock_info, &tsu, NULL); + ts->tv_sec |= ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); + /* If the top bit is set in the timestamp, * but not in 1588 timer, it has rolled over, * so subtract max size @@ -266,8 +268,6 @@ static int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) ts->tv_sec -= GEM_DMA_SEC_TOP; - ts->tv_sec += ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); - return 0; } @@ -292,79 +292,39 @@ void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, } } -static void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb, - struct macb_dma_desc_ptp *desc_ptp) +void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb, + struct macb_dma_desc *desc) { struct skb_shared_hwtstamps shhwtstamps; - struct timespec64 ts; - - gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); - skb_tstamp_tx(skb, &shhwtstamps); -} - -int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, - struct macb_dma_desc *desc) -{ - unsigned long tail = READ_ONCE(queue->tx_ts_tail); - unsigned long head = queue->tx_ts_head; struct macb_dma_desc_ptp *desc_ptp; - struct gem_tx_ts *tx_timestamp; - - if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) - return -EINVAL; + struct timespec64 ts; - if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0) - return -ENOMEM; + if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) { + dev_warn_ratelimited(&bp->pdev->dev, + "Timestamp not set in TX BD as expected\n"); + return; + } - desc_ptp = macb_ptp_desc(queue->bp, desc); + desc_ptp = macb_ptp_desc(bp, desc); /* Unlikely but check */ - if (!desc_ptp) - return -EINVAL; - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - tx_timestamp = &queue->tx_timestamps[head]; - tx_timestamp->skb = skb; + if (!desc_ptp) { + dev_warn_ratelimited(&bp->pdev->dev, + "Timestamp not supported in BD\n"); + return; + } + /* ensure ts_1/ts_2 is loaded after ctrl (TX_USED check) */ dma_rmb(); - tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1; - tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2; - /* move head */ - smp_store_release(&queue->tx_ts_head, - (head + 1) & (PTP_TS_BUFFER_SIZE - 1)); - - schedule_work(&queue->tx_ts_task); - return 0; -} + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); -static void gem_tx_timestamp_flush(struct work_struct *work) -{ - struct macb_queue *queue = - container_of(work, struct macb_queue, tx_ts_task); - unsigned long head, tail; - struct gem_tx_ts *tx_ts; - - /* take current head */ - head = smp_load_acquire(&queue->tx_ts_head); - tail = queue->tx_ts_tail; - - while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) { - tx_ts = &queue->tx_timestamps[tail]; - gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp); - /* cleanup */ - dev_kfree_skb_any(tx_ts->skb); - /* remove old tail */ - smp_store_release(&queue->tx_ts_tail, - (tail + 1) & (PTP_TS_BUFFER_SIZE - 1)); - tail = queue->tx_ts_tail; - } + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); } void gem_ptp_init(struct net_device *dev) { struct macb *bp = netdev_priv(dev); - struct macb_queue *queue; - unsigned int q; bp->ptp_clock_info = gem_ptp_caps_template; @@ -384,11 +344,6 @@ void gem_ptp_init(struct net_device *dev) } spin_lock_init(&bp->tsu_clk_lock); - for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - queue->tx_ts_head = 0; - queue->tx_ts_tail = 0; - INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush); - } gem_ptp_init_tsu(bp); diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index ad71c88c87e78..731dee12eb279 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -14,8 +14,12 @@ #include #include #include +#include #include #include +#include +#include +#include #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) @@ -30,8 +34,17 @@ MODULE_LICENSE("GPL"); struct bcm54xx_phy_priv { u64 *stats; struct bcm_ptp_private *ptp; + int wake_irq; + bool wake_irq_enabled; }; +static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev) +{ + struct bcm54xx_phy_priv *priv = phydev->priv; + + return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0; +} + static int bcm54xx_config_clock_delay(struct phy_device *phydev) { int rc, val; @@ -88,6 +101,11 @@ static int bcm54210e_config_init(struct phy_device *phydev) return 0; } +static int bcm54213pe_config_init(struct phy_device *phydev) +{ + return bcm54210e_config_init(phydev); +} + static int bcm54612e_config_init(struct phy_device *phydev) { int reg; @@ -259,7 +277,8 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 && - BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) + BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811 && + BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54213PE) return; val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); @@ -337,6 +356,9 @@ static void bcm54xx_ptp_config_init(struct phy_device *phydev) static int bcm54xx_config_init(struct phy_device *phydev) { int reg, err, val; + u32 led_modes[] = {BCM_LED_MULTICOLOR_LINK_ACT, + BCM_LED_MULTICOLOR_LINK}; + struct device_node *np = phydev->mdio.dev.of_node; reg = phy_read(phydev, MII_BCM54XX_ECR); if (reg < 0) @@ -361,6 +383,9 @@ static int bcm54xx_config_init(struct phy_device *phydev) (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0); + if (of_property_read_bool(np, "brcm,powerdown-enable")) + phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE; + bcm54xx_adjust_rxrefclk(phydev); switch (BRCM_PHY_MODEL(phydev)) { @@ -374,6 +399,9 @@ static int bcm54xx_config_init(struct phy_device *phydev) case PHY_ID_BCM54612E: err = bcm54612e_config_init(phydev); break; + case PHY_ID_BCM54213PE: + err = bcm54213pe_config_init(phydev); + break; case PHY_ID_BCM54616S: err = bcm54616s_config_init(phydev); break; @@ -392,27 +420,42 @@ static int bcm54xx_config_init(struct phy_device *phydev) bcm54xx_phydsp_config(phydev); + of_property_read_u32_array(np, "led-modes", led_modes, 2); + /* For non-SFP setups, encode link speed into LED1 and LED3 pair * (green/amber). - * Also flash these two LEDs on activity. This means configuring - * them for MULTICOLOR and encoding link/activity into them. * Don't do this for devices on an SFP module, since some of these * use the LED outputs to control the SFP LOS signal, and changing * these settings will cause LOS to malfunction. */ if (!phy_on_sfp(phydev)) { - val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) | - BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1); - bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val); + val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) | + BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val); + /* BCM54210PE controls two extra LEDs with the next register. + * Make them shadow the first pair of LEDs - useful on CM4 which + * uses LED3 for ETH_LEDY instead of LED1. + */ + bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1 + 1, val); val = BCM_LED_MULTICOLOR_IN_PHASE | - BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) | - BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT); + BCM54XX_SHD_LEDS1_LED1(led_modes[0]) | + BCM54XX_SHD_LEDS1_LED3(led_modes[1]); bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val); } bcm54xx_ptp_config_init(phydev); + /* Acknowledge any left over interrupt and charge the device for + * wake-up. + */ + err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); + if (err < 0) + return err; + + if (err) + pm_wakeup_event(&phydev->mdio.dev, 0); + return 0; } @@ -437,12 +480,41 @@ static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable) return ret; } +static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state) +{ + struct bcm54xx_phy_priv *priv = phydev->priv; + int ret = 0; + + if (!bcm54xx_phy_can_wakeup(phydev)) + return ret; + + if (priv->wake_irq_enabled != state) { + if (state) + ret = enable_irq_wake(priv->wake_irq); + else + ret = disable_irq_wake(priv->wake_irq); + priv->wake_irq_enabled = state; + } + + return ret; +} + static int bcm54xx_suspend(struct phy_device *phydev) { - int ret; + int ret = 0; bcm54xx_ptp_stop(phydev); +#if 0 + /* Acknowledge any Wake-on-LAN interrupt prior to suspend */ + ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); + if (ret < 0) + return ret; + + if (phydev->wol_enabled) + return bcm54xx_set_wakeup_irq(phydev, true); +#endif + /* We cannot use a read/modify/write here otherwise the PHY gets into * a bad state where its LEDs keep flashing, thus defeating the purpose * of low power mode. @@ -456,11 +528,19 @@ static int bcm54xx_suspend(struct phy_device *phydev) static int bcm54xx_resume(struct phy_device *phydev) { - int ret; + int ret = 0; + +#if 0 + if (phydev->wol_enabled) { + ret = bcm54xx_set_wakeup_irq(phydev, false); + if (ret) + return ret; + } ret = bcm54xx_iddq_set(phydev, false); if (ret < 0) return ret; +#endif /* Writes to register other than BMCR would be ignored * unless we clear the PDOWN bit first @@ -486,6 +566,17 @@ static int bcm54xx_resume(struct phy_device *phydev) return bcm54xx_config_init(phydev); } +static int bcm54810_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) +{ + return -EOPNOTSUPP; +} + +static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, + u16 val) +{ + return -EOPNOTSUPP; +} + static int bcm54811_config_init(struct phy_device *phydev) { int err, reg; @@ -608,17 +699,6 @@ static int bcm54616s_read_status(struct phy_device *phydev) return err; } -static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set) -{ - int val; - - val = phy_read(phydev, reg); - if (val < 0) - return val; - - return phy_write(phydev, reg, val | set); -} - static int brcm_fet_config_init(struct phy_device *phydev) { int reg, err, err2, brcmtest; @@ -689,15 +769,15 @@ static int brcm_fet_config_init(struct phy_device *phydev) goto done; /* Enable auto MDIX */ - err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL, - MII_BRCM_FET_SHDW_MC_FAME); + err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL, + MII_BRCM_FET_SHDW_MC_FAME); if (err < 0) goto done; if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) { /* Enable auto power down */ - err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, - MII_BRCM_FET_SHDW_AS2_APDE); + err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, + MII_BRCM_FET_SHDW_AS2_APDE); } done: @@ -801,14 +881,58 @@ static int brcm_fet_suspend(struct phy_device *phydev) return err; } +#if 0 +static void bcm54xx_phy_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + /* We cannot wake-up if we do not have a dedicated PHY interrupt line + * or an out of band GPIO descriptor for wake-up. Zeroing + * wol->supported allows the caller (MAC driver) to play through and + * offer its own Wake-on-LAN scheme if available. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) { + wol->supported = 0; + return; + } + + bcm_phy_get_wol(phydev, wol); +} + +static int bcm54xx_phy_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret; + + /* We cannot wake-up if we do not have a dedicated PHY interrupt line + * or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP + * allows the caller (MAC driver) to play through and offer its own + * Wake-on-LAN scheme if available. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) + return -EOPNOTSUPP; + + ret = bcm_phy_set_wol(phydev, wol); + if (ret < 0) + return ret; + + return 0; +} +#endif + static int bcm54xx_phy_probe(struct phy_device *phydev) { struct bcm54xx_phy_priv *priv; +#if 0 + struct gpio_desc *wakeup_gpio; + int ret = 0; +#endif priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->wake_irq = -ENXIO; + phydev->priv = priv; priv->stats = devm_kcalloc(&phydev->mdio.dev, @@ -822,6 +946,37 @@ static int bcm54xx_phy_probe(struct phy_device *phydev) return PTR_ERR(priv->ptp); return 0; +#if 0 + /* We cannot utilize the _optional variant here since we want to know + * whether the GPIO descriptor exists or not to advertise Wake-on-LAN + * support or not. + */ + wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN); + if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER) + return PTR_ERR(wakeup_gpio); + + if (!IS_ERR(wakeup_gpio)) { + priv->wake_irq = gpiod_to_irq(wakeup_gpio); + + /* Dummy interrupt handler which is not enabled but is provided + * in order for the interrupt descriptor to be fully set-up. + */ + ret = devm_request_irq(&phydev->mdio.dev, priv->wake_irq, + bcm_phy_wol_isr, + IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN, + dev_name(&phydev->mdio.dev), phydev); + if (ret) + return ret; + } + + /* If we do not have a main interrupt or a side-band wake-up interrupt, + * then the device cannot be marked as wake-up capable. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) + return 0; + + return device_init_wakeup(&phydev->mdio.dev, true); +#endif } static void bcm54xx_get_stats(struct phy_device *phydev, @@ -891,9 +1046,12 @@ static struct phy_driver broadcom_drivers[] = { .link_change_notify = bcm54xx_link_change_notify, }, { .phy_id = PHY_ID_BCM54210E, - .phy_id_mask = 0xfffffff0, + .phy_id_mask = 0xffffffff, .name = "Broadcom BCM54210E", /* PHY_GBIT_FEATURES */ +#if 0 + .flags = PHY_ALWAYS_CALL_SUSPEND, +#endif .get_sset_count = bcm_phy_get_sset_count, .get_strings = bcm_phy_get_strings, .get_stats = bcm54xx_get_stats, @@ -904,6 +1062,24 @@ static struct phy_driver broadcom_drivers[] = { .link_change_notify = bcm54xx_link_change_notify, .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, +#if 0 + .get_wol = bcm54xx_phy_get_wol, + .set_wol = bcm54xx_phy_set_wol, + .led_brightness_set = bcm_phy_led_brightness_set, +#endif +}, { + .phy_id = PHY_ID_BCM54213PE, + .phy_id_mask = 0xffffffff, + .name = "Broadcom BCM54213PE", + /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, + .config_init = bcm54xx_config_init, + .config_intr = bcm_phy_config_intr, + .suspend = bcm54xx_suspend, + .resume = bcm54xx_resume, }, { .phy_id = PHY_ID_BCM5461, .phy_id_mask = 0xfffffff0, @@ -917,6 +1093,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM54612E, .phy_id_mask = 0xfffffff0, @@ -930,6 +1109,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM54616S, .phy_id_mask = 0xfffffff0, @@ -943,6 +1125,9 @@ static struct phy_driver broadcom_drivers[] = { .read_status = bcm54616s_read_status, .probe = bcm54616s_probe, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM5464, .phy_id_mask = 0xfffffff0, @@ -958,6 +1143,9 @@ static struct phy_driver broadcom_drivers[] = { .suspend = genphy_suspend, .resume = genphy_resume, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM5481, .phy_id_mask = 0xfffffff0, @@ -972,6 +1160,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM54810, .phy_id_mask = 0xfffffff0, @@ -981,6 +1172,8 @@ static struct phy_driver broadcom_drivers[] = { .get_strings = bcm_phy_get_strings, .get_stats = bcm54xx_get_stats, .probe = bcm54xx_phy_probe, + .read_mmd = bcm54810_read_mmd, + .write_mmd = bcm54810_write_mmd, .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, .config_intr = bcm_phy_config_intr, @@ -988,6 +1181,9 @@ static struct phy_driver broadcom_drivers[] = { .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM54811, .phy_id_mask = 0xfffffff0, @@ -1004,6 +1200,9 @@ static struct phy_driver broadcom_drivers[] = { .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM5482, .phy_id_mask = 0xfffffff0, @@ -1017,6 +1216,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM50610, .phy_id_mask = 0xfffffff0, @@ -1032,6 +1234,9 @@ static struct phy_driver broadcom_drivers[] = { .link_change_notify = bcm54xx_link_change_notify, .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM50610M, .phy_id_mask = 0xfffffff0, @@ -1047,6 +1252,9 @@ static struct phy_driver broadcom_drivers[] = { .link_change_notify = bcm54xx_link_change_notify, .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM57780, .phy_id_mask = 0xfffffff0, @@ -1060,6 +1268,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCMAC131, .phy_id_mask = 0xfffffff0, @@ -1091,6 +1302,9 @@ static struct phy_driver broadcom_drivers[] = { .get_stats = bcm54xx_get_stats, .probe = bcm54xx_phy_probe, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM53125, .phy_id_mask = 0xfffffff0, @@ -1105,6 +1319,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM53128, .phy_id_mask = 0xfffffff0, @@ -1119,6 +1336,9 @@ static struct phy_driver broadcom_drivers[] = { .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, +#if 0 + .led_brightness_set = bcm_phy_led_brightness_set, +#endif }, { .phy_id = PHY_ID_BCM89610, .phy_id_mask = 0xfffffff0, @@ -1139,7 +1359,8 @@ module_phy_driver(broadcom_drivers); static struct mdio_device_id __maybe_unused broadcom_tbl[] = { { PHY_ID_BCM5411, 0xfffffff0 }, { PHY_ID_BCM5421, 0xfffffff0 }, - { PHY_ID_BCM54210E, 0xfffffff0 }, + { PHY_ID_BCM54210E, 0xffffffff }, + { PHY_ID_BCM54213PE, 0xffffffff }, { PHY_ID_BCM5461, 0xfffffff0 }, { PHY_ID_BCM54612E, 0xfffffff0 }, { PHY_ID_BCM54616S, 0xfffffff0 }, diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 521acd632f1ac..457a8d29fad87 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -47,11 +49,26 @@ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 +#define PCIE_RC_TL_VDM_CTL0 0x0a20 +#define PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK 0x10000 +#define PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK 0x20000 +#define PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK 0x40000 + +#define PCIE_RC_TL_VDM_CTL1 0x0a0c +#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK 0x0000ffff +#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK 0xffff0000 + #define PCIE_RC_DL_MDIO_ADDR 0x1100 #define PCIE_RC_DL_MDIO_WR_DATA 0x1104 #define PCIE_RC_DL_MDIO_RD_DATA 0x1108 +#define PCIE_RC_PL_PHY_CTL_15 0x184c +#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000 +#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff + #define PCIE_MISC_MISC_CTRL 0x4008 +#define PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80 +#define PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400 #define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000 #define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000 #define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000 @@ -71,6 +88,7 @@ #define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c #define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_RC_BAR1_CONFIG_HI 0x4030 #define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 #define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f @@ -78,6 +96,7 @@ #define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c #define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_RC_BAR3_CONFIG_HI 0x4040 #define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044 #define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048 @@ -86,12 +105,15 @@ #define PCIE_MISC_MSI_DATA_CONFIG_VAL_32 0xffe06540 #define PCIE_MISC_MSI_DATA_CONFIG_VAL_8 0xfff86540 +#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c + #define PCIE_MISC_PCIE_CTRL 0x4064 #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 #define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4 #define PCIE_MISC_PCIE_STATUS 0x4068 #define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80 +#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712 0x40 #define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20 #define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10 #define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40 @@ -116,13 +138,77 @@ #define PCIE_MEM_WIN0_LIMIT_HI(win) \ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) -#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG pcie->reg_offsets[PCIE_HARD_DEBUG] #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK 0x8 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000 - - -#define PCIE_INTR2_CPU_BASE 0x4300 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK 0x00200000 + +#define PCIE_MISC_CTRL_1 0x40A0 +#define PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK 0xf +#define PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK BIT(3) +#define PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK BIT(4) +#define PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK BIT(5) + +#define PCIE_MISC_UBUS_CTRL 0x40a4 +#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13) +#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19) + +#define PCIE_MISC_UBUS_TIMEOUT 0x40A8 + +#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac +#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) +#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI 0x40b0 + +#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4 +#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) + +/* Additional RC BARs */ +#define PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4 +#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8 +/* ... */ +#define PCIE_MISC_RC_BAR10_CONFIG_LO 0x4104 +#define PCIE_MISC_RC_BAR10_CONFIG_HI 0x4108 + +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1 +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000 +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff +#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c +#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110 +/* ... */ +#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_LO 0x413c +#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_HI 0x4140 + +/* AXI priority forwarding - automatic level-based */ +#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x) (0x4160 - (x) * 4) +/* Defined in quarter-fullness */ +#define QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT 12 +#define QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT 8 +#define QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT 4 +#define QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT 0 +#define QUEUE_THRESHOLD_MASK 0xf + +/* VDM messages indexing TCs to AXI priorities */ +/* Indexes 8-15 */ +#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI 0x4164 +/* Indexes 0-7 */ +#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO 0x4168 +#define VDM_PRIORITY_TO_QOS_MAP_SHIFT(x) (4 * (x)) +#define VDM_PRIORITY_TO_QOS_MAP_MASK 0xf + +#define PCIE_MISC_AXI_INTF_CTRL 0x416C +#define AXI_EN_RCLK_QOS_ARRAY_FIX BIT(13) +#define AXI_EN_QOS_UPDATE_TIMING_FIX BIT(12) +#define AXI_DIS_QOS_GATING_IN_MASTER BIT(11) +#define AXI_REQFIFO_EN_QOS_PROPAGATION BIT(7) +#define AXI_BRIDGE_LOW_LATENCY_MODE BIT(6) +#define AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK 0x3f + +#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170 + +#define PCIE_INTR2_CPU_BASE (pcie->reg_offsets[INTR2_CPU]) #define PCIE_MSI_INTR2_BASE 0x4500 /* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */ #define MSI_INT_STATUS 0x0 @@ -196,6 +282,8 @@ enum { RGR1_SW_INIT_1, EXT_CFG_INDEX, EXT_CFG_DATA, + PCIE_HARD_DEBUG, + INTR2_CPU, }; enum { @@ -210,6 +298,7 @@ enum pcie_type { BCM4908, BCM7278, BCM2711, + BCM2712, }; struct pcie_cfg_data { @@ -217,6 +306,7 @@ struct pcie_cfg_data { const enum pcie_type type; void (*perst_set)(struct brcm_pcie *pcie, u32 val); void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); + bool (*rc_mode)(struct brcm_pcie *pcie); }; struct subdev_regulators { @@ -233,7 +323,7 @@ struct brcm_msi { struct mutex lock; /* guards the alloc/free operations */ u64 target_addr; int irq; - DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR); + DECLARE_BITMAP(used, 64); bool legacy; /* Some chips have MSIs in bits [31..24] of a shared register. */ int legacy_shift; @@ -249,6 +339,8 @@ struct brcm_pcie { struct clk *clk; struct device_node *np; bool ssc; + bool l1ss; + bool rcb_mps_mode; int gen; u64 msi_target_addr; struct brcm_msi *msi; @@ -256,13 +348,17 @@ struct brcm_pcie { enum pcie_type type; struct reset_control *rescal; struct reset_control *perst_reset; + struct reset_control *bridge_reset; int num_memc; u64 memc_size[PCIE_BRCM_MAX_MEMC]; u32 hw_rev; + u32 qos_map; void (*perst_set)(struct brcm_pcie *pcie, u32 val); void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); + bool (*rc_mode)(struct brcm_pcie *pcie); struct subdev_regulators *sr; bool ep_wakeup_capable; + u32 tperst_clk_ms; }; static inline bool is_bmips(const struct brcm_pcie *pcie) @@ -281,8 +377,8 @@ static int brcm_pcie_encode_ibar_size(u64 size) if (log2_in >= 12 && log2_in <= 15) /* Covers 4KB to 32KB (inclusive) */ return (log2_in - 12) + 0x1c; - else if (log2_in >= 16 && log2_in <= 35) - /* Covers 64KB to 32GB, (inclusive) */ + else if (log2_in >= 16 && log2_in <= 36) + /* Covers 64KB to 64GB, (inclusive) */ return log2_in - 15; /* Something is awry so disable */ return 0; @@ -302,42 +398,34 @@ static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) /* negative return value indicates error */ static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val) { - int tries; u32 data; + int err; writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ), base + PCIE_RC_DL_MDIO_ADDR); readl(base + PCIE_RC_DL_MDIO_ADDR); - - data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); - for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) { - udelay(10); - data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); - } - + err = readl_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_RD_DATA, data, + MDIO_RD_DONE(data), 10, 100); *val = FIELD_GET(MDIO_DATA_MASK, data); - return MDIO_RD_DONE(data) ? 0 : -EIO; + + return err; } /* negative return value indicates error */ static int brcm_pcie_mdio_write(void __iomem *base, u8 port, u8 regad, u16 wrdata) { - int tries; u32 data; + int err; writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE), base + PCIE_RC_DL_MDIO_ADDR); readl(base + PCIE_RC_DL_MDIO_ADDR); writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA); - data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); - for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) { - udelay(10); - data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); - } - - return MDIO_WT_DONE(data) ? 0 : -EIO; + err = readl_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_WR_DATA, data, + MDIO_WT_DONE(data), 10, 100); + return err; } /* @@ -379,6 +467,35 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) return ssc && pll ? 0 : -EIO; } +static void brcm_pcie_munge_pll(struct brcm_pcie *pcie) +{ + //print "MDIO block 0x1600 written per Dannys instruction" + //tmp = pcie_mdio_write(phyad, &h16&, &h50b9&) + //tmp = pcie_mdio_write(phyad, &h17&, &hbd1a&) + //tmp = pcie_mdio_write(phyad, &h1b&, &h5030&) + //tmp = pcie_mdio_write(phyad, &h1e&, &h0007&) + + u32 tmp; + int ret, i; + u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; + u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 }; + + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, + 0x1600); + for (i = 0; i < ARRAY_SIZE(regs); i++) { + brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp); + dev_dbg(pcie->dev, "PCIE MDIO pre_refclk 0x%02x = 0x%04x\n", + regs[i], tmp); + } + for (i = 0; i < ARRAY_SIZE(regs); i++) { + brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]); + brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp); + dev_dbg(pcie->dev, "PCIE MDIO post_refclk 0x%02x = 0x%04x\n", + regs[i], tmp); + } + usleep_range(100, 200); +} + /* Limits operation to a specific generation (1, 2, or 3) */ static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) { @@ -436,6 +553,111 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); } +static void brcm_pcie_set_tc_qos(struct brcm_pcie *pcie) +{ + int i; + u32 reg; + + if (pcie->type != BCM2712) + return; + + /* Disable broken QOS forwarding search. Set chicken bits for 2712D0 */ + reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); + reg &= ~AXI_REQFIFO_EN_QOS_PROPAGATION; + reg |= AXI_EN_RCLK_QOS_ARRAY_FIX | AXI_EN_QOS_UPDATE_TIMING_FIX | + AXI_DIS_QOS_GATING_IN_MASTER; + writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL); + + /* + * If the QOS_UPDATE_TIMING_FIX bit is Reserved-0, then this is a + * 2712C1 chip, or a single-lane RC. Use the best-effort alternative + * which is to partially throttle AXI requests in-flight to the SDC. + */ + reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); + if (!(reg & AXI_EN_QOS_UPDATE_TIMING_FIX)) { + reg &= ~AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK; + reg |= 15; + writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL); + } + + /* Disable VDM reception by default - QoS map defaults to 0 */ + reg = readl(pcie->base + PCIE_MISC_CTRL_1); + reg &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; + writel(reg, pcie->base + PCIE_MISC_CTRL_1); + + if (!of_property_read_u32(pcie->np, "brcm,fifo-qos-map", &pcie->qos_map)) { + /* + * Backpressure mode - bottom 4 nibbles are QoS for each + * quartile of FIFO level. Each TC gets the same map, because + * this mode is intended for nonrealtime EPs. + */ + + pcie->qos_map &= 0x0000ffff; + for (i = 0; i < 8; i++) + writel(pcie->qos_map, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i)); + + return; + } + + if (!of_property_read_u32(pcie->np, "brcm,vdm-qos-map", &pcie->qos_map)) { + + reg = readl(pcie->base + PCIE_MISC_CTRL_1); + reg |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; + writel(reg, pcie->base + PCIE_MISC_CTRL_1); + + /* No forwarding means no point separating panic priorities from normal */ + writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO); + writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI); + + /* Match Vendor ID of 0 */ + writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1); + /* Forward VDMs to priority interface - at least the rx counters work */ + reg = readl(pcie->base + PCIE_RC_TL_VDM_CTL0); + reg |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK | + PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK | + PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK; + writel(reg, pcie->base + PCIE_RC_TL_VDM_CTL0); + } +} + +static void brcm_pcie_config_clkreq(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + int domain = pci_domain_nr(bridge->bus); + const struct pci_bus *bus = pci_find_bus(domain, 1); + struct pci_dev *pdev = (struct pci_dev *)bus->devices.next; + u32 tmp, link_cap = 0; + u16 link_ctl = 0; + int clkpm = 0; + int substates = 0; + + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); + if ((link_cap & PCI_EXP_LNKCAP_CLKPM)) + clkpm = 1; + + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctl); + if (!(link_ctl & PCI_EXP_LNKCTL_CLKREQ_EN)) + clkpm = 0; + + if (pcie->l1ss && pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS)) + substates = 1; + + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK; + + if (substates) + tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK; + else if (clkpm) + tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + if (substates || clkpm) + dev_info(pcie->dev, "clkreq control enabled\n"); +} + static struct irq_chip brcm_msi_irq_chip = { .name = "BRCM STB PCIe MSI", .irq_ack = irq_chip_ack_parent, @@ -444,15 +666,15 @@ static struct irq_chip brcm_msi_irq_chip = { }; static struct msi_domain_info brcm_msi_domain_info = { - /* Multi MSI is supported by the controller, but not by this driver */ - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), .chip = &brcm_msi_irq_chip, }; static void brcm_pcie_msi_isr(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned long status; + unsigned long status, virq; struct brcm_msi *msi; struct device *dev; u32 bit; @@ -464,10 +686,22 @@ static void brcm_pcie_msi_isr(struct irq_desc *desc) status = readl(msi->intr_base + MSI_INT_STATUS); status >>= msi->legacy_shift; - for_each_set_bit(bit, &status, msi->nr) { - int ret; - ret = generic_handle_domain_irq(msi->inner_domain, bit); - if (ret) + for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR/*msi->nr*/) { + bool found = false; + + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) { + found = true; + dev_dbg(dev, "MSI -> %ld\n", virq); + generic_handle_irq(virq); + } + virq = irq_find_mapping(msi->inner_domain, bit + 32); + if (virq) { + found = true; + dev_dbg(dev, "MSI -> %ld\n", virq); + generic_handle_irq(virq); + } + if (!found) dev_dbg(dev, "unexpected MSI\n"); } @@ -480,7 +714,7 @@ static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) msg->address_lo = lower_32_bits(msi->target_addr); msg->address_hi = upper_32_bits(msi->target_addr); - msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq; + msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | (data->hwirq & 0x1f); } static int brcm_msi_set_affinity(struct irq_data *irq_data, @@ -492,7 +726,7 @@ static int brcm_msi_set_affinity(struct irq_data *irq_data, static void brcm_msi_ack_irq(struct irq_data *data) { struct brcm_msi *msi = irq_data_get_irq_chip_data(data); - const int shift_amt = data->hwirq + msi->legacy_shift; + const int shift_amt = (data->hwirq & 0x1f) + msi->legacy_shift; writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR); } @@ -505,21 +739,23 @@ static struct irq_chip brcm_msi_bottom_irq_chip = { .irq_ack = brcm_msi_ack_irq, }; -static int brcm_msi_alloc(struct brcm_msi *msi) +static int brcm_msi_alloc(struct brcm_msi *msi, unsigned int nr_irqs) { int hwirq; mutex_lock(&msi->lock); - hwirq = bitmap_find_free_region(msi->used, msi->nr, 0); + hwirq = bitmap_find_free_region(msi->used, msi->nr, + order_base_2(nr_irqs)); mutex_unlock(&msi->lock); return hwirq; } -static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq) +static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq, + unsigned int nr_irqs) { mutex_lock(&msi->lock); - bitmap_release_region(msi->used, hwirq, 0); + bitmap_release_region(msi->used, hwirq, order_base_2(nr_irqs)); mutex_unlock(&msi->lock); } @@ -527,16 +763,17 @@ static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { struct brcm_msi *msi = domain->host_data; - int hwirq; + int hwirq, i; - hwirq = brcm_msi_alloc(msi); + hwirq = brcm_msi_alloc(msi, nr_irqs); if (hwirq < 0) return hwirq; - irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq, - &brcm_msi_bottom_irq_chip, domain->host_data, - handle_edge_irq, NULL, NULL); + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &brcm_msi_bottom_irq_chip, domain->host_data, + handle_edge_irq, NULL, NULL); return 0; } @@ -546,7 +783,7 @@ static void brcm_irq_domain_free(struct irq_domain *domain, struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct brcm_msi *msi = irq_data_get_irq_chip_data(d); - brcm_msi_free(msi, d->hwirq); + brcm_msi_free(msi, d->hwirq, nr_irqs); } static const struct irq_domain_ops msi_domain_ops = { @@ -650,7 +887,7 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie) msi->legacy_shift = 24; } else { msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE; - msi->nr = BRCM_INT_PCI_MSI_NR; + msi->nr = 64; //BRCM_INT_PCI_MSI_NR; msi->legacy_shift = 0; } @@ -667,7 +904,7 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie) } /* The controller is capable of serving in both RC and EP roles */ -static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) +static bool brcm_pcie_rc_mode_generic(struct brcm_pcie *pcie) { void __iomem *base = pcie->base; u32 val = readl(base + PCIE_MISC_PCIE_STATUS); @@ -675,6 +912,14 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val); } +static bool brcm_pcie_rc_mode_2712(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + u32 val = readl(base + PCIE_MISC_PCIE_STATUS); + + return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712, val) | 1; //XXX +} + static bool brcm_pcie_link_up(struct brcm_pcie *pcie) { u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS); @@ -726,7 +971,7 @@ static void __iomem *brcm7425_pcie_map_bus(struct pci_bus *bus, return base + DATA_ADDR(pcie); } -static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val) +static void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val) { u32 tmp, mask = RGR1_SW_INIT_1_INIT_GENERIC_MASK; u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT; @@ -736,7 +981,7 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); } -static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val) +static void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val) { u32 tmp, mask = RGR1_SW_INIT_1_INIT_7278_MASK; u32 shift = RGR1_SW_INIT_1_INIT_7278_SHIFT; @@ -746,7 +991,19 @@ static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); } -static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val) +static void brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val) +{ + if (WARN_ONCE(!pcie->bridge_reset, + "missing bridge reset controller\n")) + return; + + if (val) + reset_control_assert(pcie->bridge_reset); + else + reset_control_deassert(pcie->bridge_reset); +} + +static void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val) { if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n")) return; @@ -757,7 +1014,17 @@ static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val) reset_control_deassert(pcie->perst_reset); } -static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val) +static void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + /* Perst bit has moved and assert value is 0 */ + tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK); + writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL); +} + +static void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val) { u32 tmp; @@ -767,7 +1034,7 @@ static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val) writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL); } -static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) +static void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) { u32 tmp; @@ -776,7 +1043,7 @@ static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); } -static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, +static int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, u64 *rc_bar2_size, u64 *rc_bar2_offset) { @@ -793,6 +1060,8 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, size += entry->res->end - entry->res->start + 1; if (pcie_beg < lowest_pcie_addr) lowest_pcie_addr = pcie_beg; + if (pcie->type == BCM2711 || pcie->type == BCM2712) + break; // Only consider the first entry } if (lowest_pcie_addr == ~(u64)0) { @@ -863,6 +1132,30 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, return 0; } +static int brcm_pcie_get_rc_bar_n(struct brcm_pcie *pcie, + int idx, + u64 *rc_bar_cpu, + u64 *rc_bar_size, + u64 *rc_bar_pci) +{ + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + struct resource_entry *entry; + int i = 0; + + resource_list_for_each_entry(entry, &bridge->dma_ranges) { + if (i == idx) { + *rc_bar_cpu = entry->res->start; + *rc_bar_size = entry->res->end - entry->res->start + 1; + *rc_bar_pci = entry->res->start - entry->offset; + return 0; + } + + i++; + } + + return -EINVAL; +} + static int brcm_pcie_setup(struct brcm_pcie *pcie) { u64 rc_bar2_offset, rc_bar2_size; @@ -871,10 +1164,15 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) struct resource_entry *entry; u32 tmp, burst, aspm_support; int num_out_wins = 0; - int ret, memc; + int ret, memc, count, i; /* Reset the bridge */ pcie->bridge_sw_init_set(pcie, 1); + + /* Ensure that PERST# is asserted; some bootloaders may deassert it. */ + if (pcie->type == BCM2711) + pcie->perst_set(pcie, 1); + usleep_range(100, 200); /* Take the bridge out of reset */ @@ -889,6 +1187,17 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) /* Wait for SerDes to be stable */ usleep_range(100, 200); + if (pcie->type == BCM2712) { + /* Allow a 54MHz (xosc) refclk source */ + brcm_pcie_munge_pll(pcie); + /* Fix for L1SS errata */ + tmp = readl(base + PCIE_RC_PL_PHY_CTL_15); + tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK; + /* PM clock period is 18.52ns (round down) */ + tmp |= 0x12; + writel(tmp, base + PCIE_RC_PL_PHY_CTL_15); + } + /* * SCB_MAX_BURST_SIZE is a two bit field. For GENERIC chips it * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it @@ -898,18 +1207,27 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) burst = 0x1; /* 256 bytes */ else if (pcie->type == BCM2711) burst = 0x0; /* 128 bytes */ + else if (pcie->type == BCM2712) + burst = 0x1; /* 128 bytes */ else if (pcie->type == BCM7278) burst = 0x3; /* 512 bytes */ else burst = 0x2; /* 512 bytes */ - /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ + /* + * Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN, + * RCB_MPS_MODE + */ tmp = readl(base + PCIE_MISC_MISC_CTRL); u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK); u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK); u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK); + if (pcie->rcb_mps_mode) + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK); writel(tmp, base + PCIE_MISC_MISC_CTRL); + brcm_pcie_set_tc_qos(pcie); + ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size, &rc_bar2_offset); if (ret) @@ -922,7 +1240,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) writel(upper_32_bits(rc_bar2_offset), base + PCIE_MISC_RC_BAR2_CONFIG_HI); + tmp = readl(base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP); + u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK); + writel(tmp, base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP); tmp = readl(base + PCIE_MISC_MISC_CTRL); + for (memc = 0; memc < pcie->num_memc; memc++) { u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15; @@ -933,8 +1255,32 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) else if (memc == 2) u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2)); } + writel(tmp, base + PCIE_MISC_MISC_CTRL); + if (pcie->type == BCM2712) { + /* Suppress AXI error responses and return 1s for read failures */ + tmp = readl(base + PCIE_MISC_UBUS_CTRL); + u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK); + writel(tmp, base + PCIE_MISC_UBUS_CTRL); + writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA); + + /* + * Adjust timeouts. The UBUS timeout also affects CRS + * completion retries, as the request will get terminated if + * either timeout expires, so both have to be a large value + * (in clocks of 750MHz). + * Set UBUS timeout to 250ms, then set RC config retry timeout + * to be ~240ms. + * + * Setting CRSVis=1 will stop the core from blocking on a CRS + * response, but does require the device to be well-behaved... + */ + writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT); + writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT); + } + /* * We ideally want the MSI target address to be located in the 32bit * addressable memory area. Some devices might depend on it. This is @@ -947,7 +1293,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) else pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; - if (!brcm_pcie_rc_mode(pcie)) { + if (!pcie->rc_mode(pcie)) { dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n"); return -EINVAL; } @@ -971,6 +1317,38 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); + /* program additional inbound windows (RC_BAR4..RC_BAR10) */ + count = (pcie->type == BCM2712) ? 7 : 0; + for (i = 0; i < count; i++) { + u64 bar_cpu, bar_size, bar_pci; + + ret = brcm_pcie_get_rc_bar_n(pcie, 1 + i, &bar_cpu, &bar_size, + &bar_pci); + if (ret) + break; + + tmp = lower_32_bits(bar_pci); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size), + PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK); + writel(tmp, base + PCIE_MISC_RC_BAR4_CONFIG_LO + i * 8); + writel(upper_32_bits(bar_pci), + base + PCIE_MISC_RC_BAR4_CONFIG_HI + i * 8); + + tmp = upper_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK; + writel(tmp, + base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI + i * 8); + tmp = lower_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK; + writel(tmp | PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE, + base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + i * 8); + } + + if (pcie->gen) { + dev_info(pcie->dev, "Forcing gen %d\n", pcie->gen); + brcm_pcie_set_gen(pcie, pcie->gen); + } + /* * For config space accesses on the RC, show the right class for * a PCIe-PCIe bridge (the default setting is to be EP mode). @@ -1024,17 +1402,42 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) { struct device *dev = pcie->dev; void __iomem *base = pcie->base; - u16 nlw, cls, lnksta; + u16 nlw, cls, lnksta, tmp16; bool ssc_good = false; - u32 tmp; int ret, i; + u32 tmp; /* Unassert the fundamental reset */ - pcie->perst_set(pcie, 0); + if (pcie->tperst_clk_ms) { + /* + * Increase Tperst_clk time by forcing PERST# output low while + * the internal reset is released, so the PLL generates stable + * refclk output further in advance of PERST# deassertion. + */ + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + pcie->perst_set(pcie, 0); + msleep(pcie->tperst_clk_ms); + + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + } else { + pcie->perst_set(pcie, 0); + } + + /* + * Wait for 100ms after PERST# deassertion; see PCIe CEM specification + * sections 2.2, PCIe r5.0, 6.6.1. + */ + msleep(100); /* - * Give the RC/EP time to wake up, before trying to configure RC. - * Intermittently check status for link-up, up to a total of 100ms. + * Give the RC/EP even more time to wake up, before trying to + * configure RC. Intermittently check status for link-up, up to a + * total of 100ms. */ for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5) msleep(5); @@ -1055,6 +1458,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) dev_err(dev, "failed attempt to enter ssc mode\n"); } + lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA); cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); @@ -1063,13 +1467,15 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) ssc_good ? "(SSC)" : "(!SSC)"); /* - * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 - * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. + * RootCtl bits are reset by perst_n, which undoes pci_enable_crs() + * called prior to pci_add_new_bus() during probe. Re-enable here. */ - tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; - writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - + tmp16 = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCAP); + if (tmp16 & PCI_EXP_RTCAP_CRSVIS) { + tmp16 = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCTL); + u16p_replace_bits(&tmp16, 1, PCI_EXP_RTCTL_CRSSVE); + writew(tmp16, base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCTL); + } return 0; } @@ -1177,6 +1583,7 @@ static void brcm_pcie_enter_l23(struct brcm_pcie *pcie) static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) { +#if 0 static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = { PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT, PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT, @@ -1209,6 +1616,9 @@ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop")); return ret; +#else + return 0; +#endif } static inline int brcm_phy_start(struct brcm_pcie *pcie) @@ -1241,6 +1651,12 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie) u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + /* + * Shutting down this bridge on pcie1 means accesses to rescal block + * will hang the chip if another RC wants to assert/deassert rescal. + */ + if (pcie->type == BCM2712) + return; /* Shutdown PCIe bridge */ pcie->bridge_sw_init_set(pcie, 1); } @@ -1271,9 +1687,9 @@ static int brcm_pcie_suspend_noirq(struct device *dev) if (brcm_phy_stop(pcie)) dev_err(dev, "Could not stop phy for suspend\n"); - ret = reset_control_rearm(pcie->rescal); + ret = reset_control_assert(pcie->rescal); if (ret) { - dev_err(dev, "Could not rearm rescal reset\n"); + dev_err(dev, "Could not assert rescal reset\n"); return ret; } @@ -1368,7 +1784,7 @@ static int brcm_pcie_resume_noirq(struct device *dev) if (pcie->sr) regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies); err_reset: - reset_control_rearm(pcie->rescal); + reset_control_assert(pcie->rescal); err_disable_clk: clk_disable_unprepare(pcie->clk); return ret; @@ -1380,12 +1796,12 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie) brcm_pcie_turn_off(pcie); if (brcm_phy_stop(pcie)) dev_err(pcie->dev, "Could not stop phy\n"); - if (reset_control_rearm(pcie->rescal)) - dev_err(pcie->dev, "Could not rearm rescal reset\n"); + if (reset_control_assert(pcie->rescal)) + dev_err(pcie->dev, "Could not assert rescal reset\n"); clk_disable_unprepare(pcie->clk); } -static int brcm_pcie_remove(struct platform_device *pdev) +static void brcm_pcie_remove(struct platform_device *pdev) { struct brcm_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); @@ -1393,20 +1809,22 @@ static int brcm_pcie_remove(struct platform_device *pdev) pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); __brcm_pcie_remove(pcie); - - return 0; } static const int pcie_offsets[] = { [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, [EXT_CFG_DATA] = 0x9004, + [PCIE_HARD_DEBUG] = 0x4204, + [INTR2_CPU] = 0x4300, }; static const int pcie_offsets_bmips_7425[] = { [RGR1_SW_INIT_1] = 0x8010, [EXT_CFG_INDEX] = 0x8300, [EXT_CFG_DATA] = 0x8304, + [PCIE_HARD_DEBUG] = 0x4204, + [INTR2_CPU] = 0x4300, }; static const struct pcie_cfg_data generic_cfg = { @@ -1414,6 +1832,7 @@ static const struct pcie_cfg_data generic_cfg = { .type = GENERIC, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, + .rc_mode = brcm_pcie_rc_mode_generic, }; static const struct pcie_cfg_data bcm7425_cfg = { @@ -1421,6 +1840,7 @@ static const struct pcie_cfg_data bcm7425_cfg = { .type = BCM7425, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, + .rc_mode = brcm_pcie_rc_mode_generic, }; static const struct pcie_cfg_data bcm7435_cfg = { @@ -1435,12 +1855,15 @@ static const struct pcie_cfg_data bcm4908_cfg = { .type = BCM4908, .perst_set = brcm_pcie_perst_set_4908, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, + .rc_mode = brcm_pcie_rc_mode_generic, }; static const int pcie_offset_bcm7278[] = { [RGR1_SW_INIT_1] = 0xc010, [EXT_CFG_INDEX] = 0x9000, [EXT_CFG_DATA] = 0x9004, + [PCIE_HARD_DEBUG] = 0x4204, + [INTR2_CPU] = 0x4300, }; static const struct pcie_cfg_data bcm7278_cfg = { @@ -1448,6 +1871,7 @@ static const struct pcie_cfg_data bcm7278_cfg = { .type = BCM7278, .perst_set = brcm_pcie_perst_set_7278, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278, + .rc_mode = brcm_pcie_rc_mode_generic, }; static const struct pcie_cfg_data bcm2711_cfg = { @@ -1455,10 +1879,27 @@ static const struct pcie_cfg_data bcm2711_cfg = { .type = BCM2711, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, + .rc_mode = brcm_pcie_rc_mode_generic, +}; + +static const int pcie_offsets_bcm2712[] = { + [EXT_CFG_INDEX] = 0x9000, + [EXT_CFG_DATA] = 0x9004, + [PCIE_HARD_DEBUG] = 0x4304, + [INTR2_CPU] = 0x4400, +}; + +static const struct pcie_cfg_data bcm2712_cfg = { + .offsets = pcie_offsets_bcm2712, + .type = BCM2712, + .perst_set = brcm_pcie_perst_set_2712, + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712, + .rc_mode = brcm_pcie_rc_mode_2712, }; static const struct of_device_id brcm_pcie_match[] = { { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg }, + { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg }, { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg }, { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg }, { .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg }, @@ -1499,7 +1940,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); if (!data) { - pr_err("failed to look up compatible string\n"); + dev_err(&pdev->dev, "failed to look up compatible string\n"); return -EINVAL; } @@ -1510,6 +1951,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->type = data->type; pcie->perst_set = data->perst_set; pcie->bridge_sw_init_set = data->bridge_sw_init_set; + pcie->rc_mode = data->rc_mode; pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) @@ -1523,6 +1965,9 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->gen = (ret < 0) ? 0 : ret; pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); + pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss"); + pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb"); + of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms); ret = clk_prepare_enable(pcie->clk); if (ret) { @@ -1539,14 +1984,20 @@ static int brcm_pcie_probe(struct platform_device *pdev) clk_disable_unprepare(pcie->clk); return PTR_ERR(pcie->perst_reset); } + pcie->bridge_reset = + devm_reset_control_get_optional_exclusive(&pdev->dev, "bridge"); + if (IS_ERR(pcie->bridge_reset)) { + clk_disable_unprepare(pcie->clk); + return PTR_ERR(pcie->bridge_reset); + } - ret = reset_control_reset(pcie->rescal); + ret = reset_control_deassert(pcie->rescal); if (ret) dev_err(&pdev->dev, "failed to deassert 'rescal'\n"); ret = brcm_phy_start(pcie); if (ret) { - reset_control_rearm(pcie->rescal); + reset_control_assert(pcie->rescal); clk_disable_unprepare(pcie->clk); return ret; } @@ -1569,6 +2020,33 @@ static int brcm_pcie_probe(struct platform_device *pdev) dev_err(pcie->dev, "probe of internal MSI failed"); goto fail; } + } else if (pci_msi_enabled() && msi_np != pcie->np) { + /* Use RC_BAR1 for MIP access */ + u64 msi_pci_addr; + u64 msi_phys_addr; + + if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) { + dev_err(pcie->dev, "Unable to find MSI PCI address\n"); + ret = -EINVAL; + goto fail; + } + + if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) { + dev_err(pcie->dev, "Unable to find MSI physical address\n"); + ret = -EINVAL; + goto fail; + } + + writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000), + pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO); + writel(upper_32_bits(msi_pci_addr), + pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI); + + writel(lower_32_bits(msi_phys_addr) | + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK, + pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP); + writel(upper_32_bits(msi_phys_addr), + pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI); } bridge->ops = pcie->type == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops; @@ -1585,6 +2063,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) return ret; } + brcm_pcie_config_clkreq(pcie); + return 0; fail: @@ -1601,7 +2081,7 @@ static const struct dev_pm_ops brcm_pcie_pm_ops = { static struct platform_driver brcm_pcie_driver = { .probe = brcm_pcie_probe, - .remove = brcm_pcie_remove, + .remove_new = brcm_pcie_remove, .driver = { .name = "brcm-pcie", .of_match_table = brcm_pcie_match, diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 3b374b37b965b..8d9e91c99bb4b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -18,14 +18,14 @@ #define PIARBCTL_CAM 0x00 #define PIARBCTL_SPLITTER 0x04 #define PIARBCTL_MISC 0x08 -#define PIARBCTL_MISC_SECURE_MASK 0x80000000 -#define PIARBCTL_MISC_USB_SELECT_MASK 0x40000000 -#define PIARBCTL_MISC_USB_4G_SDRAM_MASK 0x20000000 -#define PIARBCTL_MISC_USB_PRIORITY_MASK 0x000f0000 -#define PIARBCTL_MISC_USB_MEM_PAGE_MASK 0x0000f000 -#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00 -#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0 -#define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f +#define PIARBCTL_MISC_SATA_PRIORITY_MASK GENMASK(3, 0) +#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK GENMASK(7, 4) +#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK GENMASK(11, 8) +#define PIARBCTL_MISC_USB_MEM_PAGE_MASK GENMASK(15, 12) +#define PIARBCTL_MISC_USB_PRIORITY_MASK GENMASK(19, 16) +#define PIARBCTL_MISC_USB_4G_SDRAM_MASK BIT(29) +#define PIARBCTL_MISC_USB_SELECT_MASK BIT(30) +#define PIARBCTL_MISC_SECURE_MASK BIT(31) #define PIARBCTL_MISC_USB_ONLY_MASK \ (PIARBCTL_MISC_USB_SELECT_MASK | \ @@ -35,46 +35,49 @@ /* Register definitions for the USB CTRL block */ #define USB_CTRL_SETUP 0x00 -#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 -#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 -#define USB_CTRL_SETUP_tca_drv_sel_MASK 0x01000000 -#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 -#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200 -#define USB_CTRL_SETUP_IPP_MASK 0x00000020 -#define USB_CTRL_SETUP_IOC_MASK 0x00000010 +#define USB_CTRL_SETUP_IOC_MASK BIT(4) +#define USB_CTRL_SETUP_IPP_MASK BIT(5) +#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK BIT(9) +#define USB_CTRL_SETUP_SCB1_EN_MASK BIT(14) +#define USB_CTRL_SETUP_SCB2_EN_MASK BIT(15) +#define USB_CTRL_SETUP_tca_drv_sel_MASK BIT(24) +#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25) #define USB_CTRL_USB_PM 0x04 -#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 -#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 -#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 -#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 -#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010 -#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008 +#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK BIT(3) +#define USB_CTRL_USB_PM_XHC_PME_EN_MASK BIT(4) +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK BIT(22) +#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK BIT(23) +#define USB_CTRL_USB_PM_SOFT_RESET_MASK BIT(30) +#define USB_CTRL_USB_PM_USB_PWRDN_MASK BIT(31) #define USB_CTRL_USB_PM_STATUS 0x08 #define USB_CTRL_USB_DEVICE_CTL1 0x10 -#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 +#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK GENMASK(1, 0) #define USB_CTRL_TEST_PORT_CTL 0x30 -#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK 0x000000ff +#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK GENMASK(7, 0) #define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK 0x0000002e #define USB_CTRL_TP_DIAG1 0x34 -#define USB_CTLR_TP_DIAG1_wake_MASK 0x00000002 +#define USB_CTLR_TP_DIAG1_wake_MASK BIT(1) #define USB_CTRL_CTLR_CSHCR 0x50 -#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK 0x00040000 +#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK BIT(18) +#define USB_CTRL_P0_U2PHY_CFG1 0x68 +#define USB_CTRL_P0_U2PHY_CFG1_COMMONONN_MASK BIT(10) /* Register definitions for the USB_PHY block in 7211b0 */ #define USB_PHY_PLL_CTL 0x00 -#define USB_PHY_PLL_CTL_PLL_RESETB_MASK 0x40000000 +#define USB_PHY_PLL_CTL_PLL_SUSPEND_MASK BIT(27) +#define USB_PHY_PLL_CTL_PLL_RESETB_MASK BIT(30) #define USB_PHY_PLL_LDO_CTL 0x08 -#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004 -#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK 0x00000002 -#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK 0x00000001 +#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK BIT(0) +#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK BIT(1) +#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK BIT(2) #define USB_PHY_UTMI_CTL_1 0x04 -#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800 -#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c +#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK GENMASK(3, 2) #define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2 +#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK BIT(11) #define USB_PHY_IDDQ 0x1c -#define USB_PHY_IDDQ_phy_iddq_MASK 0x00000001 +#define USB_PHY_IDDQ_phy_iddq_MASK BIT(0) #define USB_PHY_STATUS 0x20 -#define USB_PHY_STATUS_pll_lock_MASK 0x00000001 +#define USB_PHY_STATUS_pll_lock_MASK BIT(0) /* Register definitions for the MDIO registers in the DWC2 block of * the 7211b0. @@ -86,9 +89,11 @@ /* Register definitions for the BDC EC block in 7211b0 */ #define BDC_EC_AXIRDA 0x0c -#define BDC_EC_AXIRDA_RTS_MASK 0xf0000000 +#define BDC_EC_AXIRDA_RTS_MASK GENMASK(31, 28) #define BDC_EC_AXIRDA_RTS_SHIFT 28 +#define USB_XHCI_GBL_GUSB2PHYCFG 0x100 +#define USB_XHCI_GBL_GUSB2PHYCFG_U2_FREECLK_EXISTS_MASK BIT(30) static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params, uint8_t addr, uint16_t data) @@ -139,13 +144,17 @@ static void xhci_soft_reset(struct brcm_usb_init_params *params, int on_off) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + void __iomem *xhci_gbl = params->regs[BRCM_REGS_XHCI_GBL]; /* Assert reset */ - if (on_off) + if (on_off) { USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB); /* De-assert reset */ - else + } else { USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB); + /* Required for COMMONONN to be set */ + USB_XHCI_GBL_UNSET(xhci_gbl, GUSB2PHYCFG, U2_FREECLK_EXISTS); + } } static void usb_init_ipp(struct brcm_usb_init_params *params) @@ -195,10 +204,10 @@ static void usb_init_common(struct brcm_usb_init_params *params) if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) { reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); - reg |= params->mode; + reg |= params->port_mode; brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } - switch (params->mode) { + switch (params->supported_port_modes) { case USB_CTLR_MODE_HOST: USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB); break; @@ -259,6 +268,11 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); } + /* Disable PLL auto suspend */ + reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL); + reg |= USB_PHY_PLL_CTL_PLL_SUSPEND_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL); + /* Init the PHY */ reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK | USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK | @@ -276,7 +290,7 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) /* Set the PHY_MODE */ reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1); reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK; - reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT; + reg |= params->supported_port_modes << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT; brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); usb_init_common(params); @@ -286,7 +300,7 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) * the default "Read Transaction Size" of 6 (1024 bytes). * Set it to 4 (256 bytes). */ - if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) { + if ((params->supported_port_modes != USB_CTLR_MODE_HOST) && bdc_ec) { reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA); reg &= ~BDC_EC_AXIRDA_RTS_MASK; reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT); @@ -314,10 +328,43 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params) /* 1 millisecond - for USB clocks to settle down */ usleep_range(1000, 2000); + /* Disable PHY when port is suspended */ + USB_CTRL_SET(ctrl, P0_U2PHY_CFG1, COMMONONN); + usb_wake_enable_7216(params, false); usb_init_common(params); } +static void usb_init_common_2712(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC]; + u32 reg; + + if (params->syscon_piarbctl) + syscon_piarbctl_init(params->syscon_piarbctl); + + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); + + usb_wake_enable_7211b0(params, false); + + usb_init_common(params); + + /* + * The BDC controller will get occasional failures with + * the default "Read Transaction Size" of 6 (1024 bytes). + * Set it to 4 (256 bytes). + */ + if ((params->supported_port_modes != USB_CTLR_MODE_HOST) && bdc_ec) { + reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA); + reg &= ~BDC_EC_AXIRDA_RTS_MASK; + reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT); + brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA); + } + + usb2_eye_fix_7211b0(params); +} + static void usb_init_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); @@ -363,6 +410,18 @@ static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) } +static void usb_uninit_common_2712(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + if (params->wake_enabled) { + USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN); + usb_wake_enable_7211b0(params, true); + } else { + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + } +} + static void usb_uninit_xhci(struct brcm_usb_init_params *params) { @@ -384,7 +443,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params) return reg; } -static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) +static void usb_set_dual_select(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; @@ -393,7 +452,7 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); - reg |= mode; + reg |= params->port_mode; brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } @@ -417,6 +476,16 @@ static const struct brcm_usb_init_ops bcm7211b0_ops = { .set_dual_select = usb_set_dual_select, }; +static const struct brcm_usb_init_ops bcm2712_ops = { + .init_ipp = usb_init_ipp, + .init_common = usb_init_common_2712, + .init_xhci = usb_init_xhci, + .uninit_common = usb_uninit_common_2712, + .uninit_xhci = usb_uninit_xhci, + .get_dual_select = usb_get_dual_select, + .set_dual_select = usb_set_dual_select, +}; + void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) { @@ -434,3 +503,10 @@ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) params->family_name = "7211"; params->ops = &bcm7211b0_ops; } + +void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params) +{ + params->family_name = "2712"; + params->ops = &bcm2712_ops; + params->suspend_with_clocks = true; +} diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index dddcbd3cd5f3d..39536b6d96a9f 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -21,57 +21,62 @@ /* Register definitions for the USB CTRL block */ #define USB_CTRL_SETUP 0x00 -#define USB_CTRL_SETUP_IOC_MASK 0x00000010 -#define USB_CTRL_SETUP_IPP_MASK 0x00000020 -#define USB_CTRL_SETUP_BABO_MASK 0x00000001 -#define USB_CTRL_SETUP_FNHW_MASK 0x00000002 -#define USB_CTRL_SETUP_FNBO_MASK 0x00000004 -#define USB_CTRL_SETUP_WABO_MASK 0x00000008 -#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK 0x00002000 /* option */ -#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 /* option */ -#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 /* option */ -#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK 0X00020000 /* option */ -#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK 0x00010000 /* option */ -#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 /* option */ -#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK 0x04000000 /* option */ -#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK 0x08000000 /* opt */ -#define USB_CTRL_SETUP_OC3_DISABLE_MASK 0xc0000000 /* option */ +#define USB_CTRL_SETUP_BABO_MASK BIT(0) +#define USB_CTRL_SETUP_FNHW_MASK BIT(1) +#define USB_CTRL_SETUP_FNBO_MASK BIT(2) +#define USB_CTRL_SETUP_WABO_MASK BIT(3) +#define USB_CTRL_SETUP_IOC_MASK BIT(4) +#define USB_CTRL_SETUP_IPP_MASK BIT(5) +#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK BIT(13) /* option */ +#define USB_CTRL_SETUP_SCB1_EN_MASK BIT(14) /* option */ +#define USB_CTRL_SETUP_SCB2_EN_MASK BIT(15) /* option */ +#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK BIT(17) /* option */ +#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK BIT(16) /* option */ +#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25) /* option */ +#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK BIT(26) /* option */ +#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK BIT(27) /* opt */ +#define USB_CTRL_SETUP_OC_DISABLE_PORT0_MASK BIT(28) +#define USB_CTRL_SETUP_OC_DISABLE_PORT1_MASK BIT(29) +#define USB_CTRL_SETUP_OC_DISABLE_MASK GENMASK(29, 28) /* option */ +#define USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK BIT(30) +#define USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK BIT(31) +#define USB_CTRL_SETUP_OC3_DISABLE_MASK GENMASK(31, 30) /* option */ #define USB_CTRL_PLL_CTL 0x04 -#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK 0x08000000 -#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK 0x40000000 -#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */ +#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK BIT(27) +#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK BIT(30) +#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK BIT(31) /* option */ #define USB_CTRL_EBRIDGE 0x0c -#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */ -#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK 0x00000f80 /* option */ +#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK GENMASK(11, 7) /* option */ +#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK BIT(17) /* option */ #define USB_CTRL_OBRIDGE 0x10 -#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000 +#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK BIT(27) #define USB_CTRL_MDIO 0x14 #define USB_CTRL_MDIO2 0x18 #define USB_CTRL_UTMI_CTL_1 0x2c -#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800 -#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK 0x08000000 +#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK BIT(11) +#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK BIT(27) #define USB_CTRL_USB_PM 0x34 -#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 /* option */ -#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 /* option */ -#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK 0x40000000 /* option */ -#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 /* option */ -#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */ -#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */ -#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */ -#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001 +#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK BIT(0) +#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK GENMASK(21, 20) /* option */ +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK BIT(22) /* option */ +#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK BIT(23) /* option */ +#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK GENMASK(29, 28) /* option */ +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK BIT(30) /* option */ +#define USB_CTRL_USB_PM_SOFT_RESET_MASK BIT(30) /* option */ +#define USB_CTRL_USB_PM_USB_PWRDN_MASK BIT(31) /* option */ #define USB_CTRL_USB_PM_STATUS 0x38 #define USB_CTRL_USB30_CTL1 0x60 -#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010 -#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000 -#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK 0x00020000 /* option */ -#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK 0x10000000 /* option */ -#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK 0x20000000 /* option */ +#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK BIT(4) +#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK BIT(16) +#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK BIT(17) /* option */ +#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK BIT(28) /* option */ +#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK BIT(29) /* option */ #define USB_CTRL_USB30_PCTL 0x70 -#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK 0x00000002 -#define USB_CTRL_USB30_PCTL_PHY3_IDDQ_OVERRIDE_MASK 0x00008000 -#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK 0x00020000 +#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK BIT(1) +#define USB_CTRL_USB30_PCTL_PHY3_IDDQ_OVERRIDE_MASK BIT(15) +#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK BIT(17) #define USB_CTRL_USB_DEVICE_CTL1 0x90 -#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 /* option */ +#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK GENMASK(1, 0) /* option */ /* Register definitions for the XHCI EC block */ #define USB_XHCI_EC_IRAADR 0x658 @@ -114,6 +119,8 @@ enum { USB_CTRL_SETUP_SCB2_EN_SELECTOR, USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR, USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR, USB_CTRL_SETUP_OC3_DISABLE_SELECTOR, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR, USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR, @@ -190,6 +197,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -232,6 +241,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -253,6 +264,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -274,6 +287,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -295,6 +310,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -316,6 +333,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */ 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -337,6 +356,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -358,6 +379,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -379,6 +402,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -400,6 +425,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ 0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -872,15 +899,22 @@ static void usb_init_common(struct brcm_usb_init_params *params) brcmusb_memc_fix(params); + /* Workaround for false positive OC for 7439b2 in DRD/Device mode */ + if ((params->family_id == 0x74390012) && + (params->supported_port_modes != USB_CTLR_MODE_HOST)) { + USB_CTRL_SET(ctrl, SETUP, OC_DISABLE_PORT1); + USB_CTRL_SET_FAMILY(params, SETUP, OC3_DISABLE_PORT1); + } + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE); - reg |= params->mode; + reg |= params->port_mode; brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) { - switch (params->mode) { + switch (params->supported_port_modes) { case USB_CTLR_MODE_HOST: USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB); break; @@ -891,7 +925,7 @@ static void usb_init_common(struct brcm_usb_init_params *params) } } if (USB_CTRL_MASK_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE)) { - if (params->mode == USB_CTLR_MODE_TYPEC_PD) + if (params->supported_port_modes == USB_CTLR_MODE_TYPEC_PD) USB_CTRL_SET_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE); else USB_CTRL_UNSET_FAMILY(params, SETUP, @@ -1000,7 +1034,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params) return reg; } -static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) +static void usb_set_dual_select(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; @@ -1011,7 +1045,7 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE); - reg |= mode; + reg |= params->port_mode; brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } } diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index 3236e94988428..67083474126d1 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -34,6 +34,14 @@ enum brcmusb_reg_sel { brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \ USB_CTRL_##reg##_##field##_MASK) +#define USB_XHCI_GBL_REG(base, reg) ((void __iomem *)base + USB_XHCI_GBL_##reg) +#define USB_XHCI_GBL_SET(base, reg, field) \ + brcm_usb_ctrl_set(USB_XHCI_GBL_REG(base, reg), \ + USB_XHCI_GBL_##reg##_##field##_MASK) +#define USB_XHCI_GBL_UNSET(base, reg, field) \ + brcm_usb_ctrl_unset(USB_XHCI_GBL_REG(base, reg), \ + USB_XHCI_GBL_##reg##_##field##_MASK) + struct brcm_usb_init_params; struct brcm_usb_init_ops { @@ -45,14 +53,15 @@ struct brcm_usb_init_ops { void (*uninit_eohci)(struct brcm_usb_init_params *params); void (*uninit_xhci)(struct brcm_usb_init_params *params); int (*get_dual_select)(struct brcm_usb_init_params *params); - void (*set_dual_select)(struct brcm_usb_init_params *params, int mode); + void (*set_dual_select)(struct brcm_usb_init_params *params); }; struct brcm_usb_init_params { void __iomem *regs[BRCM_REGS_MAX]; int ioc; int ipp; - int mode; + int supported_port_modes; + int port_mode; u32 family_id; u32 product_id; int selected_family; @@ -61,12 +70,14 @@ struct brcm_usb_init_params { const struct brcm_usb_init_ops *ops; struct regmap *syscon_piarbctl; bool wake_enabled; + bool suspend_with_clocks; }; void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params); +void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params); static inline u32 brcm_usb_readl(void __iomem *addr) { @@ -152,11 +163,10 @@ static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini) return 0; } -static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini, - int mode) +static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini) { if (ini->ops->set_dual_select) - ini->ops->set_dual_select(ini, mode); + ini->ops->set_dual_select(ini); } #endif /* _USB_BRCM_COMMON_INIT_H */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 7a4c328bac58c..eaba09147af43 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -76,7 +76,7 @@ struct brcm_usb_phy_data { }; static s8 *node_reg_names[BRCM_REGS_MAX] = { - "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" + "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" }; static int brcm_pm_notifier(struct notifier_block *notifier, @@ -233,7 +233,7 @@ static ssize_t dr_mode_show(struct device *dev, return sprintf(buf, "%s\n", value_to_name(&brcm_dr_mode_to_name[0], ARRAY_SIZE(brcm_dr_mode_to_name), - priv->ini.mode)); + priv->ini.supported_port_modes)); } static DEVICE_ATTR_RO(dr_mode); @@ -249,7 +249,8 @@ static ssize_t dual_select_store(struct device *dev, res = name_to_value(&brcm_dual_mode_to_name[0], ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value); if (!res) { - brcm_usb_set_dual_select(&priv->ini, value); + priv->ini.port_mode = value; + brcm_usb_set_dual_select(&priv->ini); res = len; } mutex_unlock(&sysfs_lock); @@ -315,6 +316,18 @@ static const struct match_chip_info chip_info_7211b0 = { .optional_reg = BRCM_REGS_BDC_EC, }; +static const struct match_chip_info chip_info_2712 = { + .init_func = &brcm_usb_dvr_init_2712, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + BRCM_REGS_XHCI_GBL, + BRCM_REGS_USB_MDIO, + -1, + }, + .optional_reg = BRCM_REGS_BDC_EC, +}; + static const struct match_chip_info chip_info_7445 = { .init_func = &brcm_usb_dvr_init_7445, .required_regs = { @@ -337,6 +350,10 @@ static const struct of_device_id brcm_usb_dt_ids[] = { .compatible = "brcm,bcm7211-usb-phy", .data = &chip_info_7211b0, }, + { + .compatible = "brcm,bcm2712-usb-phy", + .data = &chip_info_2712, + }, { .compatible = "brcm,brcmstb-usb-phy", .data = &chip_info_7445, @@ -463,6 +480,17 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, return 0; } +static const void *of_device_get_match_data__(const struct device *dev) +{ + const struct of_device_id *match; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match) + return NULL; + + return match->data; +} + static int brcm_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -483,7 +511,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) priv->ini.family_id = brcmstb_get_family_id(); priv->ini.product_id = brcmstb_get_product_id(); - info = of_device_get_match_data(&pdev->dev); + info = of_device_get_match_data__(&pdev->dev); if (!info) return -ENOENT; @@ -495,13 +523,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); - priv->ini.mode = USB_CTLR_MODE_HOST; + priv->ini.supported_port_modes = USB_CTLR_MODE_HOST; err = of_property_read_string(dn, "dr_mode", &mode); if (err == 0) { name_to_value(&brcm_dr_mode_to_name[0], ARRAY_SIZE(brcm_dr_mode_to_name), - mode, &priv->ini.mode); + mode, &priv->ini.supported_port_modes); } + /* Default port_mode to supported port_modes */ + priv->ini.port_mode = priv->ini.supported_port_modes; + if (of_property_read_bool(dn, "brcm,has-xhci")) priv->has_xhci = true; if (of_property_read_bool(dn, "brcm,has-eohci")) @@ -539,7 +570,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) * Create sysfs entries for mode. * Remove "dual_select" attribute if not in dual mode */ - if (priv->ini.mode != USB_CTLR_MODE_DRD) + if (priv->ini.supported_port_modes != USB_CTLR_MODE_DRD) brcm_usb_phy_attrs[1] = NULL; err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group); if (err) @@ -568,14 +599,12 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } -static int brcm_usb_phy_remove(struct platform_device *pdev) +static void brcm_usb_phy_remove(struct platform_device *pdev) { struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev); sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group); unregister_pm_notifier(&priv->pm_notifier); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -666,7 +695,7 @@ MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); static struct platform_driver brcm_usb_driver = { .probe = brcm_usb_phy_probe, - .remove = brcm_usb_phy_remove, + .remove_new = brcm_usb_phy_remove, .driver = { .name = "brcmstb-usb-phy", .pm = &brcm_usb_phy_pm_ops, diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f71fefff400f5..7b11952c887db 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -512,6 +512,13 @@ config PINCTRL_ZYNQMP This driver can also be built as a module. If so, the module will be called pinctrl-zynqmp. +config PINCTRL_RP1 + bool "Pinctrl driver for RP1" + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB_IRQCHIP + source "drivers/pinctrl/actions/Kconfig" source "drivers/pinctrl/aspeed/Kconfig" source "drivers/pinctrl/bcm/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 89bfa01b5231a..a37a5f5265b1d 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o +obj-$(CONFIG_PINCTRL_RP1) += pinctrl-rp1.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index 35b51ce4298e2..f2ce009999e78 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -3,6 +3,15 @@ # Broadcom pinctrl drivers # +config PINCTRL_BCM2712 + bool "Broadcom BCM2712 PINCONF driver" + depends on OF && (ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST) + select PINMUX + select PINCONF + select GENERIC_PINCONF + help + Say Y here to enable the Broadcom BCM2712 PINCONF driver. + config PINCTRL_BCM281XX bool "Broadcom BCM281xx pinctrl driver" depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST) diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile index 82b868ec14716..d298e47858296 100644 --- a/drivers/pinctrl/bcm/Makefile +++ b/drivers/pinctrl/bcm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Broadcom pinctrl support +obj-$(CONFIG_PINCTRL_BCM2712) += pinctrl-bcm2712.o obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_BCM4908) += pinctrl-bcm4908.o diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2712.c b/drivers/pinctrl/bcm/pinctrl-bcm2712.c new file mode 100644 index 0000000000000..80a6aea8d6c96 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-bcm2712.c @@ -0,0 +1,1247 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Broadcom BCM2712 GPIO units (pinctrl only) + * + * Copyright (C) 2021-3 Raspberry Pi Ltd. + * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren + * + * Based heavily on the BCM2835 GPIO & pinctrl driver, which was inspired by: + * pinctrl-nomadik.c, please see original file for copyright information + * pinctrl-tegra.c, please see original file for copyright information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pinctrl-bcm2712" + +/* Register offsets */ + +#define BCM2712_PULL_NONE 0 +#define BCM2712_PULL_DOWN 1 +#define BCM2712_PULL_UP 2 +#define BCM2712_PULL_MASK 0x3 + +#define BCM2712_FSEL_COUNT 9 +#define BCM2712_FSEL_MASK 0xf + +#define FUNC(f) \ + [func_##f] = #f +#define PIN(i, f1, f2, f3, f4, f5, f6, f7, f8) \ + [i] = { \ + .funcs = { \ + func_##f1, \ + func_##f2, \ + func_##f3, \ + func_##f4, \ + func_##f5, \ + func_##f6, \ + func_##f7, \ + func_##f8, \ + }, \ + } + +#define MUX_BIT_VALID 0x8000 +#define REG_BIT_INVALID 0xffff + +#define BIT_TO_REG(b) (((b) >> 5) << 2) +#define BIT_TO_SHIFT(b) ((b) & 0x1f) + +#define MUX_BIT(mr, mb) (MUX_BIT_VALID + ((mr)*4)*8 + (mb)*4) +#define GPIO_REGS(n, mr, mb, pr, pb) \ + [n] = { MUX_BIT(mr, mb), ((pr)*4)*8 + (pb)*2 } + +#define EMMC_REGS(n, pr, pb) \ + [n] = { 0, ((pr)*4)*8 + (pb)*2 } + +#define AGPIO_REGS(n, mr, mb, pr, pb) \ + [n] = { MUX_BIT(mr, mb), ((pr)*4)*8 + (pb)*2 } + +#define SGPIO_REGS(n, mr, mb) \ + [n+32] = { MUX_BIT(mr, mb), REG_BIT_INVALID } + +#define GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a) +#define AGPIO_PIN(a) PINCTRL_PIN(a, "aon_gpio" #a) +#define SGPIO_PIN(a) PINCTRL_PIN(a+32, "aon_sgpio" #a) + +struct pin_regs { + u16 mux_bit; + u16 pad_bit; +}; + +struct bcm2712_pinctrl { + struct device *dev; + void __iomem *base; + struct pinctrl_dev *pctl_dev; + struct pinctrl_desc pctl_desc; + const struct pin_regs *pin_regs; + const struct bcm2712_pin_funcs *pin_funcs; + const char *const *gpio_groups; + struct pinctrl_gpio_range gpio_range; + spinlock_t lock; +}; + +struct bcm_plat_data { + const struct pinctrl_desc *pctl_desc; + const struct pinctrl_gpio_range *gpio_range; + const struct pin_regs *pin_regs; + const struct bcm2712_pin_funcs *pin_funcs; +}; + +struct bcm2712_pin_funcs { + u8 funcs[BCM2712_FSEL_COUNT - 1]; +}; + +enum bcm2712_funcs { + func_gpio, + func_alt1, + func_alt2, + func_alt3, + func_alt4, + func_alt5, + func_alt6, + func_alt7, + func_alt8, + func_aon_cpu_standbyb, + func_aon_fp_4sec_resetb, + func_aon_gpclk, + func_aon_pwm, + func_arm_jtag, + func_aud_fs_clk0, + func_avs_pmu_bsc, + func_bsc_m0, + func_bsc_m1, + func_bsc_m2, + func_bsc_m3, + func_clk_observe, + func_ctl_hdmi_5v, + func_enet0, + func_enet0_mii, + func_enet0_rgmii, + func_ext_sc_clk, + func_fl0, + func_fl1, + func_gpclk0, + func_gpclk1, + func_gpclk2, + func_hdmi_tx0_auto_i2c, + func_hdmi_tx0_bsc, + func_hdmi_tx1_auto_i2c, + func_hdmi_tx1_bsc, + func_i2s_in, + func_i2s_out, + func_ir_in, + func_mtsif, + func_mtsif_alt, + func_mtsif_alt1, + func_pdm, + func_pkt, + func_pm_led_out, + func_sc0, + func_sd0, + func_sd2, + func_sd_card_a, + func_sd_card_b, + func_sd_card_c, + func_sd_card_d, + func_sd_card_e, + func_sd_card_f, + func_sd_card_g, + func_spdif_out, + func_spi_m, + func_spi_s, + func_sr_edm_sense, + func_te0, + func_te1, + func_tsio, + func_uart0, + func_uart1, + func_uart2, + func_usb_pwr, + func_usb_vbus, + func_uui, + func_vc_i2c0, + func_vc_i2c3, + func_vc_i2c4, + func_vc_i2c5, + func_vc_i2csl, + func_vc_pcm, + func_vc_pwm0, + func_vc_pwm1, + func_vc_spi0, + func_vc_spi3, + func_vc_spi4, + func_vc_spi5, + func_vc_uart0, + func_vc_uart2, + func_vc_uart3, + func_vc_uart4, + func__, + func_count = func__ +}; + +static const struct pin_regs bcm2712_c0_gpio_pin_regs[] = { + GPIO_REGS(0, 0, 0, 7, 7), + GPIO_REGS(1, 0, 1, 7, 8), + GPIO_REGS(2, 0, 2, 7, 9), + GPIO_REGS(3, 0, 3, 7, 10), + GPIO_REGS(4, 0, 4, 7, 11), + GPIO_REGS(5, 0, 5, 7, 12), + GPIO_REGS(6, 0, 6, 7, 13), + GPIO_REGS(7, 0, 7, 7, 14), + GPIO_REGS(8, 1, 0, 8, 0), + GPIO_REGS(9, 1, 1, 8, 1), + GPIO_REGS(10, 1, 2, 8, 2), + GPIO_REGS(11, 1, 3, 8, 3), + GPIO_REGS(12, 1, 4, 8, 4), + GPIO_REGS(13, 1, 5, 8, 5), + GPIO_REGS(14, 1, 6, 8, 6), + GPIO_REGS(15, 1, 7, 8, 7), + GPIO_REGS(16, 2, 0, 8, 8), + GPIO_REGS(17, 2, 1, 8, 9), + GPIO_REGS(18, 2, 2, 8, 10), + GPIO_REGS(19, 2, 3, 8, 11), + GPIO_REGS(20, 2, 4, 8, 12), + GPIO_REGS(21, 2, 5, 8, 13), + GPIO_REGS(22, 2, 6, 8, 14), + GPIO_REGS(23, 2, 7, 9, 0), + GPIO_REGS(24, 3, 0, 9, 1), + GPIO_REGS(25, 3, 1, 9, 2), + GPIO_REGS(26, 3, 2, 9, 3), + GPIO_REGS(27, 3, 3, 9, 4), + GPIO_REGS(28, 3, 4, 9, 5), + GPIO_REGS(29, 3, 5, 9, 6), + GPIO_REGS(30, 3, 6, 9, 7), + GPIO_REGS(31, 3, 7, 9, 8), + GPIO_REGS(32, 4, 0, 9, 9), + GPIO_REGS(33, 4, 1, 9, 10), + GPIO_REGS(34, 4, 2, 9, 11), + GPIO_REGS(35, 4, 3, 9, 12), + GPIO_REGS(36, 4, 4, 9, 13), + GPIO_REGS(37, 4, 5, 9, 14), + GPIO_REGS(38, 4, 6, 10, 0), + GPIO_REGS(39, 4, 7, 10, 1), + GPIO_REGS(40, 5, 0, 10, 2), + GPIO_REGS(41, 5, 1, 10, 3), + GPIO_REGS(42, 5, 2, 10, 4), + GPIO_REGS(43, 5, 3, 10, 5), + GPIO_REGS(44, 5, 4, 10, 6), + GPIO_REGS(45, 5, 5, 10, 7), + GPIO_REGS(46, 5, 6, 10, 8), + GPIO_REGS(47, 5, 7, 10, 9), + GPIO_REGS(48, 6, 0, 10, 10), + GPIO_REGS(49, 6, 1, 10, 11), + GPIO_REGS(50, 6, 2, 10, 12), + GPIO_REGS(51, 6, 3, 10, 13), + GPIO_REGS(52, 6, 4, 10, 14), + GPIO_REGS(53, 6, 5, 11, 0), + EMMC_REGS(54, 11, 1), /* EMMC_CMD */ + EMMC_REGS(55, 11, 2), /* EMMC_DS */ + EMMC_REGS(56, 11, 3), /* EMMC_CLK */ + EMMC_REGS(57, 11, 4), /* EMMC_DAT0 */ + EMMC_REGS(58, 11, 5), /* EMMC_DAT1 */ + EMMC_REGS(59, 11, 6), /* EMMC_DAT2 */ + EMMC_REGS(60, 11, 7), /* EMMC_DAT3 */ + EMMC_REGS(61, 11, 8), /* EMMC_DAT4 */ + EMMC_REGS(62, 11, 9), /* EMMC_DAT5 */ + EMMC_REGS(63, 11, 10), /* EMMC_DAT6 */ + EMMC_REGS(64, 11, 11), /* EMMC_DAT7 */ +}; + +static struct pin_regs bcm2712_c0_aon_gpio_pin_regs[] = { + AGPIO_REGS(0, 3, 0, 6, 10), + AGPIO_REGS(1, 3, 1, 6, 11), + AGPIO_REGS(2, 3, 2, 6, 12), + AGPIO_REGS(3, 3, 3, 6, 13), + AGPIO_REGS(4, 3, 4, 6, 14), + AGPIO_REGS(5, 3, 5, 7, 0), + AGPIO_REGS(6, 3, 6, 7, 1), + AGPIO_REGS(7, 3, 7, 7, 2), + AGPIO_REGS(8, 4, 0, 7, 3), + AGPIO_REGS(9, 4, 1, 7, 4), + AGPIO_REGS(10, 4, 2, 7, 5), + AGPIO_REGS(11, 4, 3, 7, 6), + AGPIO_REGS(12, 4, 4, 7, 7), + AGPIO_REGS(13, 4, 5, 7, 8), + AGPIO_REGS(14, 4, 6, 7, 9), + AGPIO_REGS(15, 4, 7, 7, 10), + AGPIO_REGS(16, 5, 0, 7, 11), + SGPIO_REGS(0, 0, 0), + SGPIO_REGS(1, 0, 1), + SGPIO_REGS(2, 0, 2), + SGPIO_REGS(3, 0, 3), + SGPIO_REGS(4, 1, 0), + SGPIO_REGS(5, 2, 0), +}; + +static const struct pinctrl_pin_desc bcm2712_c0_gpio_pins[] = { + GPIO_PIN(0), + GPIO_PIN(1), + GPIO_PIN(2), + GPIO_PIN(3), + GPIO_PIN(4), + GPIO_PIN(5), + GPIO_PIN(6), + GPIO_PIN(7), + GPIO_PIN(8), + GPIO_PIN(9), + GPIO_PIN(10), + GPIO_PIN(11), + GPIO_PIN(12), + GPIO_PIN(13), + GPIO_PIN(14), + GPIO_PIN(15), + GPIO_PIN(16), + GPIO_PIN(17), + GPIO_PIN(18), + GPIO_PIN(19), + GPIO_PIN(20), + GPIO_PIN(21), + GPIO_PIN(22), + GPIO_PIN(23), + GPIO_PIN(24), + GPIO_PIN(25), + GPIO_PIN(26), + GPIO_PIN(27), + GPIO_PIN(28), + GPIO_PIN(29), + GPIO_PIN(30), + GPIO_PIN(31), + GPIO_PIN(32), + GPIO_PIN(33), + GPIO_PIN(34), + GPIO_PIN(35), + GPIO_PIN(36), + GPIO_PIN(37), + GPIO_PIN(38), + GPIO_PIN(39), + GPIO_PIN(40), + GPIO_PIN(41), + GPIO_PIN(42), + GPIO_PIN(43), + GPIO_PIN(44), + GPIO_PIN(45), + GPIO_PIN(46), + GPIO_PIN(47), + GPIO_PIN(48), + GPIO_PIN(49), + GPIO_PIN(50), + GPIO_PIN(51), + GPIO_PIN(52), + GPIO_PIN(53), + PINCTRL_PIN(54, "emmc_cmd"), + PINCTRL_PIN(55, "emmc_ds"), + PINCTRL_PIN(56, "emmc_clk"), + PINCTRL_PIN(57, "emmc_dat0"), + PINCTRL_PIN(58, "emmc_dat1"), + PINCTRL_PIN(59, "emmc_dat2"), + PINCTRL_PIN(60, "emmc_dat3"), + PINCTRL_PIN(61, "emmc_dat4"), + PINCTRL_PIN(62, "emmc_dat5"), + PINCTRL_PIN(63, "emmc_dat6"), + PINCTRL_PIN(64, "emmc_dat7"), +}; + +static struct pinctrl_pin_desc bcm2712_c0_aon_gpio_pins[] = { + AGPIO_PIN(0), + AGPIO_PIN(1), + AGPIO_PIN(2), + AGPIO_PIN(3), + AGPIO_PIN(4), + AGPIO_PIN(5), + AGPIO_PIN(6), + AGPIO_PIN(7), + AGPIO_PIN(8), + AGPIO_PIN(9), + AGPIO_PIN(10), + AGPIO_PIN(11), + AGPIO_PIN(12), + AGPIO_PIN(13), + AGPIO_PIN(14), + AGPIO_PIN(15), + AGPIO_PIN(16), + SGPIO_PIN(0), + SGPIO_PIN(1), + SGPIO_PIN(2), + SGPIO_PIN(3), + SGPIO_PIN(4), + SGPIO_PIN(5), +}; + +static const struct pin_regs bcm2712_d0_gpio_pin_regs[] = { + GPIO_REGS(1, 0, 0, 4, 5), + GPIO_REGS(2, 0, 1, 4, 6), + GPIO_REGS(3, 0, 2, 4, 7), + GPIO_REGS(4, 0, 3, 4, 8), + GPIO_REGS(10, 0, 4, 4, 9), + GPIO_REGS(11, 0, 5, 4, 10), + GPIO_REGS(12, 0, 6, 4, 11), + GPIO_REGS(13, 0, 7, 4, 12), + GPIO_REGS(14, 1, 0, 4, 13), + GPIO_REGS(15, 1, 1, 4, 14), + GPIO_REGS(18, 1, 2, 5, 0), + GPIO_REGS(19, 1, 3, 5, 1), + GPIO_REGS(20, 1, 4, 5, 2), + GPIO_REGS(21, 1, 5, 5, 3), + GPIO_REGS(22, 1, 6, 5, 4), + GPIO_REGS(23, 1, 7, 5, 5), + GPIO_REGS(24, 2, 0, 5, 6), + GPIO_REGS(25, 2, 1, 5, 7), + GPIO_REGS(26, 2, 2, 5, 8), + GPIO_REGS(27, 2, 3, 5, 9), + GPIO_REGS(28, 2, 4, 5, 10), + GPIO_REGS(29, 2, 5, 5, 11), + GPIO_REGS(30, 2, 6, 5, 12), + GPIO_REGS(31, 2, 7, 5, 13), + GPIO_REGS(32, 3, 0, 5, 14), + GPIO_REGS(33, 3, 1, 6, 0), + GPIO_REGS(34, 3, 2, 6, 1), + GPIO_REGS(35, 3, 3, 6, 2), + EMMC_REGS(36, 6, 3), /* EMMC_CMD */ + EMMC_REGS(37, 6, 4), /* EMMC_DS */ + EMMC_REGS(38, 6, 5), /* EMMC_CLK */ + EMMC_REGS(39, 6, 6), /* EMMC_DAT0 */ + EMMC_REGS(40, 6, 7), /* EMMC_DAT1 */ + EMMC_REGS(41, 6, 8), /* EMMC_DAT2 */ + EMMC_REGS(42, 6, 9), /* EMMC_DAT3 */ + EMMC_REGS(43, 6, 10), /* EMMC_DAT4 */ + EMMC_REGS(44, 6, 11), /* EMMC_DAT5 */ + EMMC_REGS(45, 6, 12), /* EMMC_DAT6 */ + EMMC_REGS(46, 6, 13), /* EMMC_DAT7 */ +}; + +static struct pin_regs bcm2712_d0_aon_gpio_pin_regs[] = { + AGPIO_REGS(0, 3, 0, 5, 9), + AGPIO_REGS(1, 3, 1, 5, 10), + AGPIO_REGS(2, 3, 2, 5, 11), + AGPIO_REGS(3, 3, 3, 5, 12), + AGPIO_REGS(4, 3, 4, 5, 13), + AGPIO_REGS(5, 3, 5, 5, 14), + AGPIO_REGS(6, 3, 6, 6, 0), + AGPIO_REGS(8, 3, 7, 6, 1), + AGPIO_REGS(9, 4, 0, 6, 2), + AGPIO_REGS(12, 4, 1, 6, 3), + AGPIO_REGS(13, 4, 2, 6, 4), + AGPIO_REGS(14, 4, 3, 6, 5), + SGPIO_REGS(0, 0, 0), + SGPIO_REGS(1, 0, 1), + SGPIO_REGS(2, 0, 2), + SGPIO_REGS(3, 0, 3), + SGPIO_REGS(4, 1, 0), + SGPIO_REGS(5, 2, 0), +}; + +static const struct pinctrl_pin_desc bcm2712_d0_gpio_pins[] = { + GPIO_PIN(1), + GPIO_PIN(2), + GPIO_PIN(3), + GPIO_PIN(4), + GPIO_PIN(10), + GPIO_PIN(11), + GPIO_PIN(12), + GPIO_PIN(13), + GPIO_PIN(14), + GPIO_PIN(15), + GPIO_PIN(18), + GPIO_PIN(19), + GPIO_PIN(20), + GPIO_PIN(21), + GPIO_PIN(22), + GPIO_PIN(23), + GPIO_PIN(24), + GPIO_PIN(25), + GPIO_PIN(26), + GPIO_PIN(27), + GPIO_PIN(28), + GPIO_PIN(29), + GPIO_PIN(30), + GPIO_PIN(31), + GPIO_PIN(32), + GPIO_PIN(33), + GPIO_PIN(34), + GPIO_PIN(35), + PINCTRL_PIN(36, "emmc_cmd"), + PINCTRL_PIN(37, "emmc_ds"), + PINCTRL_PIN(38, "emmc_clk"), + PINCTRL_PIN(39, "emmc_dat0"), + PINCTRL_PIN(40, "emmc_dat1"), + PINCTRL_PIN(41, "emmc_dat2"), + PINCTRL_PIN(42, "emmc_dat3"), + PINCTRL_PIN(43, "emmc_dat4"), + PINCTRL_PIN(44, "emmc_dat5"), + PINCTRL_PIN(45, "emmc_dat6"), + PINCTRL_PIN(46, "emmc_dat7"), +}; + +static struct pinctrl_pin_desc bcm2712_d0_aon_gpio_pins[] = { + AGPIO_PIN(0), + AGPIO_PIN(1), + AGPIO_PIN(2), + AGPIO_PIN(3), + AGPIO_PIN(4), + AGPIO_PIN(5), + AGPIO_PIN(6), + AGPIO_PIN(8), + AGPIO_PIN(9), + AGPIO_PIN(12), + AGPIO_PIN(13), + AGPIO_PIN(14), + SGPIO_PIN(0), + SGPIO_PIN(1), + SGPIO_PIN(2), + SGPIO_PIN(3), + SGPIO_PIN(4), + SGPIO_PIN(5), +}; + +static const char * const bcm2712_func_names[] = { + FUNC(gpio), + FUNC(alt1), + FUNC(alt2), + FUNC(alt3), + FUNC(alt4), + FUNC(alt5), + FUNC(alt6), + FUNC(alt7), + FUNC(alt8), + FUNC(aon_cpu_standbyb), + FUNC(aon_fp_4sec_resetb), + FUNC(aon_gpclk), + FUNC(aon_pwm), + FUNC(arm_jtag), + FUNC(aud_fs_clk0), + FUNC(avs_pmu_bsc), + FUNC(bsc_m0), + FUNC(bsc_m1), + FUNC(bsc_m2), + FUNC(bsc_m3), + FUNC(clk_observe), + FUNC(ctl_hdmi_5v), + FUNC(enet0), + FUNC(enet0_mii), + FUNC(enet0_rgmii), + FUNC(ext_sc_clk), + FUNC(fl0), + FUNC(fl1), + FUNC(gpclk0), + FUNC(gpclk1), + FUNC(gpclk2), + FUNC(hdmi_tx0_auto_i2c), + FUNC(hdmi_tx0_bsc), + FUNC(hdmi_tx1_auto_i2c), + FUNC(hdmi_tx1_bsc), + FUNC(i2s_in), + FUNC(i2s_out), + FUNC(ir_in), + FUNC(mtsif), + FUNC(mtsif_alt), + FUNC(mtsif_alt1), + FUNC(pdm), + FUNC(pkt), + FUNC(pm_led_out), + FUNC(sc0), + FUNC(sd0), + FUNC(sd2), + FUNC(sd_card_a), + FUNC(sd_card_b), + FUNC(sd_card_c), + FUNC(sd_card_d), + FUNC(sd_card_e), + FUNC(sd_card_f), + FUNC(sd_card_g), + FUNC(spdif_out), + FUNC(spi_m), + FUNC(spi_s), + FUNC(sr_edm_sense), + FUNC(te0), + FUNC(te1), + FUNC(tsio), + FUNC(uart0), + FUNC(uart1), + FUNC(uart2), + FUNC(usb_pwr), + FUNC(usb_vbus), + FUNC(uui), + FUNC(vc_i2c0), + FUNC(vc_i2c3), + FUNC(vc_i2c4), + FUNC(vc_i2c5), + FUNC(vc_i2csl), + FUNC(vc_pcm), + FUNC(vc_pwm0), + FUNC(vc_pwm1), + FUNC(vc_spi0), + FUNC(vc_spi3), + FUNC(vc_spi4), + FUNC(vc_spi5), + FUNC(vc_uart0), + FUNC(vc_uart2), + FUNC(vc_uart3), + FUNC(vc_uart4), +}; + +static const struct bcm2712_pin_funcs bcm2712_c0_aon_gpio_pin_funcs[] = { + PIN(0, ir_in, vc_spi0, vc_uart3, vc_i2c3, te0, vc_i2c0, _, _), + PIN(1, vc_pwm0, vc_spi0, vc_uart3, vc_i2c3, te1, aon_pwm, vc_i2c0, vc_pwm1), + PIN(2, vc_pwm0, vc_spi0, vc_uart3, ctl_hdmi_5v, fl0, aon_pwm, ir_in, vc_pwm1), + PIN(3, ir_in, vc_spi0, vc_uart3, aon_fp_4sec_resetb, fl1, sd_card_g, aon_gpclk, _), + PIN(4, gpclk0, vc_spi0, vc_i2csl, aon_gpclk, pm_led_out, aon_pwm, sd_card_g, vc_pwm0), + PIN(5, gpclk1, ir_in, vc_i2csl, clk_observe, aon_pwm, sd_card_g, vc_pwm0, _), + PIN(6, uart1, vc_uart4, gpclk2, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _), + PIN(7, uart1, vc_uart4, gpclk0, aon_pwm, vc_uart0, vc_spi3, _, _), + PIN(8, uart1, vc_uart4, vc_i2csl, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _), + PIN(9, uart1, vc_uart4, vc_i2csl, aon_pwm, vc_uart0, vc_spi3, _, _), + PIN(10, tsio, ctl_hdmi_5v, sc0, spdif_out, vc_spi5, usb_pwr, aon_gpclk, sd_card_f), + PIN(11, tsio, uart0, sc0, aud_fs_clk0, vc_spi5, usb_vbus, vc_uart2, sd_card_f), + PIN(12, tsio, uart0, vc_uart0, tsio, vc_spi5, usb_pwr, vc_uart2, sd_card_f), + PIN(13, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3), + PIN(14, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3), + PIN(15, ir_in, aon_fp_4sec_resetb, vc_uart0, pm_led_out, ctl_hdmi_5v, aon_pwm, aon_gpclk, _), + PIN(16, aon_cpu_standbyb, gpclk0, pm_led_out, ctl_hdmi_5v, vc_pwm0, usb_pwr, aud_fs_clk0, _), +}; + +static const struct bcm2712_pin_funcs bcm2712_c0_aon_sgpio_pin_funcs[] = { + PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _), + PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _), + PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, ctl_hdmi_5v, _, _, _), + PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, _, _, _, _), + PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c5, ctl_hdmi_5v, _, _, _, _), + PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c5, _, _, _, _, _), +}; + +static const struct bcm2712_pin_funcs bcm2712_c0_gpio_pin_funcs[] = { + PIN(0, bsc_m3, vc_i2c0, gpclk0, enet0, vc_pwm1, vc_spi0, ir_in, _), + PIN(1, bsc_m3, vc_i2c0, gpclk1, enet0, vc_pwm1, sr_edm_sense, vc_spi0, vc_uart3), + PIN(2, pdm, i2s_in, gpclk2, vc_spi4, pkt, vc_spi0, vc_uart3, _), + PIN(3, pdm, i2s_in, vc_spi4, pkt, vc_spi0, vc_uart3, _, _), + PIN(4, pdm, i2s_in, arm_jtag, vc_spi4, pkt, vc_spi0, vc_uart3, _), + PIN(5, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5), + PIN(6, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5), + PIN(7, i2s_out, spdif_out, arm_jtag, sd_card_e, vc_i2c3, enet0_rgmii, vc_pcm, vc_spi4), + PIN(8, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, vc_i2c3, enet0_mii, vc_pcm, vc_spi4), + PIN(9, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, enet0_mii, sd_card_c, vc_spi4, _), + PIN(10, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4), + PIN(11, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4), + PIN(12, spi_s, mtsif_alt1, i2s_in, i2s_out, vc_spi5, vc_i2csl, sd0, sd_card_d), + PIN(13, spi_s, mtsif_alt1, i2s_out, usb_vbus, vc_spi5, vc_i2csl, sd0, sd_card_d), + PIN(14, spi_s, vc_i2csl, enet0_rgmii, arm_jtag, vc_spi5, vc_pwm0, vc_i2c4, sd_card_d), + PIN(15, spi_s, vc_i2csl, vc_spi3, arm_jtag, vc_pwm0, vc_i2c4, gpclk0, _), + PIN(16, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, gpclk1, _), + PIN(17, sd_card_b, i2s_out, vc_spi3, i2s_in, ext_sc_clk, sd0, enet0_rgmii, gpclk2), + PIN(18, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, vc_pwm1, _), + PIN(19, sd_card_b, usb_pwr, vc_spi3, pkt, spdif_out, sd0, ir_in, vc_pwm1), + PIN(20, sd_card_b, uui, vc_uart0, arm_jtag, uart2, usb_pwr, vc_pcm, vc_uart4), + PIN(21, usb_pwr, uui, vc_uart0, arm_jtag, uart2, sd_card_b, vc_pcm, vc_uart4), + PIN(22, usb_pwr, enet0, vc_uart0, mtsif, uart2, usb_vbus, vc_pcm, vc_i2c5), + PIN(23, usb_vbus, enet0, vc_uart0, mtsif, uart2, i2s_out, vc_pcm, vc_i2c5), + PIN(24, mtsif, pkt, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3, _), + PIN(25, mtsif, pkt, sc0, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3), + PIN(26, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _), + PIN(27, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _), + PIN(28, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _), + PIN(29, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _), + PIN(30, mtsif, pkt, sc0, sd2, enet0_rgmii, gpclk0, vc_pwm0, _), + PIN(31, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_pwm0, _), + PIN(32, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_uart3, _), + PIN(33, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_uart3, _, _), + PIN(34, mtsif, pkt, ext_sc_clk, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _), + PIN(35, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _, _), + PIN(36, sd0, mtsif, sc0, i2s_in, vc_uart3, vc_uart2, _, _), + PIN(37, sd0, mtsif, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _), + PIN(38, sd0, mtsif_alt, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _), + PIN(39, sd0, mtsif_alt, sc0, vc_spi0, vc_uart3, vc_uart2, _, _), + PIN(40, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _), + PIN(41, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _), + PIN(42, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m), + PIN(43, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m), + PIN(44, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m), + PIN(45, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m), + PIN(46, vc_spi0, mtsif_alt, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m, _), + PIN(47, enet0, mtsif_alt, i2s_out, mtsif_alt1, arm_jtag, _, _, _), + PIN(48, sc0, usb_pwr, spdif_out, mtsif, _, _, _, _), + PIN(49, sc0, usb_pwr, aud_fs_clk0, mtsif, _, _, _, _), + PIN(50, sc0, usb_vbus, sc0, _, _, _, _, _), + PIN(51, sc0, enet0, sc0, sr_edm_sense, _, _, _, _), + PIN(52, sc0, enet0, vc_pwm1, _, _, _, _, _), + PIN(53, sc0, enet0_rgmii, ext_sc_clk, _, _, _, _, _), +}; + +static const struct bcm2712_pin_funcs bcm2712_d0_aon_gpio_pin_funcs[] = { + PIN(0, ir_in, vc_spi0, vc_uart0, vc_i2c3, uart0, vc_i2c0, _, _), + PIN(1, vc_pwm0, vc_spi0, vc_uart0, vc_i2c3, uart0, aon_pwm, vc_i2c0, vc_pwm1), + PIN(2, vc_pwm0, vc_spi0, vc_uart0, ctl_hdmi_5v, uart0, aon_pwm, ir_in, vc_pwm1), + PIN(3, ir_in, vc_spi0, vc_uart0, uart0, sd_card_g, aon_gpclk, _, _), + PIN(4, gpclk0, vc_spi0, pm_led_out, aon_pwm, sd_card_g, vc_pwm0, _, _), + PIN(5, gpclk1, ir_in, aon_pwm, sd_card_g, vc_pwm0, _, _, _), + PIN(6, uart1, vc_uart2, ctl_hdmi_5v, gpclk2, vc_spi3, _, _, _), + PIN(7, _, _, _, _, _, _, _, _), + PIN(8, uart1, vc_uart2, ctl_hdmi_5v, vc_spi0, vc_spi3, _, _, _), + PIN(9, uart1, vc_uart2, vc_uart0, aon_pwm, vc_spi0, vc_uart2, vc_spi3, _), + PIN(10, _, _, _, _, _, _, _, _), + PIN(11, _, _, _, _, _, _, _, _), + PIN(12, uart1, vc_uart2, vc_uart0, vc_spi0, usb_pwr, vc_uart2, vc_spi3, _), + PIN(13, bsc_m1, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3, _), + PIN(14, bsc_m1, aon_gpclk, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3), +}; + +static const struct bcm2712_pin_funcs bcm2712_d0_aon_sgpio_pin_funcs[] = { + PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _), + PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _), + PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, ctl_hdmi_5v, _, _, _), + PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, _, _, _, _), + PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c3, ctl_hdmi_5v, _, _, _, _), + PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c3, _, _, _, _, _), +}; + +static const struct bcm2712_pin_funcs bcm2712_d0_gpio_pin_funcs[] = { + PIN(1, vc_i2c0, usb_pwr, gpclk0, sd_card_e, vc_spi3, sr_edm_sense, vc_spi0, vc_uart0), + PIN(2, vc_i2c0, usb_pwr, gpclk1, sd_card_e, vc_spi3, clk_observe, vc_spi0, vc_uart0), + PIN(3, vc_i2c3, usb_vbus, gpclk2, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _), + PIN(4, vc_i2c3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _), + PIN(10, bsc_m3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, gpclk0, _, _), + PIN(11, bsc_m3, vc_spi3, clk_observe, sd_card_c, gpclk1, _, _, _), + PIN(12, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _), + PIN(13, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _), + PIN(14, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, sd_card_d, _, _), + PIN(15, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, gpclk0, _, _), + PIN(18, sd_card_f, vc_pwm1, _, _, _, _, _, _), + PIN(19, sd_card_f, usb_pwr, vc_pwm1, _, _, _, _, _), + PIN(20, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _), + PIN(21, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _), + PIN(22, sd_card_f, vc_uart0, vc_i2c3, _, _, _, _, _), + PIN(23, vc_uart0, vc_i2c3, _, _, _, _, _, _), + PIN(24, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _), + PIN(25, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _), + PIN(26, sd_card_b, vc_spi0, arm_jtag, uart0, usb_vbus, vc_uart2, vc_spi0, _), + PIN(27, sd_card_b, vc_spi0, arm_jtag, uart0, vc_uart2, vc_spi0, _, _), + PIN(28, sd_card_b, vc_spi0, arm_jtag, vc_i2c0, vc_spi0, _, _, _), + PIN(29, arm_jtag, vc_i2c0, vc_spi0, _, _, _, _, _), + PIN(30, sd2, gpclk0, vc_pwm0, _, _, _, _, _), + PIN(31, sd2, vc_spi3, vc_pwm0, _, _, _, _, _), + PIN(32, sd2, vc_spi3, vc_uart3, _, _, _, _, _), + PIN(33, sd2, vc_spi3, vc_uart3, _, _, _, _, _), + PIN(34, sd2, vc_spi3, vc_i2c5, _, _, _, _, _), + PIN(35, sd2, vc_spi3, vc_i2c5, _, _, _, _, _), +}; + +static inline u32 bcm2712_reg_rd(struct bcm2712_pinctrl *pc, unsigned reg) +{ + return readl(pc->base + reg); +} + +static inline void bcm2712_reg_wr(struct bcm2712_pinctrl *pc, unsigned reg, + u32 val) +{ + writel(val, pc->base + reg); +} + +static enum bcm2712_funcs bcm2712_pinctrl_fsel_get( + struct bcm2712_pinctrl *pc, unsigned pin) +{ + u32 bit = pc->pin_regs[pin].mux_bit; + enum bcm2712_funcs func; + int fsel; + u32 val; + + if (!bit) + return func_gpio; + bit &= ~MUX_BIT_VALID; + + val = bcm2712_reg_rd(pc, BIT_TO_REG(bit)); + fsel = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK; + func = pc->pin_funcs[pin].funcs[fsel]; + if (func >= func_count) + func = (enum bcm2712_funcs)fsel; + + dev_dbg(pc->dev, "get %04x: %08x (%u => %s)\n", + BIT_TO_REG(bit), val, pin, + bcm2712_func_names[func]); + + return func; +} + +static void bcm2712_pinctrl_fsel_set( + struct bcm2712_pinctrl *pc, unsigned pin, + enum bcm2712_funcs func) +{ + u32 bit = pc->pin_regs[pin].mux_bit, val; + const u8 *pin_funcs; + unsigned long flags; + int fsel; + int cur; + int i; + + if (!bit || func >= func_count) + return; + bit &= ~MUX_BIT_VALID; + + fsel = BCM2712_FSEL_COUNT; + + if (func >= BCM2712_FSEL_COUNT) { + /* Convert to an fsel number */ + pin_funcs = pc->pin_funcs[pin].funcs; + for (i = 1; i < BCM2712_FSEL_COUNT; i++) { + if (pin_funcs[i - 1] == func) { + fsel = i; + break; + } + } + } else { + fsel = (enum bcm2712_funcs)func; + } + if (fsel >= BCM2712_FSEL_COUNT) + return; + + spin_lock_irqsave(&pc->lock, flags); + + val = bcm2712_reg_rd(pc, BIT_TO_REG(bit)); + cur = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK; + + dev_dbg(pc->dev, "read %04x: %08x (%u => %s)\n", + BIT_TO_REG(bit), val, pin, + bcm2712_func_names[cur]); + + if (cur != fsel) { + val &= ~(BCM2712_FSEL_MASK << BIT_TO_SHIFT(bit)); + val |= fsel << BIT_TO_SHIFT(bit); + + dev_dbg(pc->dev, "write %04x: %08x (%u <= %s)\n", + BIT_TO_REG(bit), val, pin, + bcm2712_func_names[fsel]); + bcm2712_reg_wr(pc, BIT_TO_REG(bit), val); + } + + spin_unlock_irqrestore(&pc->lock, flags); +} + +static int bcm2712_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + return pc->pctl_desc.npins; +} + +static const char *bcm2712_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + return pc->gpio_groups[selector]; +} + +static int bcm2712_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + *pins = &pc->pctl_desc.pins[selector].number; + *num_pins = 1; + + return 0; +} + +static void bcm2712_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + enum bcm2712_funcs fsel = bcm2712_pinctrl_fsel_get(pc, offset); + const char *fname = bcm2712_func_names[fsel]; + + seq_printf(s, "function %s", fname); +} + +static void bcm2712_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(maps[i].data.configs.configs); + + kfree(maps); +} + +static const struct pinctrl_ops bcm2712_pctl_ops = { + .get_groups_count = bcm2712_pctl_get_groups_count, + .get_group_name = bcm2712_pctl_get_group_name, + .get_group_pins = bcm2712_pctl_get_group_pins, + .pin_dbg_show = bcm2712_pctl_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = bcm2712_pctl_dt_free_map, +}; + +static int bcm2712_pmx_free(struct pinctrl_dev *pctldev, + unsigned offset) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + /* disable by setting to GPIO */ + bcm2712_pinctrl_fsel_set(pc, offset, func_gpio); + return 0; +} + +static int bcm2712_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ + return func_count; +} + +static const char *bcm2712_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return (selector < func_count) ? bcm2712_func_names[selector] : NULL; +} + +static int bcm2712_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + /* every pin can do every function */ + *groups = pc->gpio_groups; + *num_groups = pc->pctl_desc.npins; + + return 0; +} + +static int bcm2712_pmx_set(struct pinctrl_dev *pctldev, + unsigned func_selector, + unsigned group_selector) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + const struct pinctrl_desc *pctldesc = &pc->pctl_desc; + const struct pinctrl_pin_desc *pindesc; + + if (group_selector >= pctldesc->npins) + return -EINVAL; + pindesc = &pctldesc->pins[group_selector]; + bcm2712_pinctrl_fsel_set(pc, pindesc->number, func_selector); + + return 0; +} +static int bcm2712_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + bcm2712_pinctrl_fsel_set(pc, pin, func_gpio); + + return 0; +} + +static void bcm2712_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + /* disable by setting to GPIO */ + bcm2712_pinctrl_fsel_set(pc, offset, func_gpio); +} + +static const struct pinmux_ops bcm2712_pmx_ops = { + .free = bcm2712_pmx_free, + .get_functions_count = bcm2712_pmx_get_functions_count, + .get_function_name = bcm2712_pmx_get_function_name, + .get_function_groups = bcm2712_pmx_get_function_groups, + .set_mux = bcm2712_pmx_set, + .gpio_request_enable = bcm2712_pmx_gpio_request_enable, + .gpio_disable_free = bcm2712_pmx_gpio_disable_free, +}; + +static unsigned int bcm2712_pull_config_get(struct bcm2712_pinctrl *pc, + unsigned int pin) +{ + u32 bit = pc->pin_regs[pin].pad_bit, val; + + if (unlikely(bit == REG_BIT_INVALID)) + return BCM2712_PULL_NONE; + + val = bcm2712_reg_rd(pc, BIT_TO_REG(bit)); + return (val >> BIT_TO_SHIFT(bit)) & BCM2712_PULL_MASK; +} + +static void bcm2712_pull_config_set(struct bcm2712_pinctrl *pc, + unsigned int pin, unsigned int arg) +{ + u32 bit = pc->pin_regs[pin].pad_bit, val; + unsigned long flags; + + if (unlikely(bit == REG_BIT_INVALID)) { + dev_warn(pc->dev, "can't set pulls for %s\n", pc->gpio_groups[pin]); + return; + } + + spin_lock_irqsave(&pc->lock, flags); + + val = bcm2712_reg_rd(pc, BIT_TO_REG(bit)); + val &= ~(BCM2712_PULL_MASK << BIT_TO_SHIFT(bit)); + val |= (arg << BIT_TO_SHIFT(bit)); + bcm2712_reg_wr(pc, BIT_TO_REG(bit), val); + + spin_unlock_irqrestore(&pc->lock, flags); +} + +static int bcm2712_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 arg; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_NONE); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_DOWN); + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_UP); + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return -ENOTSUPP; +} + +static int bcm2712_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + u32 param, arg; + int i; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + bcm2712_pull_config_set(pc, pin, BCM2712_PULL_NONE); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + bcm2712_pull_config_set(pc, pin, BCM2712_PULL_DOWN); + break; + case PIN_CONFIG_BIAS_PULL_UP: + bcm2712_pull_config_set(pc, pin, BCM2712_PULL_UP); + break; + default: + return -ENOTSUPP; + } + } /* for each config */ + + return 0; +} + +static const struct pinconf_ops bcm2712_pinconf_ops = { + .is_generic = true, + .pin_config_get = bcm2712_pinconf_get, + .pin_config_set = bcm2712_pinconf_set, +}; + +static const struct pinctrl_desc bcm2712_c0_pinctrl_desc = { + .name = "pinctrl-bcm2712", + .pins = bcm2712_c0_gpio_pins, + .npins = ARRAY_SIZE(bcm2712_c0_gpio_pins), + .pctlops = &bcm2712_pctl_ops, + .pmxops = &bcm2712_pmx_ops, + .confops = &bcm2712_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const struct pinctrl_desc bcm2712_c0_aon_pinctrl_desc = { + .name = "aon-pinctrl-bcm2712", + .pins = bcm2712_c0_aon_gpio_pins, + .npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins), + .pctlops = &bcm2712_pctl_ops, + .pmxops = &bcm2712_pmx_ops, + .confops = &bcm2712_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const struct pinctrl_desc bcm2712_d0_pinctrl_desc = { + .name = "pinctrl-bcm2712", + .pins = bcm2712_d0_gpio_pins, + .npins = ARRAY_SIZE(bcm2712_d0_gpio_pins), + .pctlops = &bcm2712_pctl_ops, + .pmxops = &bcm2712_pmx_ops, + .confops = &bcm2712_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const struct pinctrl_desc bcm2712_d0_aon_pinctrl_desc = { + .name = "aon-pinctrl-bcm2712", + .pins = bcm2712_d0_aon_gpio_pins, + .npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins), + .pctlops = &bcm2712_pctl_ops, + .pmxops = &bcm2712_pmx_ops, + .confops = &bcm2712_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const struct pinctrl_gpio_range bcm2712_c0_pinctrl_gpio_range = { + .name = "pinctrl-bcm2712", + .npins = ARRAY_SIZE(bcm2712_c0_gpio_pins), +}; + +static const struct pinctrl_gpio_range bcm2712_c0_aon_pinctrl_gpio_range = { + .name = "aon-pinctrl-bcm2712", + .npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins), +}; + +static const struct pinctrl_gpio_range bcm2712_d0_pinctrl_gpio_range = { + .name = "pinctrl-bcm2712", + .npins = ARRAY_SIZE(bcm2712_d0_gpio_pins), +}; + +static const struct pinctrl_gpio_range bcm2712_d0_aon_pinctrl_gpio_range = { + .name = "aon-pinctrl-bcm2712", + .npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins), +}; + +static const struct bcm_plat_data bcm2712_c0_plat_data = { + .pctl_desc = &bcm2712_c0_pinctrl_desc, + .gpio_range = &bcm2712_c0_pinctrl_gpio_range, + .pin_regs = bcm2712_c0_gpio_pin_regs, + .pin_funcs = bcm2712_c0_gpio_pin_funcs, +}; + +static const struct bcm_plat_data bcm2712_c0_aon_plat_data = { + .pctl_desc = &bcm2712_c0_aon_pinctrl_desc, + .gpio_range = &bcm2712_c0_aon_pinctrl_gpio_range, + .pin_regs = bcm2712_c0_aon_gpio_pin_regs, + .pin_funcs = bcm2712_c0_aon_gpio_pin_funcs, +}; + +static const struct bcm_plat_data bcm2712_d0_plat_data = { + .pctl_desc = &bcm2712_d0_pinctrl_desc, + .gpio_range = &bcm2712_d0_pinctrl_gpio_range, + .pin_regs = bcm2712_d0_gpio_pin_regs, + .pin_funcs = bcm2712_d0_gpio_pin_funcs, +}; + +static const struct bcm_plat_data bcm2712_d0_aon_plat_data = { + .pctl_desc = &bcm2712_d0_aon_pinctrl_desc, + .gpio_range = &bcm2712_d0_aon_pinctrl_gpio_range, + .pin_regs = bcm2712_d0_aon_gpio_pin_regs, + .pin_funcs = bcm2712_d0_aon_gpio_pin_funcs, +}; + +static const struct of_device_id bcm2712_pinctrl_match[] = { + { + .compatible = "brcm,bcm2712-pinctrl", + .data = &bcm2712_c0_plat_data, + }, + { + .compatible = "brcm,bcm2712-aon-pinctrl", + .data = &bcm2712_c0_aon_plat_data, + }, + + { + .compatible = "brcm,bcm2712c0-pinctrl", + .data = &bcm2712_c0_plat_data, + }, + { + .compatible = "brcm,bcm2712c0-aon-pinctrl", + .data = &bcm2712_c0_aon_plat_data, + }, + + { + .compatible = "brcm,bcm2712d0-pinctrl", + .data = &bcm2712_d0_plat_data, + }, + { + .compatible = "brcm,bcm2712d0-aon-pinctrl", + .data = &bcm2712_d0_aon_plat_data, + }, + {} +}; + +static int bcm2712_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct bcm_plat_data *pdata; + const struct of_device_id *match; + struct bcm2712_pinctrl *pc; + const char **names; + int num_pins, i; + + match = of_match_node(bcm2712_pinctrl_match, np); + if (!match) + return -EINVAL; + pdata = match->data; + + pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return -ENOMEM; + + platform_set_drvdata(pdev, pc); + pc->dev = dev; + spin_lock_init(&pc->lock); + + pc->base = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(pc->base)) { + dev_err(dev, "could not get IO memory\n"); + return PTR_ERR(pc->base); + } + + pc->pctl_desc = *pdata->pctl_desc; + num_pins = pc->pctl_desc.npins; + names = devm_kmalloc_array(dev, num_pins, sizeof(const char *), + GFP_KERNEL); + if (!names) + return -ENOMEM; + for (i = 0; i < num_pins; i++) + names[i] = pc->pctl_desc.pins[i].name; + pc->gpio_groups = names; + pc->pin_regs = pdata->pin_regs; + pc->pin_funcs = pdata->pin_funcs; + pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc); + if (IS_ERR(pc->pctl_dev)) + return PTR_ERR(pc->pctl_dev); + + pc->gpio_range = *pdata->gpio_range; + pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range); + + return 0; +} + +static struct platform_driver bcm2712_pinctrl_driver = { + .probe = bcm2712_pinctrl_probe, + .driver = { + .name = MODULE_NAME, + .of_match_table = bcm2712_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(bcm2712_pinctrl_driver); diff --git a/drivers/pinctrl/pinctrl-rp1.c b/drivers/pinctrl/pinctrl-rp1.c new file mode 100644 index 0000000000000..6684b171e98b5 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rp1.c @@ -0,0 +1,1605 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Raspberry Pi RP1 GPIO unit (pinctrl + GPIO) + * + * Copyright (C) 2023 Raspberry Pi Ltd. + * + * This driver is inspired by: + * pinctrl-bcm2835.c, please see original file for copyright information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define MODULE_NAME "pinctrl-rp1" +#define RP1_NUM_GPIOS 54 +#define RP1_NUM_BANKS 3 + +#define RP1_RW_OFFSET 0x0000 +#define RP1_XOR_OFFSET 0x1000 +#define RP1_SET_OFFSET 0x2000 +#define RP1_CLR_OFFSET 0x3000 + +#define RP1_GPIO_STATUS 0x0000 +#define RP1_GPIO_CTRL 0x0004 + +#define RP1_GPIO_PCIE_INTE 0x011c +#define RP1_GPIO_PCIE_INTS 0x0124 + +#define RP1_GPIO_EVENTS_SHIFT_RAW 20 +#define RP1_GPIO_STATUS_FALLING BIT(20) +#define RP1_GPIO_STATUS_RISING BIT(21) +#define RP1_GPIO_STATUS_LOW BIT(22) +#define RP1_GPIO_STATUS_HIGH BIT(23) + +#define RP1_GPIO_EVENTS_SHIFT_FILTERED 24 +#define RP1_GPIO_STATUS_F_FALLING BIT(24) +#define RP1_GPIO_STATUS_F_RISING BIT(25) +#define RP1_GPIO_STATUS_F_LOW BIT(26) +#define RP1_GPIO_STATUS_F_HIGH BIT(27) + +#define RP1_GPIO_CTRL_FUNCSEL_LSB 0 +#define RP1_GPIO_CTRL_FUNCSEL_MASK 0x0000001f +#define RP1_GPIO_CTRL_OUTOVER_LSB 12 +#define RP1_GPIO_CTRL_OUTOVER_MASK 0x00003000 +#define RP1_GPIO_CTRL_OEOVER_LSB 14 +#define RP1_GPIO_CTRL_OEOVER_MASK 0x0000c000 +#define RP1_GPIO_CTRL_INOVER_LSB 16 +#define RP1_GPIO_CTRL_INOVER_MASK 0x00030000 +#define RP1_GPIO_CTRL_IRQEN_FALLING BIT(20) +#define RP1_GPIO_CTRL_IRQEN_RISING BIT(21) +#define RP1_GPIO_CTRL_IRQEN_LOW BIT(22) +#define RP1_GPIO_CTRL_IRQEN_HIGH BIT(23) +#define RP1_GPIO_CTRL_IRQEN_F_FALLING BIT(24) +#define RP1_GPIO_CTRL_IRQEN_F_RISING BIT(25) +#define RP1_GPIO_CTRL_IRQEN_F_LOW BIT(26) +#define RP1_GPIO_CTRL_IRQEN_F_HIGH BIT(27) +#define RP1_GPIO_CTRL_IRQRESET BIT(28) +#define RP1_GPIO_CTRL_IRQOVER_LSB 30 +#define RP1_GPIO_CTRL_IRQOVER_MASK 0xc0000000 + +#define RP1_INT_EDGE_FALLING BIT(0) +#define RP1_INT_EDGE_RISING BIT(1) +#define RP1_INT_LEVEL_LOW BIT(2) +#define RP1_INT_LEVEL_HIGH BIT(3) +#define RP1_INT_MASK 0xf + +#define RP1_INT_EDGE_BOTH (RP1_INT_EDGE_FALLING | \ + RP1_INT_EDGE_RISING) +#define RP1_PUD_OFF 0 +#define RP1_PUD_DOWN 1 +#define RP1_PUD_UP 2 + +#define RP1_FSEL_COUNT 9 + +#define RP1_FSEL_ALT0 0x00 +#define RP1_FSEL_GPIO 0x05 +#define RP1_FSEL_NONE 0x09 +#define RP1_FSEL_NONE_HW 0x1f + +#define RP1_DIR_OUTPUT 0 +#define RP1_DIR_INPUT 1 + +#define RP1_OUTOVER_PERI 0 +#define RP1_OUTOVER_INVPERI 1 +#define RP1_OUTOVER_LOW 2 +#define RP1_OUTOVER_HIGH 3 + +#define RP1_OEOVER_PERI 0 +#define RP1_OEOVER_INVPERI 1 +#define RP1_OEOVER_DISABLE 2 +#define RP1_OEOVER_ENABLE 3 + +#define RP1_INOVER_PERI 0 +#define RP1_INOVER_INVPERI 1 +#define RP1_INOVER_LOW 2 +#define RP1_INOVER_HIGH 3 + +#define RP1_RIO_OUT 0x00 +#define RP1_RIO_OE 0x04 +#define RP1_RIO_IN 0x08 + +#define RP1_PAD_SLEWFAST_MASK 0x00000001 +#define RP1_PAD_SLEWFAST_LSB 0 +#define RP1_PAD_SCHMITT_MASK 0x00000002 +#define RP1_PAD_SCHMITT_LSB 1 +#define RP1_PAD_PULL_MASK 0x0000000c +#define RP1_PAD_PULL_LSB 2 +#define RP1_PAD_DRIVE_MASK 0x00000030 +#define RP1_PAD_DRIVE_LSB 4 +#define RP1_PAD_IN_ENABLE_MASK 0x00000040 +#define RP1_PAD_IN_ENABLE_LSB 6 +#define RP1_PAD_OUT_DISABLE_MASK 0x00000080 +#define RP1_PAD_OUT_DISABLE_LSB 7 + +#define RP1_PAD_DRIVE_2MA 0x00000000 +#define RP1_PAD_DRIVE_4MA 0x00000010 +#define RP1_PAD_DRIVE_8MA 0x00000020 +#define RP1_PAD_DRIVE_12MA 0x00000030 + +#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB)) +#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB))) + +#define FUNC(f) \ + [func_##f] = #f +#define RP1_MAX_FSEL 8 +#define PIN(i, f0, f1, f2, f3, f4, f5, f6, f7, f8) \ + [i] = { \ + .funcs = { \ + func_##f0, \ + func_##f1, \ + func_##f2, \ + func_##f3, \ + func_##f4, \ + func_##f5, \ + func_##f6, \ + func_##f7, \ + func_##f8, \ + }, \ + } + +#define LEGACY_MAP(n, f0, f1, f2, f3, f4, f5) \ + [n] = { \ + func_gpio, \ + func_gpio, \ + func_##f5, \ + func_##f4, \ + func_##f0, \ + func_##f1, \ + func_##f2, \ + func_##f3, \ + } + +struct rp1_iobank_desc { + int min_gpio; + int num_gpios; + int gpio_offset; + int inte_offset; + int ints_offset; + int rio_offset; + int pads_offset; +}; + +struct rp1_pin_info { + u8 num; + u8 bank; + u8 offset; + u8 fsel; + u8 irq_type; + + void __iomem *gpio; + void __iomem *rio; + void __iomem *inte; + void __iomem *ints; + void __iomem *pad; +}; + +enum funcs { + func_alt0, + func_alt1, + func_alt2, + func_alt3, + func_alt4, + func_gpio, + func_alt6, + func_alt7, + func_alt8, + func_none, + func_aaud, + func_dcd0, + func_dpi, + func_dsi0_te_ext, + func_dsi1_te_ext, + func_dsr0, + func_dtr0, + func_gpclk0, + func_gpclk1, + func_gpclk2, + func_gpclk3, + func_gpclk4, + func_gpclk5, + func_i2c0, + func_i2c1, + func_i2c2, + func_i2c3, + func_i2c4, + func_i2c5, + func_i2c6, + func_i2s0, + func_i2s1, + func_i2s2, + func_ir, + func_mic, + func_pcie_clkreq_n, + func_pio, + func_proc_rio, + func_pwm0, + func_pwm1, + func_ri0, + func_sd0, + func_sd1, + func_spi0, + func_spi1, + func_spi2, + func_spi3, + func_spi4, + func_spi5, + func_spi6, + func_spi7, + func_spi8, + func_uart0, + func_uart1, + func_uart2, + func_uart3, + func_uart4, + func_uart5, + func_vbus0, + func_vbus1, + func_vbus2, + func_vbus3, + func__, + func_count = func__, + func_invalid = func__, +}; + +struct rp1_pin_funcs { + u8 funcs[RP1_FSEL_COUNT]; +}; + +struct rp1_pinctrl { + struct device *dev; + void __iomem *gpio_base; + void __iomem *rio_base; + void __iomem *pads_base; + int irq[RP1_NUM_BANKS]; + struct rp1_pin_info pins[RP1_NUM_GPIOS]; + + struct pinctrl_dev *pctl_dev; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range gpio_range; + + raw_spinlock_t irq_lock[RP1_NUM_BANKS]; +}; + +const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = { + /* gpio inte ints rio pads */ + { 0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 }, + { 28, 6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 }, + { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 }, +}; + +/* pins are just named GPIO0..GPIO53 */ +#define RP1_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a) +static struct pinctrl_pin_desc rp1_gpio_pins[] = { + RP1_GPIO_PIN(0), + RP1_GPIO_PIN(1), + RP1_GPIO_PIN(2), + RP1_GPIO_PIN(3), + RP1_GPIO_PIN(4), + RP1_GPIO_PIN(5), + RP1_GPIO_PIN(6), + RP1_GPIO_PIN(7), + RP1_GPIO_PIN(8), + RP1_GPIO_PIN(9), + RP1_GPIO_PIN(10), + RP1_GPIO_PIN(11), + RP1_GPIO_PIN(12), + RP1_GPIO_PIN(13), + RP1_GPIO_PIN(14), + RP1_GPIO_PIN(15), + RP1_GPIO_PIN(16), + RP1_GPIO_PIN(17), + RP1_GPIO_PIN(18), + RP1_GPIO_PIN(19), + RP1_GPIO_PIN(20), + RP1_GPIO_PIN(21), + RP1_GPIO_PIN(22), + RP1_GPIO_PIN(23), + RP1_GPIO_PIN(24), + RP1_GPIO_PIN(25), + RP1_GPIO_PIN(26), + RP1_GPIO_PIN(27), + RP1_GPIO_PIN(28), + RP1_GPIO_PIN(29), + RP1_GPIO_PIN(30), + RP1_GPIO_PIN(31), + RP1_GPIO_PIN(32), + RP1_GPIO_PIN(33), + RP1_GPIO_PIN(34), + RP1_GPIO_PIN(35), + RP1_GPIO_PIN(36), + RP1_GPIO_PIN(37), + RP1_GPIO_PIN(38), + RP1_GPIO_PIN(39), + RP1_GPIO_PIN(40), + RP1_GPIO_PIN(41), + RP1_GPIO_PIN(42), + RP1_GPIO_PIN(43), + RP1_GPIO_PIN(44), + RP1_GPIO_PIN(45), + RP1_GPIO_PIN(46), + RP1_GPIO_PIN(47), + RP1_GPIO_PIN(48), + RP1_GPIO_PIN(49), + RP1_GPIO_PIN(50), + RP1_GPIO_PIN(51), + RP1_GPIO_PIN(52), + RP1_GPIO_PIN(53), +}; + +/* one pin per group */ +static const char * const rp1_gpio_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", + "gpio8", + "gpio9", + "gpio10", + "gpio11", + "gpio12", + "gpio13", + "gpio14", + "gpio15", + "gpio16", + "gpio17", + "gpio18", + "gpio19", + "gpio20", + "gpio21", + "gpio22", + "gpio23", + "gpio24", + "gpio25", + "gpio26", + "gpio27", + "gpio28", + "gpio29", + "gpio30", + "gpio31", + "gpio32", + "gpio33", + "gpio34", + "gpio35", + "gpio36", + "gpio37", + "gpio38", + "gpio39", + "gpio40", + "gpio41", + "gpio42", + "gpio43", + "gpio44", + "gpio45", + "gpio46", + "gpio47", + "gpio48", + "gpio49", + "gpio50", + "gpio51", + "gpio52", + "gpio53", +}; + +static const char * const rp1_func_names[] = { + FUNC(alt0), + FUNC(alt1), + FUNC(alt2), + FUNC(alt3), + FUNC(alt4), + FUNC(gpio), + FUNC(alt6), + FUNC(alt7), + FUNC(alt8), + FUNC(none), + FUNC(aaud), + FUNC(dcd0), + FUNC(dpi), + FUNC(dsi0_te_ext), + FUNC(dsi1_te_ext), + FUNC(dsr0), + FUNC(dtr0), + FUNC(gpclk0), + FUNC(gpclk1), + FUNC(gpclk2), + FUNC(gpclk3), + FUNC(gpclk4), + FUNC(gpclk5), + FUNC(i2c0), + FUNC(i2c1), + FUNC(i2c2), + FUNC(i2c3), + FUNC(i2c4), + FUNC(i2c5), + FUNC(i2c6), + FUNC(i2s0), + FUNC(i2s1), + FUNC(i2s2), + FUNC(ir), + FUNC(mic), + FUNC(pcie_clkreq_n), + FUNC(pio), + FUNC(proc_rio), + FUNC(pwm0), + FUNC(pwm1), + FUNC(ri0), + FUNC(sd0), + FUNC(sd1), + FUNC(spi0), + FUNC(spi1), + FUNC(spi2), + FUNC(spi3), + FUNC(spi4), + FUNC(spi5), + FUNC(spi6), + FUNC(spi7), + FUNC(spi8), + FUNC(uart0), + FUNC(uart1), + FUNC(uart2), + FUNC(uart3), + FUNC(uart4), + FUNC(uart5), + FUNC(vbus0), + FUNC(vbus1), + FUNC(vbus2), + FUNC(vbus3), + [func_invalid] = "?" +}; + +static const struct rp1_pin_funcs rp1_gpio_pin_funcs[] = { + PIN(0, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2), + PIN(1, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2), + PIN(2, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2), + PIN(3, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2), + PIN(4, gpclk0, dpi, uart2, i2c2, ri0, gpio, proc_rio, pio, spi3), + PIN(5, gpclk1, dpi, uart2, i2c2, dtr0, gpio, proc_rio, pio, spi3), + PIN(6, gpclk2, dpi, uart2, i2c3, dcd0, gpio, proc_rio, pio, spi3), + PIN(7, spi0, dpi, uart2, i2c3, dsr0, gpio, proc_rio, pio, spi3), + PIN(8, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4), + PIN(9, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4), + PIN(10, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4), + PIN(11, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4), + PIN(12, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5), + PIN(13, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5), + PIN(14, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5), + PIN(15, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5), + PIN(16, spi1, dpi, dsi0_te_ext, _, uart0, gpio, proc_rio, pio, _), + PIN(17, spi1, dpi, dsi1_te_ext, _, uart0, gpio, proc_rio, pio, _), + PIN(18, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, gpclk1), + PIN(19, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, _), + PIN(20, spi1, dpi, i2s0, gpclk0, i2s1, gpio, proc_rio, pio, _), + PIN(21, spi1, dpi, i2s0, gpclk1, i2s1, gpio, proc_rio, pio, _), + PIN(22, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _), + PIN(23, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _), + PIN(24, sd0, dpi, i2s0, _, i2s1, gpio, proc_rio, pio, spi2), + PIN(25, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi3), + PIN(26, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi5), + PIN(27, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi1), + PIN(28, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _), + PIN(29, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _), + PIN(30, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(31, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(32, sd1, gpclk3, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(33, sd1, gpclk4, i2s2, spi6, uart5, gpio, proc_rio, _, _), + PIN(34, pwm1, gpclk3, vbus0, i2c4, mic, gpio, proc_rio, _, _), + PIN(35, spi8, pwm1, vbus0, i2c4, mic, gpio, proc_rio, _, _), + PIN(36, spi8, uart5, pcie_clkreq_n, i2c5, mic, gpio, proc_rio, _, _), + PIN(37, spi8, uart5, mic, i2c5, pcie_clkreq_n, gpio, proc_rio, _, _), + PIN(38, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi0_te_ext, _), + PIN(39, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi1_te_ext, _), + PIN(40, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _), + PIN(41, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _), + PIN(42, gpclk5, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(43, gpclk4, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(44, gpclk5, i2c5, pwm1, spi6, i2s2, gpio, proc_rio, _, _), + PIN(45, pwm1, i2c5, spi7, spi6, i2s2, gpio, proc_rio, _, _), + PIN(46, gpclk3, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi0_te_ext, _), + PIN(47, gpclk5, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi1_te_ext, _), + PIN(48, pwm1, pcie_clkreq_n, spi7, mic, uart5, gpio, proc_rio, _, _), + PIN(49, spi8, spi7, i2c5, aaud, uart5, gpio, proc_rio, _, _), + PIN(50, spi8, spi7, i2c5, aaud, vbus2, gpio, proc_rio, _, _), + PIN(51, spi8, spi7, i2c6, aaud, vbus2, gpio, proc_rio, _, _), + PIN(52, spi8, _, i2c6, aaud, vbus3, gpio, proc_rio, _, _), + PIN(53, spi8, spi7, _, pcie_clkreq_n, vbus3, gpio, proc_rio, _, _), +}; + +static const u8 legacy_fsel_map[][8] = { + LEGACY_MAP(0, i2c0, _, dpi, spi2, uart1, _), + LEGACY_MAP(1, i2c0, _, dpi, spi2, uart1, _), + LEGACY_MAP(2, i2c1, _, dpi, spi2, uart1, _), + LEGACY_MAP(3, i2c1, _, dpi, spi2, uart1, _), + LEGACY_MAP(4, gpclk0, _, dpi, spi3, uart2, i2c2), + LEGACY_MAP(5, gpclk1, _, dpi, spi3, uart2, i2c2), + LEGACY_MAP(6, gpclk2, _, dpi, spi3, uart2, i2c3), + LEGACY_MAP(7, spi0, _, dpi, spi3, uart2, i2c3), + LEGACY_MAP(8, spi0, _, dpi, _, uart3, i2c0), + LEGACY_MAP(9, spi0, _, dpi, _, uart3, i2c0), + LEGACY_MAP(10, spi0, _, dpi, _, uart3, i2c1), + LEGACY_MAP(11, spi0, _, dpi, _, uart3, i2c1), + LEGACY_MAP(12, pwm0, _, dpi, spi5, uart4, i2c2), + LEGACY_MAP(13, pwm0, _, dpi, spi5, uart4, i2c2), + LEGACY_MAP(14, uart0, _, dpi, spi5, uart4, _), + LEGACY_MAP(15, uart0, _, dpi, spi5, uart4, _), + LEGACY_MAP(16, _, _, dpi, uart0, spi1, _), + LEGACY_MAP(17, _, _, dpi, uart0, spi1, _), + LEGACY_MAP(18, i2s0, _, dpi, _, spi1, pwm0), + LEGACY_MAP(19, i2s0, _, dpi, _, spi1, pwm0), + LEGACY_MAP(20, i2s0, _, dpi, _, spi1, gpclk0), + LEGACY_MAP(21, i2s0, _, dpi, _, spi1, gpclk1), + LEGACY_MAP(22, sd0, _, dpi, _, _, i2c3), + LEGACY_MAP(23, sd0, _, dpi, _, _, i2c3), + LEGACY_MAP(24, sd0, _, dpi, _, _, spi2), + LEGACY_MAP(25, sd0, _, dpi, _, _, spi3), + LEGACY_MAP(26, sd0, _, dpi, _, _, spi5), + LEGACY_MAP(27, sd0, _, dpi, _, _, _), +}; + +static const char * const irq_type_names[] = { + [IRQ_TYPE_NONE] = "none", + [IRQ_TYPE_EDGE_RISING] = "edge-rising", + [IRQ_TYPE_EDGE_FALLING] = "edge-falling", + [IRQ_TYPE_EDGE_BOTH] = "edge-both", + [IRQ_TYPE_LEVEL_HIGH] = "level-high", + [IRQ_TYPE_LEVEL_LOW] = "level-low", +}; + +static bool persist_gpio_outputs = true; +module_param(persist_gpio_outputs, bool, 0644); +MODULE_PARM_DESC(persist_gpio_outputs, "Enable GPIO_OUT persistence when pin is freed"); + +static int rp1_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int offset, unsigned long *configs, + unsigned int num_configs); + +static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip, + unsigned int offset) +{ + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + + if (pc && offset < RP1_NUM_GPIOS) + return &pc->pins[offset]; + return NULL; +} + +static struct rp1_pin_info *rp1_get_pin_pctl(struct pinctrl_dev *pctldev, + unsigned int offset) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + if (pc && offset < RP1_NUM_GPIOS) + return &pc->pins[offset]; + return NULL; +} + +static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set) +{ + u32 padctrl = readl(pin->pad); + + padctrl &= ~clr; + padctrl |= set; + + writel(padctrl, pin->pad); +} + +static void rp1_input_enable(struct rp1_pin_info *pin, int value) +{ + rp1_pad_update(pin, RP1_PAD_IN_ENABLE_MASK, + value ? RP1_PAD_IN_ENABLE_MASK : 0); +} + +static void rp1_output_enable(struct rp1_pin_info *pin, int value) +{ + rp1_pad_update(pin, RP1_PAD_OUT_DISABLE_MASK, + value ? 0 : RP1_PAD_OUT_DISABLE_MASK); +} + +static u32 rp1_get_fsel(struct rp1_pin_info *pin) +{ + u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL); + u32 oeover = FLD_GET(ctrl, RP1_GPIO_CTRL_OEOVER); + u32 fsel = FLD_GET(ctrl, RP1_GPIO_CTRL_FUNCSEL); + + if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE; + + return fsel; +} + +static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel) +{ + u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL); + + if (fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE_HW; + + rp1_input_enable(pin, 1); + rp1_output_enable(pin, 1); + + if (fsel == RP1_FSEL_NONE) { + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE); + } else { + FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI); + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI); + } + FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel); + writel(ctrl, pin->gpio + RP1_GPIO_CTRL); +} + +static int rp1_get_dir(struct rp1_pin_info *pin) +{ + return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ? + RP1_DIR_INPUT : RP1_DIR_OUTPUT; +} + +static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input) +{ + int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET; + + writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset); +} + +static int rp1_get_value(struct rp1_pin_info *pin) +{ + return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset)); +} + +static void rp1_set_value(struct rp1_pin_info *pin, int value) +{ + /* Assume the pin is already an output */ + writel(1 << pin->offset, + pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET)); +} + +static int rp1_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + int ret; + + if (!pin) + return -EINVAL; + ret = rp1_get_value(pin); + return ret; +} + +static void rp1_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (pin) + rp1_set_value(pin, value); +} + +static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + u32 fsel; + + if (!pin) + return -EINVAL; + fsel = rp1_get_fsel(pin); + if (fsel != RP1_FSEL_GPIO) + return -EINVAL; + return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ? + GPIO_LINE_DIRECTION_OUT : + GPIO_LINE_DIRECTION_IN; +} + +static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (!pin) + return -EINVAL; + rp1_set_dir(pin, RP1_DIR_INPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + return 0; +} + +static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(chip, offset); + + if (!pin) + return -EINVAL; + rp1_set_value(pin, value); + rp1_set_dir(pin, RP1_DIR_OUTPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + return 0; +} + +static int rp1_gpio_set_config(struct gpio_chip *gc, unsigned offset, + unsigned long config) +{ + struct rp1_pinctrl *pc = gpiochip_get_data(gc); + unsigned long configs[] = { config }; + + return rp1_pinconf_set(pc->pctl_dev, offset, configs, + ARRAY_SIZE(configs)); +} + +static const struct gpio_chip rp1_gpio_chip = { + .label = MODULE_NAME, + .owner = THIS_MODULE, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .direction_input = rp1_gpio_direction_input, + .direction_output = rp1_gpio_direction_output, + .get_direction = rp1_gpio_get_direction, + .get = rp1_gpio_get, + .set = rp1_gpio_set, + .base = -1, + .set_config = rp1_gpio_set_config, + .ngpio = RP1_NUM_GPIOS, + .can_sleep = false, +}; + +static void rp1_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + struct irq_chip *host_chip = irq_desc_get_chip(desc); + const struct rp1_iobank_desc *bank; + int irq = irq_desc_get_irq(desc); + unsigned long ints; + int b; + + if (pc->irq[0] == irq) + bank = &rp1_iobanks[0]; + else if (pc->irq[1] == irq) + bank = &rp1_iobanks[1]; + else + bank = &rp1_iobanks[2]; + + chained_irq_enter(host_chip, desc); + + ints = readl(pc->gpio_base + bank->ints_offset); + for_each_set_bit(b, &ints, 32) { + struct rp1_pin_info *pin = rp1_get_pin(chip, bank->min_gpio + b); + + writel(RP1_GPIO_CTRL_IRQRESET, + pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL); + generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.domain, + bank->min_gpio + b)); + } + + chained_irq_exit(host_chip, desc); +} + +static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable) +{ + writel(1 << pin->offset, + pin->inte + (enable ? RP1_SET_OFFSET : RP1_CLR_OFFSET)); + if (!enable) + /* Clear any latched events */ + writel(RP1_GPIO_CTRL_IRQRESET, + pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL); +} + +static void rp1_gpio_irq_enable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + rp1_gpio_irq_config(pin, true); +} + +static void rp1_gpio_irq_disable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + rp1_gpio_irq_config(pin, false); +} + +static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type) +{ + u32 irq_flags; + + switch (type) { + case IRQ_TYPE_NONE: + irq_flags = 0; + break; + case IRQ_TYPE_EDGE_RISING: + irq_flags = RP1_INT_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + irq_flags = RP1_INT_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING; + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_flags = RP1_INT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + irq_flags = RP1_INT_LEVEL_LOW; + break; + + default: + return -EINVAL; + } + + /* Clear the event enables */ + writel(RP1_INT_MASK << RP1_GPIO_EVENTS_SHIFT_RAW, + pin->gpio + RP1_CLR_OFFSET + RP1_GPIO_CTRL); + /* Clear any latched events */ + writel(RP1_GPIO_CTRL_IRQRESET, + pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL); + /* Enable the events that are needed */ + writel(irq_flags << RP1_GPIO_EVENTS_SHIFT_RAW, + pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL); + pin->irq_type = type; + + return 0; +} + +static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + unsigned gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + int bank = pin->bank; + unsigned long flags; + int ret; + + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); + + ret = rp1_irq_set_type(pin, type); + if (!ret) { + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(data, handle_edge_irq); + else + irq_set_handler_locked(data, handle_level_irq); + } + + raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); + + return ret; +} + +static void rp1_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + unsigned gpio = irqd_to_hwirq(data); + struct rp1_pin_info *pin = rp1_get_pin(chip, gpio); + + /* Clear any latched events */ + writel(RP1_GPIO_CTRL_IRQRESET, pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL); +} + +static int rp1_gpio_irq_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct rp1_pinctrl *pc = gpiochip_get_data(chip); + const struct rp1_iobank_desc *bank; + struct irq_data *parent_data = NULL; + int i; + + for (i = 0; i < 3; i++) { + bank = &rp1_iobanks[i]; + if (data->hwirq >= bank->min_gpio && + data->hwirq < bank->min_gpio + bank->num_gpios) { + parent_data = irq_get_irq_data(pc->irq[i]); + break; + } + } + + if (parent_data && parent_data->chip->irq_set_affinity) + return parent_data->chip->irq_set_affinity(parent_data, dest, force); + + return -EINVAL; +} + +static struct irq_chip rp1_gpio_irq_chip = { + .name = MODULE_NAME, + .irq_enable = rp1_gpio_irq_enable, + .irq_disable = rp1_gpio_irq_disable, + .irq_set_type = rp1_gpio_irq_set_type, + .irq_ack = rp1_gpio_irq_ack, + .irq_mask = rp1_gpio_irq_disable, + .irq_unmask = rp1_gpio_irq_enable, + .irq_set_affinity = rp1_gpio_irq_set_affinity, + .flags = IRQCHIP_IMMUTABLE, +}; + +static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(rp1_gpio_groups); +} + +static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return rp1_gpio_groups[selector]; +} + +static enum funcs rp1_get_fsel_func(unsigned pin, unsigned fsel) +{ + if (pin < RP1_NUM_GPIOS) { + if (fsel < RP1_FSEL_COUNT) + return rp1_gpio_pin_funcs[pin].funcs[fsel]; + else if (fsel == RP1_FSEL_NONE) + return func_none; + } + return func_invalid; +} + +static int rp1_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + *pins = &rp1_gpio_pins[selector].number; + *num_pins = 1; + + return 0; +} + +static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct gpio_chip *chip = &pc->gpio_chip; + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 fsel = rp1_get_fsel(pin); + enum funcs func = rp1_get_fsel_func(offset, fsel); + int value = rp1_get_value(pin); + int irq = irq_find_mapping(chip->irq.domain, offset); + + seq_printf(s, "function %s (%s) in %s; irq %d (%s)", + rp1_func_names[fsel], rp1_func_names[func], + value ? "hi" : "lo", + irq, irq_type_names[pin->irq_type]); +} + +static void rp1_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(maps[i].data.configs.configs); + + kfree(maps); +} + +static int rp1_pctl_legacy_map_func(struct rp1_pinctrl *pc, + struct device_node *np, u32 pin, u32 fnum, + struct pinctrl_map *maps, + unsigned int *num_maps) +{ + struct pinctrl_map *map = &maps[*num_maps]; + enum funcs func; + + if (fnum >= ARRAY_SIZE(legacy_fsel_map[0])) { + dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum); + return -EINVAL; + } + + if (pin < ARRAY_SIZE(legacy_fsel_map)) { + func = legacy_fsel_map[pin][fnum]; + } else if (fnum < 2) { + func = func_gpio; + } else { + dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n", + np, pin); + return -EINVAL; + } + + if (func == func_invalid) { + dev_err(pc->dev, "%pOF: brcm,function %d not supported on pin %d\n", + np, fnum, pin); + } + + map->type = PIN_MAP_TYPE_MUX_GROUP; + map->data.mux.group = rp1_gpio_groups[pin]; + map->data.mux.function = rp1_func_names[func]; + (*num_maps)++; + + return 0; +} + +static int rp1_pctl_legacy_map_pull(struct rp1_pinctrl *pc, + struct device_node *np, u32 pin, u32 pull, + struct pinctrl_map *maps, + unsigned int *num_maps) +{ + struct pinctrl_map *map = &maps[*num_maps]; + enum pin_config_param param; + unsigned long *configs; + + switch (pull) { + case RP1_PUD_OFF: + param = PIN_CONFIG_BIAS_DISABLE; + break; + case RP1_PUD_DOWN: + param = PIN_CONFIG_BIAS_PULL_DOWN; + break; + case RP1_PUD_UP: + param = PIN_CONFIG_BIAS_PULL_UP; + break; + default: + dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull); + return -EINVAL; + } + + configs = kzalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + return -ENOMEM; + + configs[0] = pinconf_to_config_packed(param, 0); + map->type = PIN_MAP_TYPE_CONFIGS_PIN; + map->data.configs.group_or_pin = rp1_gpio_pins[pin].name; + map->data.configs.configs = configs; + map->data.configs.num_configs = 1; + (*num_maps)++; + + return 0; +} + +static int rp1_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct property *pins, *funcs, *pulls; + int num_pins, num_funcs, num_pulls, maps_per_pin; + struct pinctrl_map *maps; + unsigned long *configs = NULL; + const char *function = NULL; + unsigned int reserved_maps; + int num_configs = 0; + int i, err; + u32 pin, func, pull; + + /* Check for legacy pin declaration */ + pins = of_find_property(np, "brcm,pins", NULL); + + if (!pins) /* Assume generic bindings in this node */ + return pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps); + + funcs = of_find_property(np, "brcm,function", NULL); + if (!funcs) + of_property_read_string(np, "function", &function); + + pulls = of_find_property(np, "brcm,pull", NULL); + if (!pulls) + pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); + + if (!function && !funcs && !num_configs && !pulls) { + dev_err(pc->dev, + "%pOF: no function, brcm,function, brcm,pull, etc.\n", + np); + return -EINVAL; + } + + num_pins = pins->length / 4; + num_funcs = funcs ? (funcs->length / 4) : 0; + num_pulls = pulls ? (pulls->length / 4) : 0; + + if (num_funcs > 1 && num_funcs != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,function must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + if (num_pulls > 1 && num_pulls != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,pull must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + maps_per_pin = 0; + if (function || num_funcs) + maps_per_pin++; + if (num_configs || num_pulls) + maps_per_pin++; + reserved_maps = num_pins * maps_per_pin; + maps = kcalloc(reserved_maps, sizeof(*maps), GFP_KERNEL); + if (!maps) + return -ENOMEM; + + *num_maps = 0; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(np, "brcm,pins", i, &pin); + if (err) + goto out; + if (num_funcs) { + err = of_property_read_u32_index(np, "brcm,function", + (num_funcs > 1) ? i : 0, + &func); + if (err) + goto out; + err = rp1_pctl_legacy_map_func(pc, np, pin, func, + maps, num_maps); + } else if (function) { + err = pinctrl_utils_add_map_mux(pctldev, &maps, + &reserved_maps, num_maps, + rp1_gpio_groups[pin], + function); + } + + if (err) + goto out; + + if (num_pulls) { + err = of_property_read_u32_index(np, "brcm,pull", + (num_pulls > 1) ? i : 0, + &pull); + if (err) + goto out; + err = rp1_pctl_legacy_map_pull(pc, np, pin, pull, + maps, num_maps); + } else if (num_configs) { + err = pinctrl_utils_add_map_configs(pctldev, &maps, + &reserved_maps, num_maps, + rp1_gpio_groups[pin], + configs, num_configs, + PIN_MAP_TYPE_CONFIGS_PIN); + } + + if (err) + goto out; + } + + *map = maps; + + return 0; + +out: + rp1_pctl_dt_free_map(pctldev, maps, reserved_maps); + return err; +} + +static const struct pinctrl_ops rp1_pctl_ops = { + .get_groups_count = rp1_pctl_get_groups_count, + .get_group_name = rp1_pctl_get_group_name, + .get_group_pins = rp1_pctl_get_group_pins, + .pin_dbg_show = rp1_pctl_pin_dbg_show, + .dt_node_to_map = rp1_pctl_dt_node_to_map, + .dt_free_map = rp1_pctl_dt_free_map, +}; + +static int rp1_pmx_free(struct pinctrl_dev *pctldev, unsigned offset) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 fsel = rp1_get_fsel(pin); + + /* Return all pins to GPIO_IN, unless persist_gpio_outputs is set */ + if (persist_gpio_outputs && fsel == RP1_FSEL_GPIO) + return 0; + + rp1_set_dir(pin, RP1_DIR_INPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static int rp1_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ + return func_count; +} + +static const char *rp1_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return (selector < func_count) ? rp1_func_names[selector] : NULL; +} + +static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + /* every pin can do every function */ + *groups = rp1_gpio_groups; + *num_groups = ARRAY_SIZE(rp1_gpio_groups); + + return 0; +} + +static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned func_selector, + unsigned group_selector) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, group_selector); + const u8 *pin_funcs; + int fsel; + + /* func_selector is an enum funcs, so needs translation */ + + if (func_selector >= RP1_FSEL_COUNT) { + /* Convert to an fsel number */ + pin_funcs = rp1_gpio_pin_funcs[pin->num].funcs; + for (fsel = 0; fsel < RP1_FSEL_COUNT; fsel++) { + if (pin_funcs[fsel] == func_selector) + break; + } + } else { + fsel = (int)func_selector; + } + + if (fsel >= RP1_FSEL_COUNT && fsel != RP1_FSEL_NONE) + return -EINVAL; + + rp1_set_fsel(pin, fsel); + + return 0; +} + +static void rp1_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + (void)rp1_pmx_free(pctldev, offset); +} + +static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, + bool input) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + + rp1_set_dir(pin, input); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + + return 0; +} + +static const struct pinmux_ops rp1_pmx_ops = { + .free = rp1_pmx_free, + .get_functions_count = rp1_pmx_get_functions_count, + .get_function_name = rp1_pmx_get_function_name, + .get_function_groups = rp1_pmx_get_function_groups, + .set_mux = rp1_pmx_set, + .gpio_disable_free = rp1_pmx_gpio_disable_free, + .gpio_set_direction = rp1_pmx_gpio_set_direction, +}; + +static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg) +{ + u32 padctrl = readl(pin->pad); + + FLD_SET(padctrl, RP1_PAD_PULL, arg & 0x3); + + writel(padctrl, pin->pad); +} + +/* Generic pinconf methods */ + +static int rp1_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *configs, unsigned int num_configs) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + u32 param, arg; + int i; + + if (!pin) + return -EINVAL; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + rp1_pull_config_set(pin, RP1_PUD_OFF); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + rp1_pull_config_set(pin, RP1_PUD_DOWN); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + rp1_pull_config_set(pin, RP1_PUD_UP); + break; + + case PIN_CONFIG_INPUT_ENABLE: + rp1_input_enable(pin, arg); + break; + + case PIN_CONFIG_OUTPUT_ENABLE: + rp1_output_enable(pin, arg); + break; + + case PIN_CONFIG_OUTPUT: + rp1_set_value(pin, arg); + rp1_set_dir(pin, RP1_DIR_OUTPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + rp1_pad_update(pin, RP1_PAD_SCHMITT_MASK, + arg ? RP1_PAD_SCHMITT_MASK : 0); + break; + + case PIN_CONFIG_SLEW_RATE: + rp1_pad_update(pin, RP1_PAD_SLEWFAST_MASK, + arg ? RP1_PAD_SLEWFAST_MASK : 0); + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + switch (arg) { + case 2: + arg = RP1_PAD_DRIVE_2MA; + break; + case 4: + arg = RP1_PAD_DRIVE_4MA; + break; + case 8: + arg = RP1_PAD_DRIVE_8MA; + break; + case 12: + arg = RP1_PAD_DRIVE_12MA; + break; + default: + return -ENOTSUPP; + } + rp1_pad_update(pin, RP1_PAD_DRIVE_MASK, arg); + break; + + default: + return -ENOTSUPP; + + } /* switch param type */ + } /* for each config */ + + return 0; +} + +static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned offset, + unsigned long *config) +{ + struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 padctrl; + u32 arg; + + if (!pin) + return -EINVAL; + + padctrl = readl(pin->pad); + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + arg = !!(padctrl & RP1_PAD_IN_ENABLE_MASK); + break; + case PIN_CONFIG_OUTPUT_ENABLE: + arg = !(padctrl & RP1_PAD_OUT_DISABLE_MASK); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + arg = !!(padctrl & RP1_PAD_SCHMITT_MASK); + break; + case PIN_CONFIG_SLEW_RATE: + arg = !!(padctrl & RP1_PAD_SLEWFAST_MASK); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + switch (padctrl & RP1_PAD_DRIVE_MASK) { + case RP1_PAD_DRIVE_2MA: + arg = 2; + break; + case RP1_PAD_DRIVE_4MA: + arg = 4; + break; + case RP1_PAD_DRIVE_8MA: + arg = 8; + break; + case RP1_PAD_DRIVE_12MA: + arg = 12; + break; + } + break; + case PIN_CONFIG_BIAS_DISABLE: + arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_OFF << RP1_PAD_PULL_LSB)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_DOWN << RP1_PAD_PULL_LSB)); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_UP << RP1_PAD_PULL_LSB)); + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static const struct pinconf_ops rp1_pinconf_ops = { + .is_generic = true, + .pin_config_get = rp1_pinconf_get, + .pin_config_set = rp1_pinconf_set, +}; + +static struct pinctrl_desc rp1_pinctrl_desc = { + .name = MODULE_NAME, + .pins = rp1_gpio_pins, + .npins = ARRAY_SIZE(rp1_gpio_pins), + .pctlops = &rp1_pctl_ops, + .pmxops = &rp1_pmx_ops, + .confops = &rp1_pinconf_ops, + .owner = THIS_MODULE, +}; + +static struct pinctrl_gpio_range rp1_pinctrl_gpio_range = { + .name = MODULE_NAME, + .npins = RP1_NUM_GPIOS, +}; + +static const struct of_device_id rp1_pinctrl_match[] = { + { + .compatible = "raspberrypi,rp1-gpio", + .data = &rp1_pinconf_ops, + }, + {} +}; + +static inline void __iomem *devm_auto_iomap(struct platform_device *pdev, + unsigned int index) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + if (np) + return devm_of_iomap(dev, np, (int)index, NULL); + else + return devm_platform_ioremap_resource(pdev, index); +} + +static int rp1_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct rp1_pinctrl *pc; + struct gpio_irq_chip *girq; + int err, i; + + BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_pins) != RP1_NUM_GPIOS); + BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_groups) != RP1_NUM_GPIOS); + + pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return -ENOMEM; + + platform_set_drvdata(pdev, pc); + pc->dev = dev; + + pc->gpio_base = devm_auto_iomap(pdev, 0); + if (IS_ERR(pc->gpio_base)) { + dev_err(dev, "could not get GPIO IO memory\n"); + return PTR_ERR(pc->gpio_base); + } + + pc->rio_base = devm_auto_iomap(pdev, 1); + if (IS_ERR(pc->rio_base)) { + dev_err(dev, "could not get RIO IO memory\n"); + return PTR_ERR(pc->rio_base); + } + + pc->pads_base = devm_auto_iomap(pdev, 2); + if (IS_ERR(pc->pads_base)) { + dev_err(dev, "could not get PADS IO memory\n"); + return PTR_ERR(pc->pads_base); + } + + pc->gpio_chip = rp1_gpio_chip; + pc->gpio_chip.parent = dev; + + for (i = 0; i < RP1_NUM_BANKS; i++) { + const struct rp1_iobank_desc *bank = &rp1_iobanks[i]; + int j; + + for (j = 0; j < bank->num_gpios; j++) { + struct rp1_pin_info *pin = + &pc->pins[bank->min_gpio + j]; + + pin->num = bank->min_gpio + j; + pin->bank = i; + pin->offset = j; + + pin->gpio = pc->gpio_base + bank->gpio_offset + + j * sizeof(u32) * 2; + pin->inte = pc->gpio_base + bank->inte_offset; + pin->ints = pc->gpio_base + bank->ints_offset; + pin->rio = pc->rio_base + bank->rio_offset; + pin->pad = pc->pads_base + bank->pads_offset + + j * sizeof(u32); + } + + raw_spin_lock_init(&pc->irq_lock[i]); + } + + pc->pctl_dev = devm_pinctrl_register(dev, &rp1_pinctrl_desc, pc); + if (IS_ERR(pc->pctl_dev)) + return PTR_ERR(pc->pctl_dev); + + girq = &pc->gpio_chip.irq; + girq->chip = &rp1_gpio_irq_chip; + girq->parent_handler = rp1_gpio_irq_handler; + girq->num_parents = RP1_NUM_BANKS; + girq->parents = pc->irq; + + /* + * Use the same handler for all groups: this is necessary + * since we use one gpiochip to cover all lines - the + * irq handler then needs to figure out which group and + * bank that was firing the IRQ and look up the per-group + * and bank data. + */ + for (i = 0; i < RP1_NUM_BANKS; i++) { + pc->irq[i] = irq_of_parse_and_map(np, i); + if (!pc->irq[i]) { + girq->num_parents = i; + break; + } + } + + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + + err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc); + if (err) { + dev_err(dev, "could not add GPIO chip\n"); + return err; + } + + pc->gpio_range = rp1_pinctrl_gpio_range; + pc->gpio_range.base = pc->gpio_chip.base; + pc->gpio_range.gc = &pc->gpio_chip; + pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range); + + return 0; +} + +static struct platform_driver rp1_pinctrl_driver = { + .probe = rp1_pinctrl_probe, + .driver = { + .name = MODULE_NAME, + .of_match_table = rp1_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(rp1_pinctrl_driver); diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 24f92a6e882af..a6a705ec30c7f 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -17,6 +17,7 @@ config RASPBERRYPI_POWER bool "Raspberry Pi power domain driver" depends on ARCH_BCM2835 || (COMPILE_TEST && OF) depends on RASPBERRYPI_FIRMWARE=y + depends on PM select PM_GENERIC_DOMAINS if PM help This enables support for the RPi power domains which can be enabled diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c index 5bcd047768b60..addf5bb882e7a 100644 --- a/drivers/soc/bcm/bcm2835-power.c +++ b/drivers/soc/bcm/bcm2835-power.c @@ -79,6 +79,7 @@ #define PM_IMAGE 0x108 #define PM_GRAFX 0x10c #define PM_PROC 0x110 +#define PM_GRAFX_2712 0x304 #define PM_ENAB BIT(12) #define PM_ISPRSTN BIT(8) #define PM_H264RSTN BIT(7) @@ -175,7 +176,7 @@ static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable } writel(PM_PASSWORD | val, base + reg); - while (readl(base + reg) & ASB_ACK) { + while (!!(readl(base + reg) & ASB_ACK) == enable) { cpu_relax(); if (ktime_get_ns() - start >= 1000) return -ETIMEDOUT; @@ -381,6 +382,9 @@ static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) return bcm2835_power_power_on(pd, PM_GRAFX); case BCM2835_POWER_DOMAIN_GRAFX_V3D: + if (!power->asb) + return bcm2835_asb_power_on(pd, PM_GRAFX_2712, + 0, 0, PM_V3DRSTN); return bcm2835_asb_power_on(pd, PM_GRAFX, ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, PM_V3DRSTN); @@ -447,6 +451,9 @@ static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) return bcm2835_power_power_off(pd, PM_GRAFX); case BCM2835_POWER_DOMAIN_GRAFX_V3D: + if (!power->asb) + return bcm2835_asb_power_off(pd, PM_GRAFX_2712, + 0, 0, PM_V3DRSTN); return bcm2835_asb_power_off(pd, PM_GRAFX, ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, PM_V3DRSTN); @@ -642,19 +649,21 @@ static int bcm2835_power_probe(struct platform_device *pdev) power->asb = pm->asb; power->rpivid_asb = pm->rpivid_asb; - id = readl(power->asb + ASB_AXI_BRDG_ID); - if (id != BCM2835_BRDG_ID /* "BRDG" */) { - dev_err(dev, "ASB register ID returned 0x%08x\n", id); - return -ENODEV; - } - - if (power->rpivid_asb) { - id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); + if (power->asb) { + id = readl(power->asb + ASB_AXI_BRDG_ID); if (id != BCM2835_BRDG_ID /* "BRDG" */) { - dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", - id); + dev_err(dev, "ASB register ID returned 0x%08x\n", id); return -ENODEV; } + + if (power->rpivid_asb) { + id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); + if (id != BCM2835_BRDG_ID /* "BRDG" */) { + dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", + id); + return -ENODEV; + } + } } power->pd_xlate.domains = devm_kcalloc(dev, @@ -717,4 +726,3 @@ module_platform_driver(bcm2835_power_driver); MODULE_AUTHOR("Eric Anholt "); MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset"); -MODULE_LICENSE("GPL"); diff --git a/drivers/soc/bcm/brcmstb/Kconfig b/drivers/soc/bcm/brcmstb/Kconfig index 38e476905d96a..c68d0e5267c42 100644 --- a/drivers/soc/bcm/brcmstb/Kconfig +++ b/drivers/soc/bcm/brcmstb/Kconfig @@ -4,8 +4,6 @@ if SOC_BRCMSTB config BRCMSTB_PM bool "Support suspend/resume for STB platforms" default y - depends on PM - depends on ARCH_BRCMSTB || BMIPS_GENERIC - select ARM_CPU_SUSPEND if ARM + depends on PM && BMIPS_GENERIC endif # SOC_BRCMSTB diff --git a/include/dt-bindings/clock/rp1.h b/include/dt-bindings/clock/rp1.h new file mode 100644 index 0000000000000..27cbb6e130da6 --- /dev/null +++ b/include/dt-bindings/clock/rp1.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Raspberry Pi Ltd. + */ + +#define RP1_PLL_SYS_CORE 0 +#define RP1_PLL_AUDIO_CORE 1 +#define RP1_PLL_VIDEO_CORE 2 + +#define RP1_PLL_SYS 3 +#define RP1_PLL_AUDIO 4 +#define RP1_PLL_VIDEO 5 + +#define RP1_PLL_SYS_PRI_PH 6 +#define RP1_PLL_SYS_SEC_PH 7 +#define RP1_PLL_AUDIO_PRI_PH 8 + +#define RP1_PLL_SYS_SEC 9 +#define RP1_PLL_AUDIO_SEC 10 +#define RP1_PLL_VIDEO_SEC 11 + +#define RP1_CLK_SYS 12 +#define RP1_CLK_SLOW_SYS 13 +#define RP1_CLK_DMA 14 +#define RP1_CLK_UART 15 +#define RP1_CLK_ETH 16 +#define RP1_CLK_PWM0 17 +#define RP1_CLK_PWM1 18 +#define RP1_CLK_AUDIO_IN 19 +#define RP1_CLK_AUDIO_OUT 20 +#define RP1_CLK_I2S 21 +#define RP1_CLK_MIPI0_CFG 22 +#define RP1_CLK_MIPI1_CFG 23 +#define RP1_CLK_PCIE_AUX 24 +#define RP1_CLK_USBH0_MICROFRAME 25 +#define RP1_CLK_USBH1_MICROFRAME 26 +#define RP1_CLK_USBH0_SUSPEND 27 +#define RP1_CLK_USBH1_SUSPEND 28 +#define RP1_CLK_ETH_TSU 29 +#define RP1_CLK_ADC 30 +#define RP1_CLK_SDIO_TIMER 31 +#define RP1_CLK_SDIO_ALT_SRC 32 +#define RP1_CLK_GP0 33 +#define RP1_CLK_GP1 34 +#define RP1_CLK_GP2 35 +#define RP1_CLK_GP3 36 +#define RP1_CLK_GP4 37 +#define RP1_CLK_GP5 38 +#define RP1_CLK_VEC 39 +#define RP1_CLK_DPI 40 +#define RP1_CLK_MIPI0_DPI 41 +#define RP1_CLK_MIPI1_DPI 42 + +/* Extra PLL output channels - RP1B0 only */ +#define RP1_PLL_VIDEO_PRI_PH 43 +#define RP1_PLL_AUDIO_TERN 44 diff --git a/include/dt-bindings/mfd/rp1.h b/include/dt-bindings/mfd/rp1.h new file mode 100644 index 0000000000000..80bbfd61b2700 --- /dev/null +++ b/include/dt-bindings/mfd/rp1.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This header provides constants for the PY MFD. + */ + +#ifndef _RP1_H +#define _RP1_H + +/* Address map */ +#define RP1_SYSINFO_BASE 0x000000 +#define RP1_TBMAN_BASE 0x004000 +#define RP1_SYSCFG_BASE 0x008000 +#define RP1_OTP_BASE 0x00c000 +#define RP1_POWER_BASE 0x010000 +#define RP1_RESETS_BASE 0x014000 +#define RP1_CLOCKS_BANK_DEFAULT_BASE 0x018000 +#define RP1_CLOCKS_BANK_VIDEO_BASE 0x01c000 +#define RP1_PLL_SYS_BASE 0x020000 +#define RP1_PLL_AUDIO_BASE 0x024000 +#define RP1_PLL_VIDEO_BASE 0x028000 +#define RP1_UART0_BASE 0x030000 +#define RP1_UART1_BASE 0x034000 +#define RP1_UART2_BASE 0x038000 +#define RP1_UART3_BASE 0x03c000 +#define RP1_UART4_BASE 0x040000 +#define RP1_UART5_BASE 0x044000 +#define RP1_SPI8_BASE 0x04c000 +#define RP1_SPI0_BASE 0x050000 +#define RP1_SPI1_BASE 0x054000 +#define RP1_SPI2_BASE 0x058000 +#define RP1_SPI3_BASE 0x05c000 +#define RP1_SPI4_BASE 0x060000 +#define RP1_SPI5_BASE 0x064000 +#define RP1_SPI6_BASE 0x068000 +#define RP1_SPI7_BASE 0x06c000 +#define RP1_I2C0_BASE 0x070000 +#define RP1_I2C1_BASE 0x074000 +#define RP1_I2C2_BASE 0x078000 +#define RP1_I2C3_BASE 0x07c000 +#define RP1_I2C4_BASE 0x080000 +#define RP1_I2C5_BASE 0x084000 +#define RP1_I2C6_BASE 0x088000 +#define RP1_AUDIO_IN_BASE 0x090000 +#define RP1_AUDIO_OUT_BASE 0x094000 +#define RP1_PWM0_BASE 0x098000 +#define RP1_PWM1_BASE 0x09c000 +#define RP1_I2S0_BASE 0x0a0000 +#define RP1_I2S1_BASE 0x0a4000 +#define RP1_I2S2_BASE 0x0a8000 +#define RP1_TIMER_BASE 0x0ac000 +#define RP1_SDIO0_APBS_BASE 0x0b0000 +#define RP1_SDIO1_APBS_BASE 0x0b4000 +#define RP1_BUSFABRIC_MONITOR_BASE 0x0c0000 +#define RP1_BUSFABRIC_AXISHIM_BASE 0x0c4000 +#define RP1_ADC_BASE 0x0c8000 +#define RP1_IO_BANK0_BASE 0x0d0000 +#define RP1_IO_BANK1_BASE 0x0d4000 +#define RP1_IO_BANK2_BASE 0x0d8000 +#define RP1_SYS_RIO0_BASE 0x0e0000 +#define RP1_SYS_RIO1_BASE 0x0e4000 +#define RP1_SYS_RIO2_BASE 0x0e8000 +#define RP1_PADS_BANK0_BASE 0x0f0000 +#define RP1_PADS_BANK1_BASE 0x0f4000 +#define RP1_PADS_BANK2_BASE 0x0f8000 +#define RP1_PADS_ETH_BASE 0x0fc000 +#define RP1_ETH_IP_BASE 0x100000 +#define RP1_ETH_CFG_BASE 0x104000 +#define RP1_PCIE_APBS_BASE 0x108000 +#define RP1_MIPI0_CSIDMA_BASE 0x110000 +#define RP1_MIPI0_CSIHOST_BASE 0x114000 +#define RP1_MIPI0_DSIDMA_BASE 0x118000 +#define RP1_MIPI0_DSIHOST_BASE 0x11c000 +#define RP1_MIPI0_MIPICFG_BASE 0x120000 +#define RP1_MIPI0_ISP_BASE 0x124000 +#define RP1_MIPI1_CSIDMA_BASE 0x128000 +#define RP1_MIPI1_CSIHOST_BASE 0x12c000 +#define RP1_MIPI1_DSIDMA_BASE 0x130000 +#define RP1_MIPI1_DSIHOST_BASE 0x134000 +#define RP1_MIPI1_MIPICFG_BASE 0x138000 +#define RP1_MIPI1_ISP_BASE 0x13c000 +#define RP1_VIDEO_OUT_CFG_BASE 0x140000 +#define RP1_VIDEO_OUT_VEC_BASE 0x144000 +#define RP1_VIDEO_OUT_DPI_BASE 0x148000 +#define RP1_XOSC_BASE 0x150000 +#define RP1_WATCHDOG_BASE 0x154000 +#define RP1_DMA_TICK_BASE 0x158000 +#define RP1_SDIO_CLOCKS_BASE 0x15c000 +#define RP1_USBHOST0_APBS_BASE 0x160000 +#define RP1_USBHOST1_APBS_BASE 0x164000 +#define RP1_ROSC0_BASE 0x168000 +#define RP1_ROSC1_BASE 0x16c000 +#define RP1_VBUSCTRL_BASE 0x170000 +#define RP1_TICKS_BASE 0x174000 +#define RP1_PIO_APBS_BASE 0x178000 +#define RP1_SDIO0_AHBLS_BASE 0x180000 +#define RP1_SDIO1_AHBLS_BASE 0x184000 +#define RP1_DMA_BASE 0x188000 +#define RP1_RAM_BASE 0x1c0000 +#define RP1_RAM_SIZE 0x020000 +#define RP1_USBHOST0_AXIS_BASE 0x200000 +#define RP1_USBHOST1_AXIS_BASE 0x300000 +#define RP1_EXAC_BASE 0x400000 + +/* Interrupts */ + +#define RP1_INT_IO_BANK0 0 +#define RP1_INT_IO_BANK1 1 +#define RP1_INT_IO_BANK2 2 +#define RP1_INT_AUDIO_IN 3 +#define RP1_INT_AUDIO_OUT 4 +#define RP1_INT_PWM0 5 +#define RP1_INT_ETH 6 +#define RP1_INT_I2C0 7 +#define RP1_INT_I2C1 8 +#define RP1_INT_I2C2 9 +#define RP1_INT_I2C3 10 +#define RP1_INT_I2C4 11 +#define RP1_INT_I2C5 12 +#define RP1_INT_I2C6 13 +#define RP1_INT_I2S0 14 +#define RP1_INT_I2S1 15 +#define RP1_INT_I2S2 16 +#define RP1_INT_SDIO0 17 +#define RP1_INT_SDIO1 18 +#define RP1_INT_SPI0 19 +#define RP1_INT_SPI1 20 +#define RP1_INT_SPI2 21 +#define RP1_INT_SPI3 22 +#define RP1_INT_SPI4 23 +#define RP1_INT_SPI5 24 +#define RP1_INT_UART0 25 +#define RP1_INT_TIMER_0 26 +#define RP1_INT_TIMER_1 27 +#define RP1_INT_TIMER_2 28 +#define RP1_INT_TIMER_3 29 +#define RP1_INT_USBHOST0 30 +#define RP1_INT_USBHOST0_0 31 +#define RP1_INT_USBHOST0_1 32 +#define RP1_INT_USBHOST0_2 33 +#define RP1_INT_USBHOST0_3 34 +#define RP1_INT_USBHOST1 35 +#define RP1_INT_USBHOST1_0 36 +#define RP1_INT_USBHOST1_1 37 +#define RP1_INT_USBHOST1_2 38 +#define RP1_INT_USBHOST1_3 39 +#define RP1_INT_DMA 40 +#define RP1_INT_PWM1 41 +#define RP1_INT_UART1 42 +#define RP1_INT_UART2 43 +#define RP1_INT_UART3 44 +#define RP1_INT_UART4 45 +#define RP1_INT_UART5 46 +#define RP1_INT_MIPI0 47 +#define RP1_INT_MIPI1 48 +#define RP1_INT_VIDEO_OUT 49 +#define RP1_INT_PIO_0 50 +#define RP1_INT_PIO_1 51 +#define RP1_INT_ADC_FIFO 52 +#define RP1_INT_PCIE_OUT 53 +#define RP1_INT_SPI6 54 +#define RP1_INT_SPI7 55 +#define RP1_INT_SPI8 56 +#define RP1_INT_SYSCFG 58 +#define RP1_INT_CLOCKS_DEFAULT 59 +#define RP1_INT_VBUSCTRL 60 +#define RP1_INT_PROC_MISC 57 +#define RP1_INT_END 61 + +/* DMA peripherals (for pacing) */ +#define RP1_DMA_I2C0_RX 0x0 +#define RP1_DMA_I2C0_TX 0x1 +#define RP1_DMA_I2C1_RX 0x2 +#define RP1_DMA_I2C1_TX 0x3 +#define RP1_DMA_I2C2_RX 0x4 +#define RP1_DMA_I2C2_TX 0x5 +#define RP1_DMA_I2C3_RX 0x6 +#define RP1_DMA_I2C3_TX 0x7 +#define RP1_DMA_I2C4_RX 0x8 +#define RP1_DMA_I2C4_TX 0x9 +#define RP1_DMA_I2C5_RX 0xa +#define RP1_DMA_I2C5_TX 0xb +#define RP1_DMA_SPI0_RX 0xc +#define RP1_DMA_SPI0_TX 0xd +#define RP1_DMA_SPI1_RX 0xe +#define RP1_DMA_SPI1_TX 0xf +#define RP1_DMA_SPI2_RX 0x10 +#define RP1_DMA_SPI2_TX 0x11 +#define RP1_DMA_SPI3_RX 0x12 +#define RP1_DMA_SPI3_TX 0x13 +#define RP1_DMA_SPI4_RX 0x14 +#define RP1_DMA_SPI4_TX 0x15 +#define RP1_DMA_SPI5_RX 0x16 +#define RP1_DMA_SPI5_TX 0x17 +#define RP1_DMA_PWM0 0x18 +#define RP1_DMA_UART0_RX 0x19 +#define RP1_DMA_UART0_TX 0x1a +#define RP1_DMA_AUDIO_IN_CH0 0x1b +#define RP1_DMA_AUDIO_IN_CH1 0x1c +#define RP1_DMA_AUDIO_OUT 0x1d +#define RP1_DMA_PWM1 0x1e +#define RP1_DMA_I2S0_RX 0x1f +#define RP1_DMA_I2S0_TX 0x20 +#define RP1_DMA_I2S1_RX 0x21 +#define RP1_DMA_I2S1_TX 0x22 +#define RP1_DMA_I2S2_RX 0x23 +#define RP1_DMA_I2S2_TX 0x24 +#define RP1_DMA_UART1_RX 0x25 +#define RP1_DMA_UART1_TX 0x26 +#define RP1_DMA_UART2_RX 0x27 +#define RP1_DMA_UART2_TX 0x28 +#define RP1_DMA_UART3_RX 0x29 +#define RP1_DMA_UART3_TX 0x2a +#define RP1_DMA_UART4_RX 0x2b +#define RP1_DMA_UART4_TX 0x2c +#define RP1_DMA_UART5_RX 0x2d +#define RP1_DMA_UART5_TX 0x2e +#define RP1_DMA_ADC 0x2f +#define RP1_DMA_DMA_TICK_TICK0 0x30 +#define RP1_DMA_DMA_TICK_TICK1 0x31 +#define RP1_DMA_SPI6_RX 0x32 +#define RP1_DMA_SPI6_TX 0x33 +#define RP1_DMA_SPI7_RX 0x34 +#define RP1_DMA_SPI7_TX 0x35 +#define RP1_DMA_SPI8_RX 0x36 +#define RP1_DMA_SPI8_TX 0x37 +#define RP1_DMA_PIO_CH0_TX 0x38 +#define RP1_DMA_PIO_CH0_RX 0x39 +#define RP1_DMA_PIO_CH1_TX 0x3a +#define RP1_DMA_PIO_CH1_RX 0x3b +#define RP1_DMA_PIO_CH2_TX 0x3c +#define RP1_DMA_PIO_CH2_RX 0x3d +#define RP1_DMA_PIO_CH3_TX 0x3e +#define RP1_DMA_PIO_CH3_RX 0x3f + +#endif diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 9e77165f3ef6d..c5e6adc35cc9d 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -23,6 +23,7 @@ #define PHY_ID_BCM5411 0x00206070 #define PHY_ID_BCM5421 0x002060e0 #define PHY_ID_BCM54210E 0x600d84a0 +#define PHY_ID_BCM54213PE 0x600d84a2 #define PHY_ID_BCM5464 0x002060b0 #define PHY_ID_BCM5461 0x002060c0 #define PHY_ID_BCM54612E 0x03625e60 @@ -44,6 +45,7 @@ #define PHY_ID_BCM7366 0x600d8490 #define PHY_ID_BCM7346 0x600d8650 #define PHY_ID_BCM7362 0x600d84b0 +#define PHY_ID_BCM74165 0x359052c0 #define PHY_ID_BCM7425 0x600d86b0 #define PHY_ID_BCM7429 0x600d8730 #define PHY_ID_BCM7435 0x600d8750 @@ -89,6 +91,7 @@ #define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */ #define MII_BCM54XX_EXP_SEL_TOP 0x0d00 /* TOP_MISC expansion register select */ #define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */ +#define MII_BCM54XX_EXP_SEL_WOL 0x0e00 /* Wake-on-LAN expansion select register */ #define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */ #define MII_BCM54XX_EXP_SEL_ETC 0x0d00 /* Expansion register spare + 2k mem */ @@ -160,6 +163,7 @@ #define BCM_LED_SRC_OPENSHORT 0xb #define BCM_LED_SRC_OFF 0xe /* Tied high */ #define BCM_LED_SRC_ON 0xf /* Tied low */ +#define BCM_LED_SRC_MASK GENMASK(3, 0) /* * Broadcom Multicolor LED configurations (expansion register 4) @@ -205,11 +209,13 @@ #define BCM_NO_ANEG_APD_EN 0x0060 /* bits 5 & 6 */ #define BCM_APD_SINGLELP_EN 0x0100 /* Bit 8 */ -#define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */ +#define BCM54XX_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */ /* LED3 / ~LINKSPD[2] selector */ -#define BCM5482_SHD_LEDS1_LED3(src) ((src & 0xf) << 4) +#define BCM54XX_SHD_LEDS_SHIFT(led) (4 * (led)) +#define BCM54XX_SHD_LEDS1_LED3(src) ((src & 0xf) << 4) /* LED1 / ~LINKSPD[1] selector */ -#define BCM5482_SHD_LEDS1_LED1(src) ((src & 0xf) << 0) +#define BCM54XX_SHD_LEDS1_LED1(src) ((src & 0xf) << 0) +#define BCM54XX_SHD_LEDS2 0x0e /* 01110: LED Selector 2 */ #define BCM54XX_SHD_RGMII_MODE 0x0b /* 01011: RGMII Mode Selector */ #define BCM5482_SHD_SSD 0x14 /* 10100: Secondary SerDes control */ #define BCM5482_SHD_SSD_LEDM 0x0008 /* SSD LED Mode enable */ @@ -253,6 +259,9 @@ #define BCM54XX_TOP_MISC_IDDQ_SD (1 << 2) #define BCM54XX_TOP_MISC_IDDQ_SR (1 << 3) +#define BCM54XX_TOP_MISC_LED_CTL (MII_BCM54XX_EXP_SEL_TOP + 0x0C) +#define BCM54XX_LED4_SEL_INTR BIT(1) + /* * BCM5482: Secondary SerDes registers */ @@ -272,6 +281,57 @@ #define BCM54612E_EXP_SPARE0 (MII_BCM54XX_EXP_SEL_ETC + 0x34) #define BCM54612E_LED4_CLK125OUT_EN (1 << 1) + +/* Wake-on-LAN registers */ +#define BCM54XX_WOL_MAIN_CTL (MII_BCM54XX_EXP_SEL_WOL + 0x80) +#define BCM54XX_WOL_EN BIT(0) +#define BCM54XX_WOL_MODE_SINGLE_MPD 0 +#define BCM54XX_WOL_MODE_SINGLE_MPDSEC 1 +#define BCM54XX_WOL_MODE_DUAL 2 +#define BCM54XX_WOL_MODE_SHIFT 1 +#define BCM54XX_WOL_MODE_MASK 0x3 +#define BCM54XX_WOL_MP_MSB_FF_EN BIT(3) +#define BCM54XX_WOL_SECKEY_OPT_4B 0 +#define BCM54XX_WOL_SECKEY_OPT_6B 1 +#define BCM54XX_WOL_SECKEY_OPT_8B 2 +#define BCM54XX_WOL_SECKEY_OPT_SHIFT 4 +#define BCM54XX_WOL_SECKEY_OPT_MASK 0x3 +#define BCM54XX_WOL_L2_TYPE_CHK BIT(6) +#define BCM54XX_WOL_L4IPV4UDP_CHK BIT(7) +#define BCM54XX_WOL_L4IPV6UDP_CHK BIT(8) +#define BCM54XX_WOL_UDPPORT_CHK BIT(9) +#define BCM54XX_WOL_CRC_CHK BIT(10) +#define BCM54XX_WOL_SECKEY_MODE BIT(11) +#define BCM54XX_WOL_RST BIT(12) +#define BCM54XX_WOL_DIR_PKT_EN BIT(13) +#define BCM54XX_WOL_MASK_MODE_DA_FF 0 +#define BCM54XX_WOL_MASK_MODE_DA_MPD 1 +#define BCM54XX_WOL_MASK_MODE_DA_ONLY 2 +#define BCM54XX_WOL_MASK_MODE_MPD 3 +#define BCM54XX_WOL_MASK_MODE_SHIFT 14 +#define BCM54XX_WOL_MASK_MODE_MASK 0x3 + +#define BCM54XX_WOL_INNER_PROTO (MII_BCM54XX_EXP_SEL_WOL + 0x81) +#define BCM54XX_WOL_OUTER_PROTO (MII_BCM54XX_EXP_SEL_WOL + 0x82) +#define BCM54XX_WOL_OUTER_PROTO2 (MII_BCM54XX_EXP_SEL_WOL + 0x83) + +#define BCM54XX_WOL_MPD_DATA1(x) (MII_BCM54XX_EXP_SEL_WOL + 0x84 + (x)) +#define BCM54XX_WOL_MPD_DATA2(x) (MII_BCM54XX_EXP_SEL_WOL + 0x87 + (x)) +#define BCM54XX_WOL_SEC_KEY_8B (MII_BCM54XX_EXP_SEL_WOL + 0x8A) +#define BCM54XX_WOL_MASK(x) (MII_BCM54XX_EXP_SEL_WOL + 0x8B + (x)) +#define BCM54XX_SEC_KEY_STORE(x) (MII_BCM54XX_EXP_SEL_WOL + 0x8E) +#define BCM54XX_WOL_SHARED_CNT (MII_BCM54XX_EXP_SEL_WOL + 0x92) + +#define BCM54XX_WOL_INT_MASK (MII_BCM54XX_EXP_SEL_WOL + 0x93) +#define BCM54XX_WOL_PKT1 BIT(0) +#define BCM54XX_WOL_PKT2 BIT(1) +#define BCM54XX_WOL_DIR BIT(2) +#define BCM54XX_WOL_ALL_INTRS (BCM54XX_WOL_PKT1 | \ + BCM54XX_WOL_PKT2 | \ + BCM54XX_WOL_DIR) + +#define BCM54XX_WOL_INT_STATUS (MII_BCM54XX_EXP_SEL_WOL + 0x94) + /*****************************************************************************/ /* Fast Ethernet Transceiver definitions. */ /*****************************************************************************/ @@ -304,6 +364,8 @@ #define LPI_FEATURE_EN 0x8000 #define LPI_FEATURE_EN_DIG1000X 0x4000 +#define BRCM_CL45VEN_EEE_LPI_CNT 0x803f + /* Core register definitions*/ #define MII_BRCM_CORE_BASE12 0x12 #define MII_BRCM_CORE_BASE13 0x13 diff --git a/include/linux/platform_data/dma-bcm2708.h b/include/linux/platform_data/dma-bcm2708.h new file mode 100644 index 0000000000000..6ca874d332a8b --- /dev/null +++ b/include/linux/platform_data/dma-bcm2708.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _PLAT_BCM2708_DMA_H +#define _PLAT_BCM2708_DMA_H + +/* DMA CS Control and Status bits */ +#define BCM2708_DMA_ACTIVE BIT(0) +#define BCM2708_DMA_INT BIT(2) +#define BCM2708_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ +#define BCM2708_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ +#define BCM2708_DMA_ERR BIT(8) +#define BCM2708_DMA_ABORT BIT(30) /* stop current CB, go to next, WO */ +#define BCM2708_DMA_RESET BIT(31) /* WO, self clearing */ + +/* DMA control block "info" field bits */ +#define BCM2708_DMA_INT_EN BIT(0) +#define BCM2708_DMA_TDMODE BIT(1) +#define BCM2708_DMA_WAIT_RESP BIT(3) +#define BCM2708_DMA_D_INC BIT(4) +#define BCM2708_DMA_D_WIDTH BIT(5) +#define BCM2708_DMA_D_DREQ BIT(6) +#define BCM2708_DMA_S_INC BIT(8) +#define BCM2708_DMA_S_WIDTH BIT(9) +#define BCM2708_DMA_S_DREQ BIT(10) + +#define BCM2708_DMA_BURST(x) (((x) & 0xf) << 12) +#define BCM2708_DMA_PER_MAP(x) ((x) << 16) +#define BCM2708_DMA_WAITS(x) (((x) & 0x1f) << 21) + +#define BCM2708_DMA_DREQ_EMMC 11 +#define BCM2708_DMA_DREQ_SDHOST 13 + +#define BCM2708_DMA_CS 0x00 /* Control and Status */ +#define BCM2708_DMA_ADDR 0x04 +/* the current control block appears in the following registers - read only */ +#define BCM2708_DMA_INFO 0x08 +#define BCM2708_DMA_SOURCE_AD 0x0c +#define BCM2708_DMA_DEST_AD 0x10 +#define BCM2708_DMA_NEXTCB 0x1C +#define BCM2708_DMA_DEBUG 0x20 + +#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4) + BCM2708_DMA_CS) +#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4) + BCM2708_DMA_ADDR) + +#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) + +/* When listing features we can ask for when allocating DMA channels give + those with higher priority smaller ordinal numbers */ +#define BCM_DMA_FEATURE_FAST_ORD 0 +#define BCM_DMA_FEATURE_BULK_ORD 1 +#define BCM_DMA_FEATURE_NORMAL_ORD 2 +#define BCM_DMA_FEATURE_LITE_ORD 3 +#define BCM_DMA_FEATURE_FAST BIT(BCM_DMA_FEATURE_FAST_ORD) +#define BCM_DMA_FEATURE_BULK BIT(BCM_DMA_FEATURE_BULK_ORD) +#define BCM_DMA_FEATURE_NORMAL BIT(BCM_DMA_FEATURE_NORMAL_ORD) +#define BCM_DMA_FEATURE_LITE BIT(BCM_DMA_FEATURE_LITE_ORD) +#define BCM_DMA_FEATURE_COUNT 4 + +struct bcm2708_dma_cb { + u32 info; + u32 src; + u32 dst; + u32 length; + u32 stride; + u32 next; + u32 pad[2]; +}; + +struct scatterlist; +struct platform_device; + +#if defined(CONFIG_DMA_BCM2708) || defined(CONFIG_DMA_BCM2708_MODULE) + +int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); +void bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block); +void bcm_dma_wait_idle(void __iomem *dma_chan_base); +bool bcm_dma_is_busy(void __iomem *dma_chan_base); +int bcm_dma_abort(void __iomem *dma_chan_base); + +/* return channel no or -ve error */ +int bcm_dma_chan_alloc(unsigned preferred_feature_set, + void __iomem **out_dma_base, int *out_dma_irq); +int bcm_dma_chan_free(int channel); + +int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base, + u32 chans_available); +int bcm_dmaman_remove(struct platform_device *pdev); + +#else /* CONFIG_DMA_BCM2708 */ + +static inline int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, + int sg_len) +{ + return 0; +} + +static inline void bcm_dma_start(void __iomem *dma_chan_base, + dma_addr_t control_block) { } + +static inline void bcm_dma_wait_idle(void __iomem *dma_chan_base) { } + +static inline bool bcm_dma_is_busy(void __iomem *dma_chan_base) +{ + return false; +} + +static inline int bcm_dma_abort(void __iomem *dma_chan_base) +{ + return -EINVAL; +} + +static inline int bcm_dma_chan_alloc(unsigned preferred_feature_set, + void __iomem **out_dma_base, + int *out_dma_irq) +{ + return -EINVAL; +} + +static inline int bcm_dma_chan_free(int channel) +{ + return -EINVAL; +} + +static inline int bcm_dmaman_probe(struct platform_device *pdev, + void __iomem *base, u32 chans_available) +{ + return 0; +} + +static inline int bcm_dmaman_remove(struct platform_device *pdev) +{ + return 0; +} + +#endif /* CONFIG_DMA_BCM2708 || CONFIG_DMA_BCM2708_MODULE */ + +#endif /* _PLAT_BCM2708_DMA_H */ diff --git a/include/linux/rp1_platform.h b/include/linux/rp1_platform.h new file mode 100644 index 0000000000000..f805dbe1ed9be --- /dev/null +++ b/include/linux/rp1_platform.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2022 Raspberry Pi Ltd. + * All rights reserved. + */ + +#ifndef _RP1_PLATFORM_H +#define _RP1_PLATFORM_H + +#include + +#define RP1_B0_CHIP_ID 0x10001927 +#define RP1_C0_CHIP_ID 0x20001927 + +#define RP1_PLATFORM_ASIC BIT(1) +#define RP1_PLATFORM_FPGA BIT(0) + +void rp1_get_platform(u32 *chip_id, u32 *platform); + +#endif