mirror of
https://github.com/VIKINGYFY/immortalwrt.git
synced 2025-12-16 17:15:26 +00:00
Merge branch 'VIKINGYFY:main' into main
This commit is contained in:
commit
f96f4f2947
@ -201,7 +201,7 @@ else
|
||||
DOWNLOAD_DIRS = package/download
|
||||
endif
|
||||
|
||||
download: .config FORCE $(if $(wildcard $(STAGING_DIR_HOST)/bin/flock),,tools/flock/compile)
|
||||
download: .config FORCE $(if $(wildcard $(STAGING_DIR_HOST)/bin/flock),,tools/flock/compile) $(if $(wildcard $(STAGING_DIR_HOST)/bin/zstd),,tools/zstd/compile)
|
||||
@+$(foreach dir,$(DOWNLOAD_DIRS),$(SUBMAKE) $(dir);)
|
||||
|
||||
clean dirclean: .config
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
[ "$(uci -q get network.globals.dhcp_default_duid)" != "auto" ] && exit 0
|
||||
[ "$(uci -q get network.globals.dhcp_default_duid || echo "auto")" != "auto" ] && exit 0
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
# DUID-UUID - RFC6355
|
||||
@ -30,6 +30,7 @@ define Trusted-Firmware-A/Default
|
||||
BOOT_DEVICE:=
|
||||
DDR3_FLYBY:=
|
||||
DDR3_FREQ_1866:=
|
||||
DDR4_4BG_MODE:=
|
||||
DDR_TYPE:=
|
||||
NAND_TYPE:=
|
||||
BOARD_QFN:=
|
||||
@ -139,6 +140,14 @@ define Trusted-Firmware-A/mt7622-sdmmc-2ddr
|
||||
DDR3_FLYBY:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-nor-ddr4
|
||||
NAME:=MediaTek MT7981 (SPI-NOR, DDR4)
|
||||
BOOT_DEVICE:=nor
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7981
|
||||
DDR_TYPE:=ddr4
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-ram-ddr4
|
||||
NAME:=MediaTek MT7981 (RAM, DDR4)
|
||||
BOOT_DEVICE:=ram
|
||||
@ -158,6 +167,14 @@ define Trusted-Firmware-A/mt7981-emmc-ddr4
|
||||
DDR_TYPE:=ddr4
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-sdmmc-ddr4
|
||||
NAME:=MediaTek MT7981 (SD card, DDR4)
|
||||
BOOT_DEVICE:=sdmmc
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7981
|
||||
DDR_TYPE:=ddr4
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-spim-nand-ddr4
|
||||
NAME:=MediaTek MT7981 (SPI-NAND via SPIM, DDR4)
|
||||
BOOT_DEVICE:=spim-nand
|
||||
@ -186,14 +203,6 @@ define Trusted-Firmware-A/mt7981-ram-ddr3
|
||||
DEFAULT:=TARGET_mediatek_filogic
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-nor-ddr4
|
||||
NAME:=MediaTek MT7981 (SPI-NOR, DDR4)
|
||||
BOOT_DEVICE:=nor
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7981
|
||||
DDR_TYPE:=ddr4
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-emmc-ddr3
|
||||
NAME:=MediaTek MT7981 (eMMC, DDR3)
|
||||
BOOT_DEVICE:=emmc
|
||||
@ -244,6 +253,15 @@ define Trusted-Firmware-A/mt7981-spim-nand-ddr3-1866mhz
|
||||
DDR3_FREQ_1866:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-spim-nand-ubi-ddr4
|
||||
NAME:=MediaTek MT7981 (SPI-NAND via SPIM, DDR4)
|
||||
BOOT_DEVICE:=spim-nand
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7981
|
||||
DDR_TYPE:=ddr4
|
||||
USE_UBI:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-cudy-tr3000-v1
|
||||
NAME:=Cudy TR3000 v1 (SPI-NAND via SPIM, DDR3)
|
||||
BOOT_DEVICE:=spim-nand
|
||||
@ -265,15 +283,6 @@ define Trusted-Firmware-A/mt7986-ram-ddr4
|
||||
DEFAULT:=TARGET_mediatek_filogic
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7981-spim-nand-ubi-ddr4
|
||||
NAME:=MediaTek MT7981 (SPI-NAND via SPIM, DDR4)
|
||||
BOOT_DEVICE:=spim-nand
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7981
|
||||
DDR_TYPE:=ddr4
|
||||
USE_UBI:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7986-nor-ddr4
|
||||
NAME:=MediaTek MT7986 (SPI-NOR, DDR4)
|
||||
BOOT_DEVICE:=nor
|
||||
@ -399,7 +408,14 @@ define Trusted-Firmware-A/mt7987-emmc-comb
|
||||
BOOT_DEVICE:=emmc
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7987-emmc-ddr4-4bg
|
||||
NAME:=MediaTek MT7987 (eMMC, DDR4 4GB)
|
||||
BOOT_DEVICE:=emmc
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DDR4_4BG_MODE:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7987-nor-comb
|
||||
@ -407,7 +423,6 @@ define Trusted-Firmware-A/mt7987-nor-comb
|
||||
BOOT_DEVICE:=nor
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7987-sdmmc-comb
|
||||
@ -415,7 +430,14 @@ define Trusted-Firmware-A/mt7987-sdmmc-comb
|
||||
BOOT_DEVICE:=sdmmc
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7987-sdmmc-ddr4-4bg
|
||||
NAME:=MediaTek MT7987 (SD card, DDR4 4GB)
|
||||
BOOT_DEVICE:=sdmmc
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DDR4_4BG_MODE:=1
|
||||
endef
|
||||
|
||||
define Trusted-Firmware-A/mt7987-spim-nand0-ubi-comb
|
||||
@ -423,7 +445,6 @@ define Trusted-Firmware-A/mt7987-spim-nand0-ubi-comb
|
||||
BOOT_DEVICE:=spim-nand
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
USE_UBI:=1
|
||||
SPIM_CTRL:=0
|
||||
endef
|
||||
@ -433,7 +454,6 @@ define Trusted-Firmware-A/mt7987-spim-nand2-ubi-comb
|
||||
BOOT_DEVICE:=spim-nand
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
USE_UBI:=1
|
||||
SPIM_CTRL:=2
|
||||
endef
|
||||
@ -443,7 +463,6 @@ define Trusted-Firmware-A/mt7987-ram-comb
|
||||
BOOT_DEVICE:=ram
|
||||
BUILD_SUBTARGET:=filogic
|
||||
PLAT:=mt7987
|
||||
DRAM_USE_COMB:=1
|
||||
RAM_BOOT_UART_DL:=1
|
||||
HIDDEN:=
|
||||
DEFAULT:=TARGET_mediatek_filogic
|
||||
@ -654,6 +673,7 @@ TFA_TARGETS:= \
|
||||
mt7981-spim-nand-ubi-ddr4 \
|
||||
mt7981-ram-ddr4 \
|
||||
mt7981-emmc-ddr4 \
|
||||
mt7981-sdmmc-ddr4 \
|
||||
mt7981-spim-nand-ddr4 \
|
||||
mt7981-cudy-tr3000-v1 \
|
||||
mt7986-ram-ddr3 \
|
||||
@ -672,8 +692,10 @@ TFA_TARGETS:= \
|
||||
mt7986-spim-nand-ubi-ddr4 \
|
||||
mt7986-spim-nand-4k-ddr4 \
|
||||
mt7987-emmc-comb \
|
||||
mt7987-emmc-ddr4-4bg \
|
||||
mt7987-nor-comb \
|
||||
mt7987-sdmmc-comb \
|
||||
mt7987-sdmmc-ddr4-4bg \
|
||||
mt7987-spim-nand0-ubi-comb \
|
||||
mt7987-spim-nand2-ubi-comb \
|
||||
mt7987-ram-comb \
|
||||
@ -708,6 +730,7 @@ TFA_MAKE_FLAGS += \
|
||||
HAVE_DRAM_OBJ_FILE=yes \
|
||||
$(if $(DDR3_FLYBY),DDR3_FLYBY=1) \
|
||||
$(if $(DDR3_FREQ_1866),DDR3_FREQ_1866=1) \
|
||||
$(if $(DDR4_4BG_MODE),DDR4_4BG_MODE=1) \
|
||||
$(if $(DRAM_USE_COMB),DRAM_USE_COMB=1) \
|
||||
$(if $(RAM_BOOT_UART_DL),RAM_BOOT_UART_DL=1) \
|
||||
$(if $(USE_UBI),UBI=1 $(if $(findstring mt7622,$(PLAT)),OVERRIDE_UBI_START_ADDR=0x80000)) \
|
||||
|
||||
@ -391,6 +391,13 @@ define U-Boot/generic-rk3576
|
||||
friendlyarm_nanopi-r76s
|
||||
endef
|
||||
|
||||
define U-Boot/photonicat2-rk3576
|
||||
$(U-Boot/rk3576/Default)
|
||||
NAME:=Photonicat2
|
||||
BUILD_DEVICES:= \
|
||||
ariaboard_photonicat2
|
||||
endef
|
||||
|
||||
define U-Boot/rock-4d-rk3576
|
||||
$(U-Boot/rk3576/Default)
|
||||
NAME:=ROCK 4D
|
||||
@ -540,6 +547,7 @@ UBOOT_TARGETS := \
|
||||
rock-3b-rk3568 \
|
||||
sige3-rk3568 \
|
||||
generic-rk3576 \
|
||||
photonicat2-rk3576 \
|
||||
rock-4d-rk3576 \
|
||||
cyber3588-aib-rk3588 \
|
||||
nanopc-t6-rk3588 \
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "rk3576-u-boot.dtsi"
|
||||
@ -0,0 +1,980 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
|
||||
/*
|
||||
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/pinctrl/rockchip.h>
|
||||
#include <dt-bindings/soc/rockchip,vop2.h>
|
||||
|
||||
#include "rk3576.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Ariaboard Photonicat 2";
|
||||
compatible = "ariaboard,photonicat2", "rockchip,rk3576";
|
||||
|
||||
aliases {
|
||||
ethernet0 = &gmac0;
|
||||
ethernet1 = &gmac1;
|
||||
mmc0 = &sdhci;
|
||||
mmc1 = &sdmmc;
|
||||
};
|
||||
|
||||
chosen {
|
||||
stdout-path = "serial0:1500000n8";
|
||||
};
|
||||
|
||||
battery: battery {
|
||||
compatible = "simple-battery";
|
||||
device-chemistry = "lithium-ion";
|
||||
voltage-min-design-microvolt = <6800000>;
|
||||
voltage-max-design-microvolt = <8400000>;
|
||||
energy-full-design-microwatt-hours = <51800000>;
|
||||
ocv-capacity-celsius = <20>;
|
||||
ocv-capacity-table-0 = <8344000 100>, <8184000 95>, <8070000 90>, <7980000 85>,
|
||||
<7878000 80>, <7790000 75>, <7704000 70>, <7614000 65>,
|
||||
<7524000 60>, <7426000 55>, <7344000 50>, <7294000 45>,
|
||||
<7258000 40>, <7226000 35>, <7196000 30>, <7156000 25>,
|
||||
<7100000 20>, <7038000 15>, <6958000 10>, <6876000 5>,
|
||||
<6800000 0>;
|
||||
};
|
||||
|
||||
hdmi-con {
|
||||
compatible = "hdmi-connector";
|
||||
type = "a";
|
||||
|
||||
port {
|
||||
hdmi_con_in: endpoint {
|
||||
remote-endpoint = <&hdmi_out_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vccin_pd: regulator-vccin-pd {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vccin_pd";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <12000000>;
|
||||
regulator-max-microvolt = <12000000>;
|
||||
};
|
||||
|
||||
vcc5v0_sys_s5: regulator-vcc5v0-sys-s5 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc5v0_sys_s5";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
vin-supply = <&vccin_pd>;
|
||||
};
|
||||
|
||||
vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_1v1_nldo_s3";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1100000>;
|
||||
regulator-max-microvolt = <1100000>;
|
||||
vin-supply = <&vcc5v0_sys_s5>;
|
||||
};
|
||||
|
||||
vcc_1v8_s0: regulator-vcc-1v8-s0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_1v8_s0";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
vin-supply = <&vcc_1v8_s3>;
|
||||
};
|
||||
|
||||
vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_2v0_pldo_s3";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <2000000>;
|
||||
regulator-max-microvolt = <2000000>;
|
||||
vin-supply = <&vcc5v0_sys_s5>;
|
||||
};
|
||||
|
||||
vcc_3v3_pcie0: regulator-vcc-3v3-pcie0 {
|
||||
compatible = "regulator-fixed";
|
||||
enable-active-high;
|
||||
gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_pwren_h>;
|
||||
regulator-name = "vcc_3v3_pcie0";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
startup-delay-us = <5000>;
|
||||
vin-supply = <&vcc5v0_otg>;
|
||||
};
|
||||
|
||||
vcc_3v3_pcie1: regulator-vcc-3v3-pcie1 {
|
||||
compatible = "regulator-fixed";
|
||||
enable-active-high;
|
||||
gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_pwren_h>;
|
||||
regulator-name = "vcc_3v3_pcie1";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
startup-delay-us = <5000>;
|
||||
vin-supply = <&vcc5v0_sys_s5>;
|
||||
};
|
||||
|
||||
vcc_3v3_rtc_s5: regulator-vcc-3v3-rtc-s5 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_3v3_rtc_s5";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
vin-supply = <&vcc5v0_sys_s5>;
|
||||
};
|
||||
|
||||
vcc_3v3_s0: regulator-vcc-3v3-s0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_3v3_s0";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
vin-supply = <&vcc_3v3_s3>;
|
||||
};
|
||||
|
||||
vcc_5v0_device: regulator-vcc-5v0-device {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_5v0_device";
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
vin-supply = <&vccin_pd>;
|
||||
};
|
||||
|
||||
vcc5v0_otg: regulator-vcc5v0-otg {
|
||||
compatible = "regulator-fixed";
|
||||
enable-active-high;
|
||||
gpios = <&gpio4 RK_PC4 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&usb_otg0_pwren_h>;
|
||||
regulator-name = "vcc5v0_otg";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
vin-supply = <&vcc5v0_usb_switch>;
|
||||
};
|
||||
|
||||
vcc5v0_usb_switch: vcc5v0-usb-switch {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc5v0_usb_switch";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
enable-active-high;
|
||||
gpio = <&gpio0 RK_PC2 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&gpio0_c2>;
|
||||
vin-supply = <&vcc5v0_sys_s5>;
|
||||
};
|
||||
|
||||
rfkill-pcie-bluetooth {
|
||||
compatible = "rfkill-gpio";
|
||||
label = "rfkill-pcie-bluetooth";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&m2_bt_en>;
|
||||
radio-type = "bluetooth";
|
||||
shutdown-gpios = <&gpio0 RK_PD0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
rfkill-pcie-wlan {
|
||||
compatible = "rfkill-gpio";
|
||||
label = "rfkill-pcie-wlan";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&m2_wlan_en>;
|
||||
radio-type = "wlan";
|
||||
shutdown-gpios = <&gpio0 RK_PD1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
rfkill-usb-wlan {
|
||||
compatible = "rfkill-gpio";
|
||||
label = "rfkill-usb-wlan";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&rf_pwr_en_n>;
|
||||
radio-type = "wlan";
|
||||
reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;
|
||||
shutdown-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
rfkill-usb-wwan {
|
||||
compatible = "rfkill-gpio";
|
||||
label = "rfkill-usb-wwan";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&rst_5g_1v8_h>;
|
||||
radio-type = "wwan";
|
||||
reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
|
||||
shutdown-gpios = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
&combphy0_ps {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&combphy1_psu {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&cpu_b0 {
|
||||
cpu-supply = <&vdd_cpu_big_s0>;
|
||||
};
|
||||
|
||||
&cpu_b1 {
|
||||
cpu-supply = <&vdd_cpu_big_s0>;
|
||||
};
|
||||
|
||||
&cpu_b2 {
|
||||
cpu-supply = <&vdd_cpu_big_s0>;
|
||||
};
|
||||
|
||||
&cpu_b3 {
|
||||
cpu-supply = <&vdd_cpu_big_s0>;
|
||||
};
|
||||
|
||||
&cpu_l0 {
|
||||
cpu-supply = <&vdd_cpu_lit_s0>;
|
||||
};
|
||||
|
||||
&cpu_l1 {
|
||||
cpu-supply = <&vdd_cpu_lit_s0>;
|
||||
};
|
||||
|
||||
&cpu_l2 {
|
||||
cpu-supply = <&vdd_cpu_lit_s0>;
|
||||
};
|
||||
|
||||
&cpu_l3 {
|
||||
cpu-supply = <&vdd_cpu_lit_s0>;
|
||||
};
|
||||
|
||||
&gmac0 {
|
||||
clock_in_out = "output";
|
||||
phy-handle = <&rgmii_phy0>;
|
||||
phy-mode = "rgmii-id";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <ð0m1_miim
|
||||
ð0m1_tx_bus2
|
||||
ð0m1_rx_bus2
|
||||
ð0m1_rgmii_clk
|
||||
ð0m1_rgmii_bus
|
||||
ðm1_clk0_25m_out>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gmac1 {
|
||||
clock_in_out = "output";
|
||||
phy-handle = <&rgmii_phy1>;
|
||||
phy-mode = "rgmii-id";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <ð1m0_miim
|
||||
ð1m0_tx_bus2
|
||||
ð1m0_rx_bus2
|
||||
ð1m0_rgmii_clk
|
||||
ð1m0_rgmii_bus
|
||||
ðm0_clk1_25m_out>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpu {
|
||||
mali-supply = <&vdd_gpu_s0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&hdmi {
|
||||
enable-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&hdmi_in {
|
||||
hdmi_in_vp0: endpoint {
|
||||
remote-endpoint = <&vp0_out_hdmi>;
|
||||
};
|
||||
};
|
||||
|
||||
&hdmi_out {
|
||||
hdmi_out_con: endpoint {
|
||||
remote-endpoint = <&hdmi_con_in>;
|
||||
};
|
||||
};
|
||||
|
||||
&hdmi_sound {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&hdptxphy {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
status = "okay";
|
||||
|
||||
pmic@23 {
|
||||
compatible = "rockchip,rk806";
|
||||
reg = <0x23>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>,
|
||||
<&rk806_dvs2_null>, <&rk806_dvs3_null>;
|
||||
system-power-controller;
|
||||
|
||||
vcc1-supply = <&vcc5v0_sys_s5>;
|
||||
vcc2-supply = <&vcc5v0_sys_s5>;
|
||||
vcc3-supply = <&vcc5v0_sys_s5>;
|
||||
vcc4-supply = <&vcc5v0_sys_s5>;
|
||||
vcc5-supply = <&vcc5v0_sys_s5>;
|
||||
vcc6-supply = <&vcc5v0_sys_s5>;
|
||||
vcc7-supply = <&vcc5v0_sys_s5>;
|
||||
vcc8-supply = <&vcc5v0_sys_s5>;
|
||||
vcc9-supply = <&vcc5v0_sys_s5>;
|
||||
vcc10-supply = <&vcc5v0_sys_s5>;
|
||||
vcc11-supply = <&vcc_2v0_pldo_s3>;
|
||||
vcc12-supply = <&vcc5v0_sys_s5>;
|
||||
vcc13-supply = <&vcc_1v1_nldo_s3>;
|
||||
vcc14-supply = <&vcc_1v1_nldo_s3>;
|
||||
vcca-supply = <&vcc5v0_sys_s5>;
|
||||
|
||||
rk806_dvs1_null: dvs1-null-pins {
|
||||
pins = "gpio_pwrctrl1";
|
||||
function = "pin_fun0";
|
||||
};
|
||||
|
||||
rk806_dvs1_pwrdn: dvs1-pwrdn-pins {
|
||||
pins = "gpio_pwrctrl1";
|
||||
function = "pin_fun2";
|
||||
};
|
||||
|
||||
rk806_dvs1_rst: dvs1-rst-pins {
|
||||
pins = "gpio_pwrctrl1";
|
||||
function = "pin_fun3";
|
||||
};
|
||||
|
||||
rk806_dvs1_slp: dvs1-slp-pins {
|
||||
pins = "gpio_pwrctrl1";
|
||||
function = "pin_fun1";
|
||||
};
|
||||
|
||||
rk806_dvs2_dvs: dvs2-dvs-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun4";
|
||||
};
|
||||
|
||||
rk806_dvs2_gpio: dvs2-gpio-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun5";
|
||||
};
|
||||
|
||||
rk806_dvs2_null: dvs2-null-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun0";
|
||||
};
|
||||
|
||||
rk806_dvs2_pwrdn: dvs2-pwrdn-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun2";
|
||||
};
|
||||
|
||||
rk806_dvs2_rst: dvs2-rst-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun3";
|
||||
};
|
||||
|
||||
rk806_dvs2_slp: dvs2-slp-pins {
|
||||
pins = "gpio_pwrctrl2";
|
||||
function = "pin_fun1";
|
||||
};
|
||||
|
||||
rk806_dvs3_dvs: dvs3-dvs-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun4";
|
||||
};
|
||||
|
||||
rk806_dvs3_gpio: dvs3-gpio-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun5";
|
||||
};
|
||||
|
||||
rk806_dvs3_null: dvs3-null-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun0";
|
||||
};
|
||||
|
||||
rk806_dvs3_pwrdn: dvs3-pwrdn-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun2";
|
||||
};
|
||||
|
||||
rk806_dvs3_rst: dvs3-rst-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun3";
|
||||
};
|
||||
|
||||
rk806_dvs3_slp: dvs3-slp-pins {
|
||||
pins = "gpio_pwrctrl3";
|
||||
function = "pin_fun1";
|
||||
};
|
||||
|
||||
regulators {
|
||||
vdd_cpu_big_s0: dcdc-reg1 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <950000>;
|
||||
regulator-ramp-delay = <12500>;
|
||||
regulator-name = "vdd_cpu_big_s0";
|
||||
regulator-enable-ramp-delay = <400>;
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_npu_s0: dcdc-reg2 {
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <950000>;
|
||||
regulator-ramp-delay = <12500>;
|
||||
regulator-enable-ramp-delay = <400>;
|
||||
regulator-name = "vdd_npu_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_cpu_lit_s0: dcdc-reg3 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <950000>;
|
||||
regulator-name = "vdd_cpu_lit_s0";
|
||||
regulator-ramp-delay = <12500>;
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
regulator-suspend-microvolt = <750000>;
|
||||
};
|
||||
};
|
||||
|
||||
vcc_3v3_s3: dcdc-reg4 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-name = "vcc_3v3_s3";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-on-in-suspend;
|
||||
regulator-suspend-microvolt = <3300000>;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_gpu_s0: dcdc-reg5 {
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <900000>;
|
||||
regulator-ramp-delay = <12500>;
|
||||
regulator-name = "vdd_gpu_s0";
|
||||
regulator-enable-ramp-delay = <400>;
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
regulator-suspend-microvolt = <850000>;
|
||||
};
|
||||
};
|
||||
|
||||
vddq_ddr_s0: dcdc-reg6 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-name = "vddq_ddr_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_logic_s0: dcdc-reg7 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <800000>;
|
||||
regulator-name = "vdd_logic_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vcc_1v8_s3: dcdc-reg8 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-name = "vcc_1v8_s3";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-on-in-suspend;
|
||||
regulator-suspend-microvolt = <1800000>;
|
||||
};
|
||||
};
|
||||
|
||||
vdd2_ddr_s3: dcdc-reg9 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-name = "vdd2_ddr_s3";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-on-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_ddr_s0: dcdc-reg10 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-name = "vdd_ddr_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vcca_1v8_s0: pldo-reg1 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-name = "vcca_1v8_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vcca1v8_pldo2_s0: pldo-reg2 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-name = "vcca1v8_pldo2_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdda_1v2_s0: pldo-reg3 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-name = "vdda_1v2_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vcca_3v3_s0: pldo-reg4 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-name = "vcca_3v3_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vccio_sd_s0: pldo-reg5 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-name = "vccio_sd_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vcca1v8_pldo6_s3: pldo-reg6 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-name = "vcca1v8_pldo6_s3";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-on-in-suspend;
|
||||
regulator-suspend-microvolt = <1800000>;
|
||||
};
|
||||
};
|
||||
|
||||
vdd_0v75_s3: nldo-reg1 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <750000>;
|
||||
regulator-max-microvolt = <750000>;
|
||||
regulator-name = "vdd_0v75_s3";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-on-in-suspend;
|
||||
regulator-suspend-microvolt = <750000>;
|
||||
};
|
||||
};
|
||||
|
||||
vdda_ddr_pll_s0: nldo-reg2 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-name = "vdda_ddr_pll_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdda0v75_hdmi_s0: nldo-reg3 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <837500>;
|
||||
regulator-max-microvolt = <837500>;
|
||||
regulator-name = "vdda0v75_hdmi_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdda_0v85_s0: nldo-reg4 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-name = "vdda_0v85_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
|
||||
vdda_0v75_s0: nldo-reg5 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <750000>;
|
||||
regulator-max-microvolt = <750000>;
|
||||
regulator-name = "vdda_0v75_s0";
|
||||
|
||||
regulator-state-mem {
|
||||
regulator-off-in-suspend;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c8 {
|
||||
pinctrl-0 = <&i2c8m3_xfer>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&mdio0 {
|
||||
rgmii_phy0: phy@1 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
reg = <1>;
|
||||
clocks = <&cru REFCLKO25M_GMAC0_OUT>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <ð0_rstn_l>;
|
||||
reset-assert-us = <20000>;
|
||||
reset-deassert-us = <100000>;
|
||||
reset-gpios = <&gpio0 RK_PD2 GPIO_ACTIVE_LOW>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_AMBER>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&mdio1 {
|
||||
rgmii_phy1: phy@1 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
reg = <1>;
|
||||
clocks = <&cru REFCLKO25M_GMAC1_OUT>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <ð1_rstn_l>;
|
||||
reset-assert-us = <20000>;
|
||||
reset-deassert-us = <100000>;
|
||||
reset-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_LOW>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@1 {
|
||||
reg = <1>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
|
||||
led@2 {
|
||||
reg = <2>;
|
||||
color = <LED_COLOR_ID_AMBER>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_perstn>;
|
||||
reset-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>;
|
||||
vpcie3v3-supply = <&vcc_3v3_pcie0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_perstn &pcie1m2_pins>;
|
||||
reset-gpios = <&gpio4 RK_PB2 GPIO_ACTIVE_HIGH>;
|
||||
vpcie3v3-supply = <&vcc_3v3_pcie1>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
gmac0 {
|
||||
eth0_rstn_l: eth0-rstn-l {
|
||||
rockchip,pins = <0 RK_PD2 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1 {
|
||||
eth1_rstn_l: eth1-rstn-l {
|
||||
rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
spi1m2_1_pins: spi1m2-1-pins {
|
||||
rockchip,pins =
|
||||
/* spi1_clk_m2 */
|
||||
<3 RK_PC7 10 &pcfg_pull_none>,
|
||||
/* spi1_mosi_m2 */
|
||||
<3 RK_PC6 10 &pcfg_pull_none>;
|
||||
};
|
||||
};
|
||||
|
||||
pcie {
|
||||
pcie0_perstn: pcie0-perstn {
|
||||
rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
|
||||
pcie0_pwren_h: pcie0-pwren-h {
|
||||
rockchip,pins = <0 RK_PC7 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
|
||||
pcie1_perstn: pcie1-perstn {
|
||||
rockchip,pins = <4 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
|
||||
pcie1_pwren_h: pcie1-pwren-h {
|
||||
rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
};
|
||||
|
||||
rfkill {
|
||||
rst_5g_1v8_h: 5g-rst-1v8-h {
|
||||
rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
};
|
||||
|
||||
m2_bt_en: m2-bt-en {
|
||||
rockchip,pins = <0 RK_PD0 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
|
||||
m2_wlan_en: m2-wlan-en {
|
||||
rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
|
||||
rf_pwr_en_n: rf-pwr-en-n {
|
||||
rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||
};
|
||||
};
|
||||
|
||||
usb {
|
||||
gpio0_c2: gpio0-c2 {
|
||||
rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
};
|
||||
|
||||
pwm2_ch1_m3: pwm2-ch1-m3 {
|
||||
rockchip,pins = <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
|
||||
usb_otg0_pwren_h: usb-otg0-pwren-h {
|
||||
rockchip,pins = <4 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&sai6 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&saradc {
|
||||
vref-supply = <&vcca_1v8_s0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&sdhci {
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
full-pwr-cycle-in-suspend;
|
||||
max-frequency = <150000000>;
|
||||
mmc-hs400-1_8v;
|
||||
mmc-hs400-enhanced-strobe;
|
||||
no-sdio;
|
||||
no-sd;
|
||||
non-removable;
|
||||
/delete-property/ supports-cqe;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&sdmmc {
|
||||
bus-width = <4>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
disable-wp;
|
||||
no-mmc;
|
||||
no-sdio;
|
||||
sd-uhs-sdr104;
|
||||
vmmc-supply = <&vcc_3v3_s3>;
|
||||
vqmmc-supply = <&vccio_sd_s0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-0 = <&spi1m2_csn0 &spi1m2_1_pins>;
|
||||
status = "okay";
|
||||
|
||||
spidev@0 {
|
||||
compatible = "micron,spi-authenta";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <6000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&u2phy0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&u2phy0_otg {
|
||||
phy-supply = <&vcc5v0_otg>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usbdp_phy {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb_drd0_dwc3 {
|
||||
dr_mode = "host";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pwm2_ch1_m3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
hub_2_0: hub@1 {
|
||||
compatible = "usb5e3,610";
|
||||
reg = <1>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&vcc_3v3_s3>;
|
||||
};
|
||||
|
||||
hub_3_0: hub@2 {
|
||||
compatible = "usb5e3,620";
|
||||
reg = <2>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&vcc_3v3_s3>;
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-0 = <&uart2m2_xfer>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart10 {
|
||||
pinctrl-0 = <&uart10m2_xfer>;
|
||||
status = "okay";
|
||||
|
||||
pcat-pm {
|
||||
compatible = "photonicat-pm";
|
||||
baudrate = <115200>;
|
||||
force-poweroff-timeout = <60>;
|
||||
pm-version = <2>;
|
||||
power-gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
charger {
|
||||
monitored-battery = <&battery>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&vop {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vop_mmu {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vp0 {
|
||||
vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
||||
reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
||||
remote-endpoint = <&hdmi_in_vp0>;
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,60 @@
|
||||
CONFIG_ARM=y
|
||||
CONFIG_SKIP_LOWLEVEL_INIT=y
|
||||
CONFIG_COUNTER_FREQUENCY=24000000
|
||||
CONFIG_ARCH_ROCKCHIP=y
|
||||
CONFIG_DEFAULT_DEVICE_TREE="rk3576-photonicat2"
|
||||
CONFIG_ROCKCHIP_RK3576=y
|
||||
CONFIG_SYS_LOAD_ADDR=0x40c00800
|
||||
CONFIG_DEBUG_UART_BASE=0x2AD40000
|
||||
CONFIG_DEBUG_UART_CLOCK=24000000
|
||||
CONFIG_PCI=y
|
||||
CONFIG_DEBUG_UART=y
|
||||
CONFIG_DEFAULT_FDT_FILE="rockchip/rk3576-photonicat2.dtb"
|
||||
# CONFIG_DISPLAY_CPUINFO is not set
|
||||
CONFIG_SPL_MAX_SIZE=0x40000
|
||||
CONFIG_CMD_MEMINFO=y
|
||||
CONFIG_CMD_MEMINFO_MAP=y
|
||||
CONFIG_CMD_ADC=y
|
||||
CONFIG_CMD_GPIO=y
|
||||
CONFIG_CMD_GPT=y
|
||||
CONFIG_CMD_I2C=y
|
||||
CONFIG_CMD_MISC=y
|
||||
CONFIG_CMD_MMC=y
|
||||
CONFIG_CMD_PCI=y
|
||||
CONFIG_CMD_USB=y
|
||||
# CONFIG_CMD_SETEXPR is not set
|
||||
CONFIG_CMD_RNG=y
|
||||
CONFIG_CMD_REGULATOR=y
|
||||
# CONFIG_SPL_DOS_PARTITION is not set
|
||||
# CONFIG_OF_UPSTREAM is not set
|
||||
CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
|
||||
CONFIG_ROCKCHIP_GPIO=y
|
||||
CONFIG_SYS_I2C_ROCKCHIP=y
|
||||
CONFIG_LED=y
|
||||
CONFIG_LED_GPIO=y
|
||||
CONFIG_SUPPORT_EMMC_RPMB=y
|
||||
CONFIG_MMC_DW=y
|
||||
CONFIG_MMC_DW_ROCKCHIP=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_SDMA=y
|
||||
CONFIG_MMC_SDHCI_ROCKCHIP=y
|
||||
CONFIG_PHY_REALTEK=y
|
||||
CONFIG_DWC_ETH_QOS=y
|
||||
CONFIG_DWC_ETH_QOS_ROCKCHIP=y
|
||||
CONFIG_NVME_PCI=y
|
||||
CONFIG_PCIE_DW_ROCKCHIP=y
|
||||
CONFIG_PHY_ROCKCHIP_INNO_USB2=y
|
||||
CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
|
||||
CONFIG_PHY_ROCKCHIP_USBDP=y
|
||||
CONFIG_DM_PMIC=y
|
||||
CONFIG_PMIC_RK8XX=y
|
||||
CONFIG_REGULATOR_RK8XX=y
|
||||
CONFIG_BAUDRATE=1500000
|
||||
CONFIG_DEBUG_UART_SHIFT=2
|
||||
CONFIG_SYS_NS16550_MEM32=y
|
||||
CONFIG_SYSRESET_PSCI=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_DWC3=y
|
||||
CONFIG_USB_DWC3_GENERIC=y
|
||||
CONFIG_ERRNO_STR=y
|
||||
@ -20,7 +20,8 @@ ocedo,panda)
|
||||
ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x20000" "0x20000"
|
||||
ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x20000" "0x20000"
|
||||
;;
|
||||
watchguard,firebox-t10)
|
||||
watchguard,firebox-t10|\
|
||||
watchguard,firebox-t15)
|
||||
ubootenv_add_uci_config "$(find_mtd_part 'u-boot-env')" "0x0" "0x2000" "0x10000"
|
||||
;;
|
||||
aerohive,hiveap-330)
|
||||
|
||||
@ -1654,3 +1654,68 @@ define KernelPackage/packet-diag
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,packet-diag))
|
||||
|
||||
define KernelPackage/team
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Ethernet team driver
|
||||
KCONFIG:=CONFIG_NET_TEAM
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team))
|
||||
|
||||
define KernelPackage/team-mode-broadcast
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Broadcast mode support
|
||||
DEPENDS:=kmod-team
|
||||
KCONFIG:=CONFIG_NET_TEAM_MODE_BROADCAST
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team_mode_broadcast.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team_mode_broadcast)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team-mode-broadcast))
|
||||
|
||||
define KernelPackage/team-mode-roundrobin
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Round-robin mode support
|
||||
DEPENDS:=kmod-team
|
||||
KCONFIG:=CONFIG_NET_TEAM_MODE_ROUNDROBIN
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team_mode_roundrobin.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team_mode_roundrobin)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team-mode-roundrobin))
|
||||
|
||||
define KernelPackage/team-mode-random
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Random mode support
|
||||
DEPENDS:=kmod-team
|
||||
KCONFIG:=CONFIG_NET_TEAM_MODE_RANDOM
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team_mode_random.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team_mode_random)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team-mode-random))
|
||||
|
||||
define KernelPackage/team-mode-activebackup
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Active-backup mode support
|
||||
DEPENDS:=kmod-team
|
||||
KCONFIG:=CONFIG_NET_TEAM_MODE_ACTIVEBACKUP
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team_mode_activebackup.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team_mode_activebackup)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team-mode-activebackup))
|
||||
|
||||
define KernelPackage/team-mode-loadbalance
|
||||
SUBMENU:=$(NETWORK_SUPPORT_MENU)
|
||||
TITLE:=Load-balance mode support
|
||||
DEPENDS:=kmod-team
|
||||
KCONFIG:=CONFIG_NET_TEAM_MODE_LOADBALANCE
|
||||
FILES:=$(LINUX_DIR)/drivers/net/team/team_mode_loadbalance.ko
|
||||
AUTOLOAD:=$(call AutoProbe,team_mode_loadbalance)
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,team-mode-loadbalance))
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
From b63f79524e6f30e58692bc9ee57c6e6e0dd10de4 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Wed, 10 Dec 2025 22:37:03 +0100
|
||||
Subject: [PATCH] backports: move __counted_by to compiler_types.h header
|
||||
|
||||
Port f06e108a3dc5 ("Compiler Attributes: disable __counted_by for clang
|
||||
< 19.1.3") changes to align with new kernel version that moved the
|
||||
__counted_by to a new header file.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
.../linux/compiler_attributes.h | 15 ------------
|
||||
.../backport-include/linux/compiler_types.h | 23 +++++++++++++++++++
|
||||
2 files changed, 23 insertions(+), 15 deletions(-)
|
||||
create mode 100644 backport-include/linux/compiler_types.h
|
||||
|
||||
--- a/backport-include/linux/compiler_attributes.h
|
||||
+++ b/backport-include/linux/compiler_attributes.h
|
||||
@@ -31,19 +31,4 @@
|
||||
#endif
|
||||
#endif /* fallthrough */
|
||||
|
||||
-#ifndef __counted_by
|
||||
-/*
|
||||
- * Optional: only supported since gcc >= 14
|
||||
- * Optional: only supported since clang >= 17
|
||||
- *
|
||||
- * gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
|
||||
- * clang: https://reviews.llvm.org/D148381
|
||||
- */
|
||||
-#if __has_attribute(__element_count__)
|
||||
-# define __counted_by(member) __attribute__((__element_count__(#member)))
|
||||
-#else
|
||||
-# define __counted_by(member)
|
||||
-#endif
|
||||
-#endif
|
||||
-
|
||||
#endif /* _BACKPORTS_LINUX_COMPILER_ATTRIBUTES_H */
|
||||
--- /dev/null
|
||||
+++ b/backport-include/linux/compiler_types.h
|
||||
@@ -0,0 +1,23 @@
|
||||
+#ifndef _BACKPORTS_LINUX_COMPILER_TYPES_H
|
||||
+#define _BACKPORTS_LINUX_COMPILER_TYPES_H 1
|
||||
+
|
||||
+#if LINUX_VERSION_IS_GEQ(6,12,1)
|
||||
+#include_next <linux/compiler_types.h>
|
||||
+#endif
|
||||
+
|
||||
+#ifndef __counted_by
|
||||
+/*
|
||||
+ * Optional: only supported since gcc >= 14
|
||||
+ * Optional: only supported since clang >= 17
|
||||
+ *
|
||||
+ * gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
|
||||
+ * clang: https://reviews.llvm.org/D148381
|
||||
+ */
|
||||
+#if __has_attribute(__element_count__)
|
||||
+# define __counted_by(member) __attribute__((__element_count__(#member)))
|
||||
+#else
|
||||
+# define __counted_by(member)
|
||||
+#endif
|
||||
+#endif
|
||||
+
|
||||
+#endif /* _BACKPORTS_LINUX_COMPILER_TYPES_H */
|
||||
@ -6,12 +6,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libcap
|
||||
PKG_VERSION:=2.69
|
||||
PKG_VERSION:=2.77
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=@KERNEL/linux/libs/security/linux-privs/libcap2
|
||||
PKG_HASH:=f311f8f3dad84699d0566d1d6f7ec943a9298b28f714cae3c931dfd57492d7eb
|
||||
PKG_HASH:=897bc18b44afc26c70e78cead3dbb31e154acc24bee085a5a09079a88dbf6f52
|
||||
|
||||
PKG_MAINTAINER:=Paul Wassi <p.wassi@gmx.at>
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
From 542d7d86ecd2129dd5fe7e5b31ba307304f5b319 Mon Sep 17 00:00:00 2001
|
||||
From: "Andrew G. Morgan" <morgan@kernel.org>
|
||||
Date: Mon, 10 Nov 2025 18:26:34 -0800
|
||||
Subject: [PATCH] Revert "libcap: Add build ldflags to _makenames rule"
|
||||
|
||||
This reverts commit c3ddf45d9afaab85d3b7db0dc7bfd1aafb8fde50.
|
||||
|
||||
The details of what this broke are here:
|
||||
|
||||
https://bugzilla.kernel.org/show_bug.cgi?id=220691#c2
|
||||
|
||||
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
|
||||
---
|
||||
libcap/Makefile | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/libcap/Makefile
|
||||
+++ b/libcap/Makefile
|
||||
@@ -80,7 +80,7 @@ $(PSXTITLE).pc: $(PSXTITLE).pc.in
|
||||
$< >$@
|
||||
|
||||
_makenames: _makenames.c cap_names.list.h
|
||||
- $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $< -o $@ $(BUILD_LDFLAGS)
|
||||
+ $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $< -o $@
|
||||
|
||||
cap_names.h: _makenames
|
||||
./_makenames > cap_names.h
|
||||
@ -9,12 +9,12 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libnftnl
|
||||
PKG_CPE_ID:=cpe:/a:netfilter:libnftnl
|
||||
PKG_VERSION:=1.3.0
|
||||
PKG_VERSION:=1.3.1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=https://netfilter.org/projects/$(PKG_NAME)/files
|
||||
PKG_HASH:=0f4be47a8bb8b77a350ee58cbd4b5fae6260ad486a527706ab15cfe1dd55a3c4
|
||||
PKG_HASH:=607da28dba66fbdeccf8ef1395dded9077e8d19f2995f9a4d45a9c2f0bcffba8
|
||||
|
||||
PKG_MAINTAINER:=Steven Barth <steven@midlink.org>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
|
||||
@ -31,7 +31,7 @@ Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
|
||||
NFTNL_EXPR_REDIR_FLAGS,
|
||||
--- a/include/linux/netfilter/nf_tables.h
|
||||
+++ b/include/linux/netfilter/nf_tables.h
|
||||
@@ -1508,6 +1508,22 @@ enum nft_masq_attributes {
|
||||
@@ -1510,6 +1510,22 @@ enum nft_masq_attributes {
|
||||
#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1)
|
||||
|
||||
/**
|
||||
|
||||
@ -6,12 +6,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libnl
|
||||
PKG_VERSION:=3.11.0
|
||||
PKG_VERSION:=3.12.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/thom311/libnl/releases/download/libnl$(subst .,_,$(PKG_VERSION))
|
||||
PKG_HASH:=2a56e1edefa3e68a7c00879496736fdbf62fc94ed3232c0baba127ecfa76874d
|
||||
PKG_HASH:=fc51ca7196f1a3f5fdf6ffd3864b50f4f9c02333be28be4eeca057e103c0dd18
|
||||
|
||||
PKG_LICENSE:=LGPL-2.1
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
@ -12,9 +12,9 @@ PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcp6c.git
|
||||
PKG_SOURCE_DATE:=2025-12-09
|
||||
PKG_SOURCE_VERSION:=5212a1019d75db47a03c95c12e385e4625dda3d0
|
||||
PKG_MIRROR_HASH:=9c6501ea995c685e4e1b65ae74cbaecb6f787a493fdd5f8d3d095aeb0466b418
|
||||
PKG_SOURCE_DATE:=2025-12-04
|
||||
PKG_SOURCE_VERSION:=f19dd37fb467c9cf10cad57aefa0d048312d7dfd
|
||||
PKG_MIRROR_HASH:=30b8e16589cc26d251e40feaad847b47529c19f2862c6396cf85c5e474c97576
|
||||
|
||||
PKG_MAINTAINER:=Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=iproute2
|
||||
PKG_VERSION:=6.17.0
|
||||
PKG_VERSION:=6.18.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=@KERNEL/linux/utils/net/iproute2
|
||||
PKG_HASH:=9781e59410ab7dea8e9f79bb10ff1488e63d10fcbb70503b94426ba27a8e2dec
|
||||
PKG_HASH:=6ba520e1975e4c50dc931eeae91ea37c198b8a173744885f8895b84325f9d456
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_DEPENDS:=iptables
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
From 7119f3736f689ab396ca4193ac593938affd55fa Mon Sep 17 00:00:00 2001
|
||||
From: Yureka <yureka@cyberchaos.dev>
|
||||
Date: Sun, 12 Oct 2025 14:39:47 +0200
|
||||
Subject: lib: bridge: avoid redefinition of in6_addr
|
||||
|
||||
On musl libc, which does not use the kernel definitions of in6_addr, including
|
||||
the libc headers after the kernel (UAPI) headers would cause a redefinition
|
||||
error. The opposite order avoids the redefinition.
|
||||
|
||||
Fixes: 9e89d5b94d749f37525cd8778311e1c9f28f172a
|
||||
Signed-off-by: Yureka <yureka@cyberchaos.dev>
|
||||
---
|
||||
lib/bridge.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/lib/bridge.c
|
||||
+++ b/lib/bridge.c
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
-#include "bridge.h"
|
||||
#include "utils.h"
|
||||
+#include "bridge.h"
|
||||
|
||||
void bridge_print_vlan_flags(__u16 flags)
|
||||
{
|
||||
@ -7,7 +7,7 @@
|
||||
-SUBDIRS=lib ip tc bridge misc netem genl man
|
||||
+SUBDIRS=lib ip tc bridge misc genl
|
||||
ifeq ($(HAVE_MNL),y)
|
||||
-SUBDIRS += tipc devlink rdma dcb vdpa
|
||||
-SUBDIRS += tipc devlink rdma dcb vdpa netshaper
|
||||
+SUBDIRS += devlink rdma
|
||||
endif
|
||||
|
||||
|
||||
@ -6,12 +6,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=nftables
|
||||
PKG_VERSION:=1.1.5
|
||||
PKG_VERSION:=1.1.6
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=https://netfilter.org/projects/$(PKG_NAME)/files
|
||||
PKG_HASH:=1daf10f322e14fd90a017538aaf2c034d7cc1eb1cc418ded47445d714ea168d4
|
||||
PKG_HASH:=372931bda8556b310636a2f9020adc710f9bab66f47efe0ce90bff800ac2530c
|
||||
|
||||
PKG_MAINTAINER:=
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@ -7,9 +7,9 @@ PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=https://github.com/immortalwrt/keyring.git
|
||||
PKG_SOURCE_DATE:=2024-11-11
|
||||
PKG_SOURCE_VERSION:=f034bf5e9e6a28ad8fcfd82245d7812f561f79ef
|
||||
PKG_MIRROR_HASH:=4d92c04801e6bf63bc9e76c377bf2a24ffd7e7493d886fc29ae408cf5e849be5
|
||||
PKG_SOURCE_DATE:=2025-12-11
|
||||
PKG_SOURCE_VERSION:=14e1f88b521f6466dfcc06b055646cfa25e8e02f
|
||||
PKG_MIRROR_HASH:=1928719f41b4aa2709829618e54a1dbd1322cdccf6bc56213354bf9c50017995
|
||||
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@ -12,9 +12,9 @@ PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=https://github.com/jow-/ucode.git
|
||||
PKG_SOURCE_DATE:=2025-11-19
|
||||
PKG_SOURCE_VERSION:=48ed18d2532e9197212c34473ab926c7b5e8ac73
|
||||
PKG_MIRROR_HASH:=34529706bcb413dffb3d73e78fe97971bd2b518c097c86470edadc1ca79a480c
|
||||
PKG_SOURCE_DATE:=2025-12-01
|
||||
PKG_SOURCE_VERSION:=f7c2b97a82e8b505bf4b2c0d8883b5116e1960f9
|
||||
PKG_MIRROR_HASH:=7de6a6d5b3c7392624bccefdc03e2dc61f097b414989eeced29ecc3e020bbd0b
|
||||
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
|
||||
PKG_LICENSE:=ISC
|
||||
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
From 165d395ffa2a22e293160b24d4791302a156eab8 Mon Sep 17 00:00:00 2001
|
||||
From: Rosen Penev <rosenp@gmail.com>
|
||||
Date: Mon, 8 Dec 2025 12:48:01 -0800
|
||||
Subject: add include for older kernels
|
||||
|
||||
in6.h is needed for some macros. Seems newer kernels include this implicitly.
|
||||
---
|
||||
lib/socket.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/lib/socket.c
|
||||
+++ b/lib/socket.c
|
||||
@@ -77,6 +77,7 @@
|
||||
#include "ucode/platform.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
+# include <linux/in6.h>
|
||||
# include <linux/if_packet.h>
|
||||
# include <linux/filter.h>
|
||||
|
||||
@ -97,21 +97,21 @@ def image_dump(tn, dumpfile):
|
||||
buf = tn.read_until("Returned 0",2)
|
||||
i = buf.find("Platform:")
|
||||
if i < 0:
|
||||
platform="jungo"
|
||||
else:
|
||||
line=buf[i+9:]
|
||||
i=line.find('\n')
|
||||
platform=line[:i].split()[-1]
|
||||
platform="jungo"
|
||||
else:
|
||||
line=buf[i+9:]
|
||||
i=line.find('\n')
|
||||
platform=line[:i].split()[-1]
|
||||
|
||||
tn.write("rg_conf_print /dev/%s/mac\n" % device);
|
||||
buf = tn.read_until("Returned 0",3)
|
||||
|
||||
i = buf.find("mac(")
|
||||
if i > 0:
|
||||
i += 4
|
||||
else:
|
||||
print("No MAC address found! (use -f option)")
|
||||
sys.exit(1)
|
||||
i = buf.find("mac(")
|
||||
if i > 0:
|
||||
i += 4
|
||||
else:
|
||||
print("No MAC address found! (use -f option)")
|
||||
sys.exit(1)
|
||||
dumpfile = "%s-%s.bin" % (platform, buf[i:i+17].replace(':',''))
|
||||
else:
|
||||
tn.write("\n")
|
||||
@ -121,31 +121,31 @@ def image_dump(tn, dumpfile):
|
||||
|
||||
t=flashsize/dumplen
|
||||
for addr in range(t):
|
||||
if verbose:
|
||||
sys.stdout.write('\r%d%%'%(100*addr/t))
|
||||
sys.stdout.flush()
|
||||
if verbose:
|
||||
sys.stdout.write('\r%d%%'%(100*addr/t))
|
||||
sys.stdout.flush()
|
||||
|
||||
tn.write("flash_dump -r 0x%x -l %d -4\n" % (addr*dumplen, dumplen))
|
||||
tn.read_until("\n")
|
||||
tn.read_until("\n")
|
||||
|
||||
count = addr*dumplen
|
||||
count = addr*dumplen
|
||||
while 1:
|
||||
buf = tn.read_until("\n")
|
||||
if buf.strip() == "Returned 0":
|
||||
break
|
||||
s = buf.split()
|
||||
if s and s[0][-1] == ':':
|
||||
a=int(s[0][:-1],16)
|
||||
if a != count:
|
||||
print("Format error: %x != %x"%(a,count))
|
||||
sys.exit(2)
|
||||
count += 16
|
||||
f.write(binascii.a2b_hex(string.join(s[1:],'')))
|
||||
tn.read_until(">",1)
|
||||
a=int(s[0][:-1],16)
|
||||
if a != count:
|
||||
print("Format error: %x != %x"%(a,count))
|
||||
sys.exit(2)
|
||||
count += 16
|
||||
f.write(binascii.a2b_hex(string.join(s[1:],'')))
|
||||
tn.read_until(">",1)
|
||||
|
||||
f.close()
|
||||
if verbose:
|
||||
print("")
|
||||
print("")
|
||||
|
||||
def telnet_option(sock,cmd,option):
|
||||
#print "Option: %d %d" % (ord(cmd), ord(option))
|
||||
@ -175,27 +175,27 @@ except getopt.GetoptError:
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
usage()
|
||||
sys.exit(1)
|
||||
usage()
|
||||
sys.exit(1)
|
||||
elif o in ("-V", "--version"):
|
||||
print("%s: 0.11" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
print("%s: 0.11" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
elif o in ("-d", "--no-dump"):
|
||||
do_dump = 1
|
||||
do_dump = 1
|
||||
elif o in ("-f", "--file"):
|
||||
dumpfile = a
|
||||
dumpfile = a
|
||||
elif o in ("-u", "--user"):
|
||||
user = a
|
||||
user = a
|
||||
elif o in ("-p", "--pass"):
|
||||
password = a
|
||||
password = a
|
||||
elif o == "--port":
|
||||
PORT = int(a)
|
||||
PORT = int(a)
|
||||
elif o in ("-q", "--quiet"):
|
||||
verbose = 0
|
||||
verbose = 0
|
||||
elif o in ("-r", "--reboot"):
|
||||
reboot = 1
|
||||
reboot = 1
|
||||
elif o in ("-v", "--verbose"):
|
||||
verbose = 1
|
||||
verbose = 1
|
||||
|
||||
# make sure we have enough arguments
|
||||
if len(args) > 0:
|
||||
@ -260,12 +260,12 @@ if imagefile or url:
|
||||
start_server(server)
|
||||
|
||||
if verbose:
|
||||
print("Unlocking flash...")
|
||||
print("Unlocking flash...")
|
||||
tn.write("unlock 0 0x%x\n" % flashsize)
|
||||
buf = tn.read_until("Returned 0",5)
|
||||
|
||||
if verbose:
|
||||
print("Writing new image...")
|
||||
print("Writing new image...")
|
||||
print(cmd, end=' ')
|
||||
tn.write(cmd)
|
||||
buf = tn.read_until("Returned 0",10)
|
||||
@ -273,7 +273,7 @@ if imagefile or url:
|
||||
# wait till the transfer completed
|
||||
buf = tn.read_until("Download completed successfully",20)
|
||||
if buf:
|
||||
print("Flash update complete!")
|
||||
print("Flash update complete!")
|
||||
if reboot:
|
||||
tn.write("reboot\n")
|
||||
print("Rebooting...")
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
From 954129f16c200e41a00ebebe2e22efc01b243538 Mon Sep 17 00:00:00 2001
|
||||
From: Dom Cobley <popcornmix@gmail.com>
|
||||
Date: Fri, 14 Nov 2025 14:08:18 +0000
|
||||
Subject: [PATCH] Revert "Revert "media: i2c: adv7180: Add support for
|
||||
V4L2_CID_LINK_FREQ""
|
||||
|
||||
This reverts commit 00ecb85c58501f8f7ae75fcb069bbbad5542e853.
|
||||
---
|
||||
drivers/media/i2c/adv7180.c | 39 +++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 37 insertions(+), 2 deletions(-)
|
||||
|
||||
From 954129f16c200e41a00ebebe2e22efc01b243538 Mon Sep 17 00:00:00 2001
|
||||
From: Dom Cobley <popcornmix@gmail.com>
|
||||
Date: Fri, 14 Nov 2025 14:08:18 +0000
|
||||
Subject: [PATCH] Revert "Revert "media: i2c: adv7180: Add support for
|
||||
V4L2_CID_LINK_FREQ""
|
||||
|
||||
This reverts commit 00ecb85c58501f8f7ae75fcb069bbbad5542e853.
|
||||
---
|
||||
drivers/media/i2c/adv7180.c | 39 +++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 37 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/media/i2c/adv7180.c
|
||||
+++ b/drivers/media/i2c/adv7180.c
|
||||
@@ -189,6 +189,16 @@
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
From 21368fcbb124d51b5d8bd8fa0a286a23c34a0888 Mon Sep 17 00:00:00 2001
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Date: Mon, 25 Aug 2025 10:28:21 +0200
|
||||
Subject: [PATCH] bitmap: introduce hardware-specific bitfield operations
|
||||
|
||||
Hardware of various vendors, but very notably Rockchip, often uses
|
||||
32-bit registers where the upper 16-bit half of the register is a
|
||||
write-enable mask for the lower half.
|
||||
|
||||
This type of hardware setup allows for more granular concurrent register
|
||||
write access.
|
||||
|
||||
Over the years, many drivers have hand-rolled their own version of this
|
||||
macro, usually without any checks, often called something like
|
||||
HIWORD_UPDATE or FIELD_PREP_HIWORD, commonly with slightly different
|
||||
semantics between them.
|
||||
|
||||
Clearly there is a demand for such a macro, and thus the demand should
|
||||
be satisfied in a common header file. As this is a convention that spans
|
||||
across multiple vendors, and similar conventions may also have
|
||||
cross-vendor adoption, it's best if it lives in a vendor-agnostic header
|
||||
file that can be expanded over time.
|
||||
|
||||
Add hw_bitfield.h with two macros: FIELD_PREP_WM16, and
|
||||
FIELD_PREP_WM16_CONST. The latter is a version that can be used in
|
||||
initializers, like FIELD_PREP_CONST.
|
||||
|
||||
Suggested-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Acked-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Acked-by: Heiko Stuebner <heiko@sntech.de>
|
||||
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
|
||||
---
|
||||
MAINTAINERS | 1 +
|
||||
include/linux/hw_bitfield.h | 62 +++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 63 insertions(+)
|
||||
create mode 100644 include/linux/hw_bitfield.h
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -3904,6 +3904,7 @@ F: include/linux/bits.h
|
||||
F: include/linux/cpumask.h
|
||||
F: include/linux/cpumask_types.h
|
||||
F: include/linux/find.h
|
||||
+F: include/linux/hw_bitfield.h
|
||||
F: include/linux/nodemask.h
|
||||
F: include/linux/nodemask_types.h
|
||||
F: include/vdso/bits.h
|
||||
--- /dev/null
|
||||
+++ b/include/linux/hw_bitfield.h
|
||||
@@ -0,0 +1,62 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2025, Collabora Ltd.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _LINUX_HW_BITFIELD_H
|
||||
+#define _LINUX_HW_BITFIELD_H
|
||||
+
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/build_bug.h>
|
||||
+#include <linux/limits.h>
|
||||
+
|
||||
+/**
|
||||
+ * FIELD_PREP_WM16() - prepare a bitfield element with a mask in the upper half
|
||||
+ * @_mask: shifted mask defining the field's length and position
|
||||
+ * @_val: value to put in the field
|
||||
+ *
|
||||
+ * FIELD_PREP_WM16() masks and shifts up the value, as well as bitwise ORs the
|
||||
+ * result with the mask shifted up by 16.
|
||||
+ *
|
||||
+ * This is useful for a common design of hardware registers where the upper
|
||||
+ * 16-bit half of a 32-bit register is used as a write-enable mask. In such a
|
||||
+ * register, a bit in the lower half is only updated if the corresponding bit
|
||||
+ * in the upper half is high.
|
||||
+ */
|
||||
+#define FIELD_PREP_WM16(_mask, _val) \
|
||||
+ ({ \
|
||||
+ typeof(_val) __val = _val; \
|
||||
+ typeof(_mask) __mask = _mask; \
|
||||
+ __BF_FIELD_CHECK(__mask, ((u16)0U), __val, \
|
||||
+ "HWORD_UPDATE: "); \
|
||||
+ (((typeof(__mask))(__val) << __bf_shf(__mask)) & (__mask)) | \
|
||||
+ ((__mask) << 16); \
|
||||
+ })
|
||||
+
|
||||
+/**
|
||||
+ * FIELD_PREP_WM16_CONST() - prepare a constant bitfield element with a mask in
|
||||
+ * the upper half
|
||||
+ * @_mask: shifted mask defining the field's length and position
|
||||
+ * @_val: value to put in the field
|
||||
+ *
|
||||
+ * FIELD_PREP_WM16_CONST() masks and shifts up the value, as well as bitwise ORs
|
||||
+ * the result with the mask shifted up by 16.
|
||||
+ *
|
||||
+ * This is useful for a common design of hardware registers where the upper
|
||||
+ * 16-bit half of a 32-bit register is used as a write-enable mask. In such a
|
||||
+ * register, a bit in the lower half is only updated if the corresponding bit
|
||||
+ * in the upper half is high.
|
||||
+ *
|
||||
+ * Unlike FIELD_PREP_WM16(), this is a constant expression and can therefore
|
||||
+ * be used in initializers. Error checking is less comfortable for this
|
||||
+ * version.
|
||||
+ */
|
||||
+#define FIELD_PREP_WM16_CONST(_mask, _val) \
|
||||
+ ( \
|
||||
+ FIELD_PREP_CONST(_mask, _val) | \
|
||||
+ (BUILD_BUG_ON_ZERO(const_true((u64)(_mask) > U16_MAX)) + \
|
||||
+ ((_mask) << 16)) \
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+#endif /* _LINUX_HW_BITFIELD_H */
|
||||
@ -29,7 +29,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -14427,8 +14427,8 @@ M: Qingfang Deng <dqfext@gmail.com>
|
||||
@@ -14428,8 +14428,8 @@ M: Qingfang Deng <dqfext@gmail.com>
|
||||
M: SkyLake Huang <SkyLake.Huang@mediatek.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
@ -24,7 +24,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -14428,7 +14428,9 @@ M: SkyLake Huang <SkyLake.Huang@mediatek
|
||||
@@ -14429,7 +14429,9 @@ M: SkyLake Huang <SkyLake.Huang@mediatek
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/phy/mediatek/mtk-ge-soc.c
|
||||
|
||||
@ -4244,6 +4244,11 @@ CONFIG_NET_SOCK_MSG=y
|
||||
CONFIG_NET_SWITCHDEV=y
|
||||
# CONFIG_NET_TC_SKB_EXT is not set
|
||||
# CONFIG_NET_TEAM is not set
|
||||
# CONFIG_NET_TEAM_MODE_ACTIVEBACKUP is not set
|
||||
# CONFIG_NET_TEAM_MODE_BROADCAST is not set
|
||||
# CONFIG_NET_TEAM_MODE_LOADBALANCE is not set
|
||||
# CONFIG_NET_TEAM_MODE_RANDOM is not set
|
||||
# CONFIG_NET_TEAM_MODE_ROUNDROBIN is not set
|
||||
# CONFIG_NET_TULIP is not set
|
||||
# CONFIG_NET_UDP_TUNNEL is not set
|
||||
CONFIG_NET_VENDOR_3COM=y
|
||||
|
||||
@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -23668,6 +23668,12 @@ F: Documentation/filesystems/ubifs-authe
|
||||
@@ -23669,6 +23669,12 @@ F: Documentation/filesystems/ubifs-authe
|
||||
F: Documentation/filesystems/ubifs.rst
|
||||
F: fs/ubifs/
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -14419,7 +14419,9 @@ M: Daniel Golle <daniel@makrotopia.org>
|
||||
@@ -14420,7 +14420,9 @@ M: Daniel Golle <daniel@makrotopia.org>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
|
||||
@ -52,7 +52,7 @@ CONFIG_ARCH_MMAP_RND_BITS_MIN=12
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_ARCH_SPARSEMEM_ENABLE=y
|
||||
CONFIG_ARCH_STACKWALK=y
|
||||
CONFIG_ARCH_STRICT_ALIGN=y
|
||||
# CONFIG_ARCH_STRICT_ALIGN is not set
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARCH_WANTS_NO_INSTR=y
|
||||
CONFIG_ARCH_WANTS_THP_SWAP=y
|
||||
|
||||
@ -29,7 +29,8 @@ ocedo,panda)
|
||||
tplink,tl-wdr4900-v1)
|
||||
ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "wan"
|
||||
;;
|
||||
watchguard,firebox-t10)
|
||||
watchguard,firebox-t10|\
|
||||
watchguard,firebox-t15)
|
||||
ucidef_set_interfaces_lan_wan "eth1 eth2" "eth0"
|
||||
;;
|
||||
*)
|
||||
|
||||
@ -16,7 +16,8 @@ platform_do_upgrade() {
|
||||
hpe,msm460|\
|
||||
ocedo,panda|\
|
||||
sophos,red-15w-rev1|\
|
||||
watchguard,firebox-t10)
|
||||
watchguard,firebox-t10|\
|
||||
watchguard,firebox-t15)
|
||||
nand_do_upgrade "$1"
|
||||
;;
|
||||
*)
|
||||
|
||||
@ -71,7 +71,7 @@ CONFIG_EDAC_LEGACY_SYSFS=y
|
||||
CONFIG_EDAC_MPC85XX=y
|
||||
CONFIG_EDAC_SUPPORT=y
|
||||
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
|
||||
# CONFIG_FIREBOX_T10 is not set
|
||||
# CONFIG_FIREBOX_T1X is not set
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FORCE_NR_CPUS=y
|
||||
CONFIG_FSL_EMB_PERFMON=y
|
||||
|
||||
@ -1,229 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later or MIT
|
||||
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
/include/ "fsl/p1010si-pre.dtsi"
|
||||
#include "firebox-t1x.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Watchguard Firebox T10";
|
||||
compatible = "watchguard,firebox-t10";
|
||||
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200";
|
||||
bootargs-override = "console=ttyS0,115200";
|
||||
};
|
||||
|
||||
aliases {
|
||||
spi0 = &spi0;
|
||||
led-boot = &led_mode;
|
||||
led-failsafe = &led_failover;
|
||||
led-running = &led_mode;
|
||||
led-upgrade = &led_attention;
|
||||
/delete-property/ ethernet0;
|
||||
/delete-property/ ethernet1;
|
||||
/delete-property/ ethernet2;
|
||||
};
|
||||
|
||||
memory {
|
||||
device_type = "memory";
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
led_attention: attention_orange {
|
||||
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
|
||||
label = "orange:attention";
|
||||
};
|
||||
|
||||
status_red {
|
||||
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led_mode: mode_green {
|
||||
gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
|
||||
label = "green:mode";
|
||||
};
|
||||
|
||||
led_failover: failover_green {
|
||||
gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
||||
label = "green:failover";
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
label = "Reset button";
|
||||
gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
|
||||
linux,code = <KEY_RESTART>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
soc: soc@ffe00000 {
|
||||
ranges = <0x0 0x0 0xffe00000 0x100000>;
|
||||
|
||||
i2c@3000 {
|
||||
rtc@30 {
|
||||
compatible = "sii,s35390a";
|
||||
reg = <0x30>;
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@7000 {
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
uboot: partition@0 {
|
||||
reg = <0x0 0x90000>;
|
||||
label = "u-boot";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@90000 {
|
||||
reg = <0x90000 0x10000>;
|
||||
label = "u-boot-env";
|
||||
};
|
||||
|
||||
partition@a0000 {
|
||||
reg = <0xa0000 0x20000>;
|
||||
label = "cfgxxx";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@c0000 {
|
||||
reg = <0xc0000 0x40000>;
|
||||
label = "device_id";
|
||||
read-only;
|
||||
|
||||
nvmem-layout {
|
||||
compatible = "fixed-layout";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
macaddr_device_id_1830: mac-address@1830 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1830 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
|
||||
macaddr_device_id_1844: mac-address@1844 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1844 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
|
||||
macaddr_device_id_1858: mac-address@1858 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1858 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gpio0: gpio-controller@fc00 {
|
||||
};
|
||||
|
||||
usb@22000 {
|
||||
phy_type = "utmi";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
mdio@24000 {
|
||||
phy1: ethernet-phy@1 {
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
phy2: ethernet-phy@2 {
|
||||
reg = <0x2>;
|
||||
};
|
||||
|
||||
phy3: ethernet-phy@3 {
|
||||
reg = <0x3>;
|
||||
};
|
||||
};
|
||||
|
||||
mdio@25000 {
|
||||
tbi_phy1: tbi-phy@11 {
|
||||
reg = <0x11>;
|
||||
device_type = "tbi-phy";
|
||||
};
|
||||
};
|
||||
|
||||
mdio@26000 {
|
||||
tbi_phy2: tbi-phy@11 {
|
||||
reg = <0x11>;
|
||||
device_type = "tbi-phy";
|
||||
};
|
||||
};
|
||||
|
||||
enet0: ethernet@b0000 {
|
||||
phy-handle = <&phy1>;
|
||||
phy-connection-type = "rgmii-id";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1830 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
enet1: ethernet@b1000 {
|
||||
tbi-handle = <&tbi_phy1>;
|
||||
phy-handle = <&phy2>;
|
||||
phy-connection-type = "sgmii";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1844 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
enet2: ethernet@b2000 {
|
||||
tbi-handle = <&tbi_phy2>;
|
||||
phy-handle = <&phy3>;
|
||||
phy-connection-type = "sgmii";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1858 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
sdhc@2e000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
serial1: serial@4600 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can0: can@1c000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can1: can@1d000 {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
pci0: pcie@ffe09000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pci1: pcie@ffe0a000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ifc: ifc@ffe1e000 {
|
||||
reg = <0x0 0xffe1e000 0 0x2000>;
|
||||
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later or MIT
|
||||
|
||||
/include/ "fsl/p1010si-pre.dtsi"
|
||||
#include "firebox-t1x.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Watchguard Firebox T15";
|
||||
compatible = "watchguard,firebox-t15";
|
||||
|
||||
ifc: ifc@ffe1e000 {
|
||||
reg = <0x0 0xffe1e000 0 0x2000>;
|
||||
|
||||
/* NOR, NAND Flashes and CPLD on board */
|
||||
ranges = <0x0 0x0 0x0 0xee000000 0x02000000
|
||||
0x1 0x0 0x0 0xff800000 0x00010000
|
||||
0x3 0x0 0x0 0xffb00000 0x00000020>;
|
||||
|
||||
nand@100000000 {
|
||||
compatible = "fsl,ifc-nand";
|
||||
reg = <0x1 0x0 0x10000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
/*
|
||||
* Original partition layout:
|
||||
* 0x000000000000-0x000000020000 : "NAND (RW) WG DTB Image"
|
||||
* 0x000000020000-0x000000520000 : "NAND (RW) WG SYSA Kernel"
|
||||
* 0x000000520000-0x00000f720000 : "NAND (RW) WG SYSA_CODE"
|
||||
* 0x00000f720000-0x00000fc20000 : "NAND (RW) WG SYSB Kernel"
|
||||
* 0x00000fc20000-0x000011420000 : "NAND (RW) WG SYSB_CODE"
|
||||
* 0x000011420000-0x000011920000 : "NAND (RW) WG SYSA2 Kernel"
|
||||
* 0x000011920000-0x000019220000 : "NAND (RW) WG SYSA_CODE2"
|
||||
* 0x000019220000-0x000040000000 : "NAND (RW) WG SYSA_DATA"
|
||||
*/
|
||||
|
||||
partition@0 {
|
||||
reg = <0x0 0x20000>;
|
||||
label = "wg-dtb";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@20000 {
|
||||
reg = <0x20000 0x500000>;
|
||||
label = "kernel";
|
||||
};
|
||||
|
||||
partition@520000 {
|
||||
reg = <0x520000 0xf200000>;
|
||||
label = "wg-sysa-rootfs";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@f720000 {
|
||||
reg = <0xf720000 0x500000>;
|
||||
label = "wg-sysb-kernel";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@fc20000 {
|
||||
reg = <0xfc20000 0x1800000>;
|
||||
label = "wg-sysb-rootfs";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@11420000 {
|
||||
reg = <0x11420000 0x500000>;
|
||||
label = "wg-sysa2-kernel";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@11920000 {
|
||||
reg = <0x11920000 0x7900000>;
|
||||
label = "wg-sysa2-rootfs";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@19220000 {
|
||||
reg = <0x19220000 0x26de0000>;
|
||||
label = "ubi";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/include/ "fsl/p1010si-post.dtsi"
|
||||
@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later or MIT
|
||||
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200";
|
||||
bootargs-override = "console=ttyS0,115200";
|
||||
};
|
||||
|
||||
aliases {
|
||||
spi0 = &spi0;
|
||||
led-boot = &led_mode;
|
||||
led-failsafe = &led_failover;
|
||||
led-running = &led_mode;
|
||||
led-upgrade = &led_attention;
|
||||
/delete-property/ ethernet0;
|
||||
/delete-property/ ethernet1;
|
||||
/delete-property/ ethernet2;
|
||||
};
|
||||
|
||||
memory {
|
||||
device_type = "memory";
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
led_attention: attention_orange {
|
||||
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
|
||||
label = "orange:attention";
|
||||
};
|
||||
|
||||
status_red {
|
||||
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
|
||||
function = LED_FUNCTION_STATUS;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led_mode: mode_green {
|
||||
gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
|
||||
label = "green:mode";
|
||||
};
|
||||
|
||||
led_failover: failover_green {
|
||||
gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
||||
label = "green:failover";
|
||||
};
|
||||
|
||||
led_wap_orange: wap_orange {
|
||||
gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
|
||||
function = LED_FUNCTION_WLAN_2GHZ;
|
||||
color = <LED_COLOR_ID_ORANGE>;
|
||||
label = "orange:wap";
|
||||
};
|
||||
|
||||
led_wap_green: wap_green {
|
||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
function = LED_FUNCTION_WLAN_5GHZ;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
label = "green:wap";
|
||||
linux,default-trigger = "phy0tpt";
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
label = "Reset button";
|
||||
gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
|
||||
linux,code = <KEY_RESTART>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
soc: soc@ffe00000 {
|
||||
ranges = <0x0 0x0 0xffe00000 0x100000>;
|
||||
|
||||
i2c@3000 {
|
||||
rtc@30 {
|
||||
compatible = "sii,s35390a";
|
||||
reg = <0x30>;
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@7000 {
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
uboot: partition@0 {
|
||||
reg = <0x0 0x90000>;
|
||||
label = "u-boot";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@90000 {
|
||||
reg = <0x90000 0x10000>;
|
||||
label = "u-boot-env";
|
||||
};
|
||||
|
||||
partition@a0000 {
|
||||
reg = <0xa0000 0x20000>;
|
||||
label = "cfgxxx";
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@c0000 {
|
||||
reg = <0xc0000 0x40000>;
|
||||
label = "device_id";
|
||||
read-only;
|
||||
|
||||
nvmem-layout {
|
||||
compatible = "fixed-layout";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
macaddr_device_id_1830: mac-address@1830 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1830 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
|
||||
macaddr_device_id_1844: mac-address@1844 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1844 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
|
||||
macaddr_device_id_1858: mac-address@1858 {
|
||||
compatible = "mac-base";
|
||||
reg = <0x1858 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
macaddr_device_id_186c: mac-address@186c {
|
||||
compatible = "mac-base";
|
||||
reg = <0x186c 0x11>;
|
||||
#nvmem-cell-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gpio0: gpio-controller@fc00 {
|
||||
};
|
||||
|
||||
usb@22000 {
|
||||
phy_type = "utmi";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
mdio@24000 {
|
||||
phy1: ethernet-phy@1 {
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
phy2: ethernet-phy@2 {
|
||||
reg = <0x2>;
|
||||
};
|
||||
|
||||
phy3: ethernet-phy@3 {
|
||||
reg = <0x3>;
|
||||
};
|
||||
};
|
||||
|
||||
mdio@25000 {
|
||||
tbi_phy1: tbi-phy@11 {
|
||||
reg = <0x11>;
|
||||
device_type = "tbi-phy";
|
||||
};
|
||||
};
|
||||
|
||||
mdio@26000 {
|
||||
tbi_phy2: tbi-phy@11 {
|
||||
reg = <0x11>;
|
||||
device_type = "tbi-phy";
|
||||
};
|
||||
};
|
||||
|
||||
enet0: ethernet@b0000 {
|
||||
phy-handle = <&phy1>;
|
||||
phy-connection-type = "rgmii-id";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1830 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
enet1: ethernet@b1000 {
|
||||
tbi-handle = <&tbi_phy1>;
|
||||
phy-handle = <&phy2>;
|
||||
phy-connection-type = "sgmii";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1844 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
enet2: ethernet@b2000 {
|
||||
tbi-handle = <&tbi_phy2>;
|
||||
phy-handle = <&phy3>;
|
||||
phy-connection-type = "sgmii";
|
||||
|
||||
nvmem-cells = <&macaddr_device_id_1858 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
|
||||
sdhc@2e000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
serial1: serial@4600 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can0: can@1c000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can1: can@1d000 {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
pci0: pcie@ffe09000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pci1: pcie@ffe0a000 {
|
||||
reg = <0 0xffe0a000 0 0x1000>;
|
||||
ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000
|
||||
0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>;
|
||||
pcie@0 {
|
||||
ranges = <0x2000000 0x0 0x80000000
|
||||
0x2000000 0x0 0x80000000
|
||||
0x0 0x20000000
|
||||
|
||||
0x1000000 0x0 0x0
|
||||
0x1000000 0x0 0x0
|
||||
0x0 0x100000>;
|
||||
|
||||
ath9k: wifi@0,0 {
|
||||
compatible = "pci168c,0033";
|
||||
reg = <0x0000 0 0 0 0>;
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
nvmem-cells = <&macaddr_device_id_186c 0>;
|
||||
nvmem-cell-names = "mac-address";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
#include "mpc85xx.h"
|
||||
|
||||
static void __init firebox_t10_pic_init(void)
|
||||
static void __init firebox_t1x_pic_init(void)
|
||||
{
|
||||
struct mpic *mpic;
|
||||
|
||||
@ -49,26 +49,40 @@ static void __init firebox_t10_pic_init(void)
|
||||
/*
|
||||
* Setup the architecture
|
||||
*/
|
||||
static void __init firebox_t10_setup_arch(void)
|
||||
static void __init firebox_t1x_setup_arch(void)
|
||||
{
|
||||
if (ppc_md.progress)
|
||||
ppc_md.progress("firebox_t10_setup_arch()", 0);
|
||||
ppc_md.progress("firebox_t1x_setup_arch()", 0);
|
||||
|
||||
fsl_pci_assign_primary();
|
||||
|
||||
pr_info("Firebox T10 from Watchguard\n");
|
||||
pr_info("Firebox T10/T15 from Watchguard\n");
|
||||
}
|
||||
|
||||
machine_arch_initcall(firebox_t10, mpc85xx_common_publish_devices);
|
||||
machine_arch_initcall(firebox_t15, mpc85xx_common_publish_devices);
|
||||
|
||||
define_machine(firebox_t10) {
|
||||
.name = "P1010 RDB",
|
||||
.compatible = "watchguard,firebox-t10",
|
||||
.setup_arch = firebox_t10_setup_arch,
|
||||
.init_IRQ = firebox_t10_pic_init,
|
||||
.setup_arch = firebox_t1x_setup_arch,
|
||||
.init_IRQ = firebox_t1x_pic_init,
|
||||
#ifdef CONFIG_PCI
|
||||
.pcibios_fixup_bus = fsl_pcibios_fixup_bus,
|
||||
.pcibios_fixup_phb = fsl_pcibios_fixup_phb,
|
||||
.pcibios_fixup_phb = fsl_pcibios_fixup_phb,
|
||||
#endif
|
||||
.get_irq = mpic_get_irq,
|
||||
.progress = udbg_progress,
|
||||
};
|
||||
|
||||
define_machine(firebox_t15) {
|
||||
.name = "P1010 RDB",
|
||||
.compatible = "watchguard,firebox-t15",
|
||||
.setup_arch = firebox_t1x_setup_arch,
|
||||
.init_IRQ = firebox_t1x_pic_init,
|
||||
#ifdef CONFIG_PCI
|
||||
.pcibios_fixup_bus = fsl_pcibios_fixup_bus,
|
||||
.pcibios_fixup_phb = fsl_pcibios_fixup_phb,
|
||||
#endif
|
||||
.get_irq = mpic_get_irq,
|
||||
.progress = udbg_progress,
|
||||
@ -77,13 +77,37 @@ TARGET_DEVICES += tplink_tl-wdr4900-v1
|
||||
define Device/watchguard_firebox-t10
|
||||
DEVICE_VENDOR := Watchguard
|
||||
DEVICE_MODEL := Firebox T10
|
||||
DEVICE_ALT0_VENDOR := Watchguard
|
||||
DEVICE_ALT0_MODEL := Firebox T10-W
|
||||
DEVICE_PACKAGES := kmod-rtc-s35390a kmod-eeprom-at24
|
||||
# This boot loader doesn't reliably boot an uncompressed image,
|
||||
# therefore resort to gzipping the already compressed zImage
|
||||
KERNEL = kernel-bin | gzip | fit gzip $(KDIR)/image-$$(DEVICE_DTS).dtb
|
||||
KERNEL_NAME := zImage.la3000000
|
||||
KERNEL_ENTRY := 0x3000000
|
||||
KERNEL_LOADADDR := 0x3000000
|
||||
IMAGES := sysupgrade.bin
|
||||
IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
|
||||
endef
|
||||
TARGET_DEVICES += watchguard_firebox-t10
|
||||
|
||||
define Device/watchguard_firebox-t15
|
||||
DEVICE_VENDOR := Watchguard
|
||||
DEVICE_MODEL := Firebox T15
|
||||
DEVICE_ALT0_VENDOR := Watchguard
|
||||
DEVICE_ALT0_MODEL := Firebox T15-W
|
||||
DEVICE_PACKAGES := kmod-rtc-s35390a kmod-eeprom-at24
|
||||
# This boot loader doesn't reliably boot an uncompressed image,
|
||||
# therefore resort to gzipping the already compressed zImage
|
||||
KERNEL = kernel-bin | gzip | fit gzip $(KDIR)/image-$$(DEVICE_DTS).dtb
|
||||
KERNEL_NAME := zImage.la3000000
|
||||
KERNEL_ENTRY := 0x3000000
|
||||
KERNEL_LOADADDR := 0x3000000
|
||||
IMAGES := sysupgrade.bin
|
||||
IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
|
||||
endef
|
||||
TARGET_DEVICES += watchguard_firebox-t15
|
||||
|
||||
define Device/sophos_red-15w-rev1
|
||||
DEVICE_VENDOR := Sophos
|
||||
DEVICE_MODEL := RED 15w
|
||||
|
||||
@ -6,7 +6,7 @@ CONFIG_CRYPTO_HASH_INFO=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_ZSTD=y
|
||||
CONFIG_DEFAULT_UIMAGE=y
|
||||
CONFIG_FIREBOX_T10=y
|
||||
CONFIG_FIREBOX_T1X=y
|
||||
# CONFIG_FSL_CORENET_CF is not set
|
||||
CONFIG_FSL_IFC=y
|
||||
CONFIG_GPIO_74X164=y
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
BOARDNAME:=P1010
|
||||
KERNEL_IMAGES:=simpleImage.br200-wp simpleImage.tl-wdr4900-v1 simpleImage.ws-ap3715i
|
||||
KERNEL_IMAGES:=simpleImage.br200-wp simpleImage.tl-wdr4900-v1 simpleImage.ws-ap3715i zImage.la3000000
|
||||
|
||||
define Target/Description
|
||||
Build firmware images for P1010 based boards.
|
||||
|
||||
@ -4,15 +4,15 @@
|
||||
This board is a Concurrent Dual-Band wireless access point with a
|
||||
Freescale P1020 SoC.
|
||||
|
||||
+config FIREBOX_T10
|
||||
+ bool "Watchguard Firebox T10"
|
||||
+config FIREBOX_T1X
|
||||
+ bool "Watchguard Firebox T10/T15"
|
||||
+ select DEFAULT_UIMAGE
|
||||
+ select ARCH_REQUIRE_GPIOLIB
|
||||
+ select GPIO_MPC8XXX
|
||||
+ select PPC_ZIMAGE_LA3000000
|
||||
+ help
|
||||
+ This option enables support for the Watchguard Firebox T10 board.
|
||||
+ This board is a VPN Gateway-Router with a
|
||||
+ Freescale P1010 SoC.
|
||||
+ This option enables support for the Watchguard Firebox T10/T15 board.
|
||||
+ This board is a VPN Gateway-Router with a Freescale P1010 SoC.
|
||||
+
|
||||
config MPC8540_ADS
|
||||
bool "Freescale MPC8540 ADS"
|
||||
@ -23,7 +23,7 @@
|
||||
obj-$(CONFIG_TWR_P102x) += twr_p102x.o
|
||||
obj-$(CONFIG_WS_AP3710I) += ws-ap3710i.o
|
||||
obj-$(CONFIG_WS_AP3825I) += ws-ap3825i.o
|
||||
+obj-$(CONFIG_FIREBOX_T10) += firebox_t10.o
|
||||
+obj-$(CONFIG_FIREBOX_T1X) += firebox_t1x.o
|
||||
obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o
|
||||
obj-$(CONFIG_FB_FSL_DIU) += t1042rdb_diu.o
|
||||
obj-$(CONFIG_RED_15W_REV1) += red15w_rev1.o
|
||||
@ -26,7 +26,7 @@
|
||||
obj-$(CONFIG_WS_AP3710I) += ws-ap3710i.o
|
||||
+obj-$(CONFIG_WS_AP3715I) += ws-ap3715i.o
|
||||
obj-$(CONFIG_WS_AP3825I) += ws-ap3825i.o
|
||||
obj-$(CONFIG_FIREBOX_T10) += firebox_t10.o
|
||||
obj-$(CONFIG_FIREBOX_T1X) += firebox_t1x.o
|
||||
obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o
|
||||
--- a/arch/powerpc/boot/Makefile
|
||||
+++ b/arch/powerpc/boot/Makefile
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
--- a/arch/powerpc/platforms/85xx/Kconfig
|
||||
+++ b/arch/powerpc/platforms/85xx/Kconfig
|
||||
@@ -114,6 +114,18 @@ config FIREBOX_T10
|
||||
This board is a VPN Gateway-Router with a
|
||||
Freescale P1010 SoC.
|
||||
@@ -114,6 +114,18 @@ config FIREBOX_T1X
|
||||
This option enables support for the Watchguard Firebox T10/T15 board.
|
||||
This board is a VPN Gateway-Router with a Freescale P1010 SoC.
|
||||
|
||||
+config MSM460
|
||||
+ bool "HPE MSM460"
|
||||
|
||||
@ -9,6 +9,7 @@ rockchip_setup_interfaces()
|
||||
case "$board" in
|
||||
9tripod,x3568-v4|\
|
||||
ariaboard,photonicat|\
|
||||
ariaboard,photonicat2|\
|
||||
armsom,sige7|\
|
||||
ezpro,mrkaio-m68s|\
|
||||
firefly,rk3568-roc-pc|\
|
||||
@ -93,6 +94,7 @@ rockchip_setup_macs()
|
||||
local label_mac=""
|
||||
|
||||
case "$board" in
|
||||
ariaboard,photonicat2|\
|
||||
armsom,sige3|\
|
||||
armsom,sige7|\
|
||||
cyber,cyber3588-aib|\
|
||||
|
||||
@ -31,6 +31,7 @@ set_interface_core() {
|
||||
case "$(board_name)" in
|
||||
9tripod,x3568-v4|\
|
||||
ariaboard,photonicat|\
|
||||
ariaboard,photonicat2|\
|
||||
armsom,sige3|\
|
||||
armsom,sige7|\
|
||||
ezpro,mrkaio-m68s|\
|
||||
|
||||
@ -44,6 +44,7 @@ boot() {
|
||||
netled_set "stmmac-0:01:amber:lan" "eth0" "link_10 link_100 link_1000" "0" "rx tx" "1"
|
||||
netled_set "stmmac-0:01:green:lan" "eth0" "link_10 link_100 link_1000" "1" "rx tx" "0"
|
||||
;;
|
||||
ariaboard,photonicat2|\
|
||||
linkease,easepi-r1)
|
||||
netled_set "stmmac-0:01:amber:wan" "eth0" "link_10 link_100 link_1000" "0" "rx tx" "1"
|
||||
netled_set "stmmac-0:01:green:wan" "eth0" "link_10 link_100 link_1000" "1" "rx tx" "0"
|
||||
|
||||
@ -413,6 +413,7 @@ CONFIG_MFD_CORE=y
|
||||
CONFIG_MFD_RK8XX=y
|
||||
CONFIG_MFD_RK8XX_I2C=y
|
||||
CONFIG_MFD_RK8XX_SPI=y
|
||||
CONFIG_MFD_ROCKCHIP_MFPWM=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MIGRATION=y
|
||||
CONFIG_MMC=y
|
||||
@ -571,6 +572,7 @@ CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ROCKCHIP=y
|
||||
CONFIG_PWM_ROCKCHIP_V4=y
|
||||
# CONFIG_QFMT_V2 is not set
|
||||
CONFIG_QUEUED_RWLOCKS=y
|
||||
CONFIG_QUEUED_SPINLOCKS=y
|
||||
@ -615,6 +617,7 @@ CONFIG_ROCKCHIP_MBOX=y
|
||||
CONFIG_ROCKCHIP_MFPWM=y
|
||||
CONFIG_ROCKCHIP_PHY=y
|
||||
CONFIG_ROCKCHIP_PM_DOMAINS=y
|
||||
CONFIG_ROCKCHIP_PWM_CAPTURE=y
|
||||
# CONFIG_ROCKCHIP_SARADC is not set
|
||||
CONFIG_ROCKCHIP_THERMAL=y
|
||||
CONFIG_ROCKCHIP_TIMER=y
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -90,6 +90,15 @@ define Device/ariaboard_photonicat
|
||||
endef
|
||||
TARGET_DEVICES += ariaboard_photonicat
|
||||
|
||||
define Device/ariaboard_photonicat2
|
||||
$(Device/rk3576)
|
||||
DEVICE_VENDOR := Ariaboard
|
||||
DEVICE_MODEL := Photonicat2
|
||||
DEVICE_PACKAGES := kmod-aic8800-usb wpad-openssl kmod-usb-net-cdc-mbim \
|
||||
kmod-usb-net-qmi-wwan kmod-usb-serial-option uqmi
|
||||
endef
|
||||
TARGET_DEVICES += ariaboard_photonicat2
|
||||
|
||||
define Device/armsom_sige3
|
||||
$(Device/rk3568)
|
||||
DEVICE_VENDOR := ArmSoM
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
From 1cc2e1faafb3b5a2be25112559bdb495736b5af7 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Fri, 20 Sep 2024 10:57:57 +0200
|
||||
Subject: [PATCH] pwm: Add more locking
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This ensures that a pwm_chip that has no corresponding driver isn't used
|
||||
and that a driver doesn't go away while a callback is still running.
|
||||
|
||||
In the presence of device links this isn't necessary yet (so this is no
|
||||
fix) but for pwm character device support this is needed.
|
||||
|
||||
To not serialize all pwm_apply_state() calls, this introduces a per chip
|
||||
lock. An additional complication is that for atomic chips a mutex cannot
|
||||
be used (as pwm_apply_atomic() must not sleep) and a spinlock cannot be
|
||||
held while calling an operation for a sleeping chip. So depending on the
|
||||
chip being atomic or not a spinlock or a mutex is used.
|
||||
|
||||
An additional change implemented here is that on driver remove the
|
||||
.free() callback is called for each requested pwm_device. This is the
|
||||
right time because later (e.g. when the consumer calls pwm_put()) the
|
||||
free function is (maybe) not available any more.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/026aa891c8270a11723a1ba7e4256f456f7e1e86.1726819463.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 100 ++++++++++++++++++++++++++++++++++++++++----
|
||||
include/linux/pwm.h | 13 ++++++
|
||||
2 files changed, 105 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -31,6 +31,24 @@ static DEFINE_MUTEX(pwm_lock);
|
||||
|
||||
static DEFINE_IDR(pwm_chips);
|
||||
|
||||
+static void pwmchip_lock(struct pwm_chip *chip)
|
||||
+{
|
||||
+ if (chip->atomic)
|
||||
+ spin_lock(&chip->atomic_lock);
|
||||
+ else
|
||||
+ mutex_lock(&chip->nonatomic_lock);
|
||||
+}
|
||||
+
|
||||
+static void pwmchip_unlock(struct pwm_chip *chip)
|
||||
+{
|
||||
+ if (chip->atomic)
|
||||
+ spin_unlock(&chip->atomic_lock);
|
||||
+ else
|
||||
+ mutex_unlock(&chip->nonatomic_lock);
|
||||
+}
|
||||
+
|
||||
+DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T))
|
||||
+
|
||||
static void pwm_apply_debug(struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
@@ -224,6 +242,7 @@ static int __pwm_apply(struct pwm_device
|
||||
int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
|
||||
/*
|
||||
* Some lowlevel driver's implementations of .apply() make use of
|
||||
@@ -234,7 +253,12 @@ int pwm_apply_might_sleep(struct pwm_dev
|
||||
*/
|
||||
might_sleep();
|
||||
|
||||
- if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm->chip->atomic) {
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) {
|
||||
/*
|
||||
* Catch any drivers that have been marked as atomic but
|
||||
* that will sleep anyway.
|
||||
@@ -258,9 +282,16 @@ EXPORT_SYMBOL_GPL(pwm_apply_might_sleep)
|
||||
*/
|
||||
int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
- WARN_ONCE(!pwm->chip->atomic,
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+
|
||||
+ WARN_ONCE(!chip->atomic,
|
||||
"sleeping PWM driver used in atomic context\n");
|
||||
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
return __pwm_apply(pwm, state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_apply_atomic);
|
||||
@@ -338,8 +369,18 @@ static int pwm_capture(struct pwm_device
|
||||
if (!ops->capture)
|
||||
return -ENOSYS;
|
||||
|
||||
+ /*
|
||||
+ * Holding the pwm_lock is probably not needed. If you use pwm_capture()
|
||||
+ * and you're interested to speed it up, please convince yourself it's
|
||||
+ * really not needed, test and then suggest a patch on the mailing list.
|
||||
+ */
|
||||
guard(mutex)(&pwm_lock);
|
||||
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
return ops->capture(chip, pwm, result, timeout);
|
||||
}
|
||||
|
||||
@@ -372,6 +413,14 @@ static int pwm_device_request(struct pwm
|
||||
if (test_bit(PWMF_REQUESTED, &pwm->flags))
|
||||
return -EBUSY;
|
||||
|
||||
+ /*
|
||||
+ * This function is called while holding pwm_lock. As .operational only
|
||||
+ * changes while holding this lock, checking it here without holding the
|
||||
+ * chip lock is fine.
|
||||
+ */
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
if (!try_module_get(chip->owner))
|
||||
return -ENODEV;
|
||||
|
||||
@@ -400,7 +449,9 @@ err_get_device:
|
||||
*/
|
||||
struct pwm_state state = { 0, };
|
||||
|
||||
- err = ops->get_state(chip, pwm, &state);
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ err = ops->get_state(chip, pwm, &state);
|
||||
+
|
||||
trace_pwm_get(pwm, &state, err);
|
||||
|
||||
if (!err)
|
||||
@@ -1024,6 +1075,7 @@ struct pwm_chip *pwmchip_alloc(struct de
|
||||
|
||||
chip->npwm = npwm;
|
||||
chip->uses_pwmchip_alloc = true;
|
||||
+ chip->operational = false;
|
||||
|
||||
pwmchip_dev = &chip->dev;
|
||||
device_initialize(pwmchip_dev);
|
||||
@@ -1129,6 +1181,11 @@ int __pwmchip_add(struct pwm_chip *chip,
|
||||
|
||||
chip->owner = owner;
|
||||
|
||||
+ if (chip->atomic)
|
||||
+ spin_lock_init(&chip->atomic_lock);
|
||||
+ else
|
||||
+ mutex_init(&chip->nonatomic_lock);
|
||||
+
|
||||
guard(mutex)(&pwm_lock);
|
||||
|
||||
ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
|
||||
@@ -1142,6 +1199,9 @@ int __pwmchip_add(struct pwm_chip *chip,
|
||||
if (IS_ENABLED(CONFIG_OF))
|
||||
of_pwmchip_add(chip);
|
||||
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = true;
|
||||
+
|
||||
ret = device_add(&chip->dev);
|
||||
if (ret)
|
||||
goto err_device_add;
|
||||
@@ -1149,6 +1209,9 @@ int __pwmchip_add(struct pwm_chip *chip,
|
||||
return 0;
|
||||
|
||||
err_device_add:
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = false;
|
||||
+
|
||||
if (IS_ENABLED(CONFIG_OF))
|
||||
of_pwmchip_remove(chip);
|
||||
|
||||
@@ -1168,11 +1231,27 @@ void pwmchip_remove(struct pwm_chip *chi
|
||||
{
|
||||
pwmchip_sysfs_unexport(chip);
|
||||
|
||||
- if (IS_ENABLED(CONFIG_OF))
|
||||
- of_pwmchip_remove(chip);
|
||||
+ scoped_guard(mutex, &pwm_lock) {
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = false;
|
||||
+
|
||||
+ for (i = 0; i < chip->npwm; ++i) {
|
||||
+ struct pwm_device *pwm = &chip->pwms[i];
|
||||
+
|
||||
+ if (test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
|
||||
+ dev_warn(&chip->dev, "Freeing requested PWM #%u\n", i);
|
||||
+ if (pwm->chip->ops->free)
|
||||
+ pwm->chip->ops->free(pwm->chip, pwm);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_OF))
|
||||
+ of_pwmchip_remove(chip);
|
||||
|
||||
- scoped_guard(mutex, &pwm_lock)
|
||||
idr_remove(&pwm_chips, chip->id);
|
||||
+ }
|
||||
|
||||
device_del(&chip->dev);
|
||||
}
|
||||
@@ -1542,12 +1621,17 @@ void pwm_put(struct pwm_device *pwm)
|
||||
|
||||
guard(mutex)(&pwm_lock);
|
||||
|
||||
- if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
|
||||
+ /*
|
||||
+ * Trigger a warning if a consumer called pwm_put() twice.
|
||||
+ * If the chip isn't operational, PWMF_REQUESTED was already cleared in
|
||||
+ * pwmchip_remove(). So don't warn in this case.
|
||||
+ */
|
||||
+ if (chip->operational && !test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
|
||||
pr_warn("PWM device already freed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
- if (chip->ops->free)
|
||||
+ if (chip->operational && chip->ops->free)
|
||||
pwm->chip->ops->free(pwm->chip, pwm);
|
||||
|
||||
pwm->label = NULL;
|
||||
--- a/include/linux/pwm.h
|
||||
+++ b/include/linux/pwm.h
|
||||
@@ -275,6 +275,9 @@ struct pwm_ops {
|
||||
* @of_xlate: request a PWM device given a device tree PWM specifier
|
||||
* @atomic: can the driver's ->apply() be called in atomic context
|
||||
* @uses_pwmchip_alloc: signals if pwmchip_allow was used to allocate this chip
|
||||
+ * @operational: signals if the chip can be used (or is already deregistered)
|
||||
+ * @nonatomic_lock: mutex for nonatomic chips
|
||||
+ * @atomic_lock: mutex for atomic chips
|
||||
* @pwms: array of PWM devices allocated by the framework
|
||||
*/
|
||||
struct pwm_chip {
|
||||
@@ -290,6 +293,16 @@ struct pwm_chip {
|
||||
|
||||
/* only used internally by the PWM framework */
|
||||
bool uses_pwmchip_alloc;
|
||||
+ bool operational;
|
||||
+ union {
|
||||
+ /*
|
||||
+ * depending on the chip being atomic or not either the mutex or
|
||||
+ * the spinlock is used. It protects .operational and
|
||||
+ * synchronizes the callbacks in .ops
|
||||
+ */
|
||||
+ struct mutex nonatomic_lock;
|
||||
+ spinlock_t atomic_lock;
|
||||
+ };
|
||||
struct pwm_device pwms[] __counted_by(npwm);
|
||||
};
|
||||
|
||||
@ -0,0 +1,408 @@
|
||||
From 17e40c25158f2505cbcdeda96624afcbab4af368 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Fri, 20 Sep 2024 10:57:58 +0200
|
||||
Subject: [PATCH] pwm: New abstraction for PWM waveforms
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Up to now the configuration of a PWM setting is described exclusively by
|
||||
a struct pwm_state which contains information about period, duty_cycle,
|
||||
polarity and if the PWM is enabled. (There is another member usage_power
|
||||
which doesn't completely fit into pwm_state, I ignore it here for
|
||||
simplicity.)
|
||||
|
||||
Instead of a polarity the new abstraction has a member duty_offset_ns
|
||||
that defines when the rising edge happens after the period start. This
|
||||
is more general, as with a pwm_state the rising edge can only happen at
|
||||
the period's start or such that the falling edge is at the end of the
|
||||
period (i.e. duty_offset_ns == 0 or duty_offset_ns == period_length_ns -
|
||||
duty_length_ns).
|
||||
|
||||
A disabled PWM is modeled by .period_length_ns = 0. In my eyes this is a
|
||||
nice usage of that otherwise unusable setting, as it doesn't define
|
||||
anything about the future which matches the fact that consumers should
|
||||
consider the state of the output as undefined and it's just there to say
|
||||
"No further requirements about the output, you can save some power.".
|
||||
|
||||
Further I renamed period and duty_cycle to period_length_ns and
|
||||
duty_length_ns. In the past there was confusion from time to time about
|
||||
duty_cycle being measured in nanoseconds because people expected a
|
||||
percentage of period instead. With "length_ns" as suffix the semantic
|
||||
should be more obvious to people unfamiliar with the pwm subsystem.
|
||||
period is renamed to period_length_ns for consistency.
|
||||
|
||||
The API for consumers doesn't change yet, but lowlevel drivers can
|
||||
implement callbacks that work with pwm_waveforms instead of pwm_states.
|
||||
A new thing about these callbacks is that the calculation of hardware
|
||||
settings needed to implement a certain waveform is separated from
|
||||
actually writing these settings. The motivation for that is that this
|
||||
allows a consumer to query the hardware capabilities without actually
|
||||
modifying the hardware state.
|
||||
|
||||
The rounding rules that are expected to be implemented in the
|
||||
round_waveform_tohw() are: First pick the biggest possible period not
|
||||
bigger than wf->period_length_ns. For that period pick the biggest
|
||||
possible duty setting not bigger than wf->duty_length_ns. Third pick the
|
||||
biggest possible offset not bigger than wf->duty_offset_ns. If the
|
||||
requested period is too small for the hardware, it's expected that a
|
||||
setting with the minimal period and duty_length_ns = duty_offset_ns = 0
|
||||
is returned and this fact is signaled by a return value of 1.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Tested-by: Trevor Gamblin <tgamblin@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/df0faa33bf9e7c9e2e5eab8d31bbf61e861bd401.1726819463.git.u.kleine-koenig@baylibre.com
|
||||
[ukleinek: Update pwm_check_rounding() to return bool instead of int.]
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 234 ++++++++++++++++++++++++++++++++++++++++----
|
||||
include/linux/pwm.h | 36 +++++++
|
||||
2 files changed, 249 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -49,6 +49,102 @@ static void pwmchip_unlock(struct pwm_ch
|
||||
|
||||
DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T))
|
||||
|
||||
+static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state)
|
||||
+{
|
||||
+ if (wf->period_length_ns) {
|
||||
+ if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns)
|
||||
+ *state = (struct pwm_state){
|
||||
+ .enabled = true,
|
||||
+ .polarity = PWM_POLARITY_NORMAL,
|
||||
+ .period = wf->period_length_ns,
|
||||
+ .duty_cycle = wf->duty_length_ns,
|
||||
+ };
|
||||
+ else
|
||||
+ *state = (struct pwm_state){
|
||||
+ .enabled = true,
|
||||
+ .polarity = PWM_POLARITY_INVERSED,
|
||||
+ .period = wf->period_length_ns,
|
||||
+ .duty_cycle = wf->period_length_ns - wf->duty_length_ns,
|
||||
+ };
|
||||
+ } else {
|
||||
+ *state = (struct pwm_state){
|
||||
+ .enabled = false,
|
||||
+ };
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf)
|
||||
+{
|
||||
+ if (state->enabled) {
|
||||
+ if (state->polarity == PWM_POLARITY_NORMAL)
|
||||
+ *wf = (struct pwm_waveform){
|
||||
+ .period_length_ns = state->period,
|
||||
+ .duty_length_ns = state->duty_cycle,
|
||||
+ .duty_offset_ns = 0,
|
||||
+ };
|
||||
+ else
|
||||
+ *wf = (struct pwm_waveform){
|
||||
+ .period_length_ns = state->period,
|
||||
+ .duty_length_ns = state->period - state->duty_cycle,
|
||||
+ .duty_offset_ns = state->duty_cycle,
|
||||
+ };
|
||||
+ } else {
|
||||
+ *wf = (struct pwm_waveform){
|
||||
+ .period_length_ns = 0,
|
||||
+ };
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static bool pwm_check_rounding(const struct pwm_waveform *wf,
|
||||
+ const struct pwm_waveform *wf_rounded)
|
||||
+{
|
||||
+ if (!wf->period_length_ns)
|
||||
+ return true;
|
||||
+
|
||||
+ if (wf->period_length_ns < wf_rounded->period_length_ns)
|
||||
+ return false;
|
||||
+
|
||||
+ if (wf->duty_length_ns < wf_rounded->duty_length_ns)
|
||||
+ return false;
|
||||
+
|
||||
+ if (wf->duty_offset_ns < wf_rounded->duty_offset_ns)
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const struct pwm_waveform *wf, void *wfhw)
|
||||
+{
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+
|
||||
+ return ops->round_waveform_tohw(chip, pwm, wf, wfhw);
|
||||
+}
|
||||
+
|
||||
+static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const void *wfhw, struct pwm_waveform *wf)
|
||||
+{
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+
|
||||
+ return ops->round_waveform_fromhw(chip, pwm, wfhw, wf);
|
||||
+}
|
||||
+
|
||||
+static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw)
|
||||
+{
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+
|
||||
+ return ops->read_waveform(chip, pwm, wfhw);
|
||||
+}
|
||||
+
|
||||
+static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw)
|
||||
+{
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+
|
||||
+ return ops->write_waveform(chip, pwm, wfhw);
|
||||
+}
|
||||
+
|
||||
+#define WFHWSIZE 20
|
||||
+
|
||||
static void pwm_apply_debug(struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
@@ -186,6 +282,7 @@ static bool pwm_state_valid(const struct
|
||||
static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
struct pwm_chip *chip;
|
||||
+ const struct pwm_ops *ops;
|
||||
int err;
|
||||
|
||||
if (!pwm || !state)
|
||||
@@ -209,6 +306,7 @@ static int __pwm_apply(struct pwm_device
|
||||
}
|
||||
|
||||
chip = pwm->chip;
|
||||
+ ops = chip->ops;
|
||||
|
||||
if (state->period == pwm->state.period &&
|
||||
state->duty_cycle == pwm->state.duty_cycle &&
|
||||
@@ -217,18 +315,69 @@ static int __pwm_apply(struct pwm_device
|
||||
state->usage_power == pwm->state.usage_power)
|
||||
return 0;
|
||||
|
||||
- err = chip->ops->apply(chip, pwm, state);
|
||||
- trace_pwm_apply(pwm, state, err);
|
||||
- if (err)
|
||||
- return err;
|
||||
+ if (ops->write_waveform) {
|
||||
+ struct pwm_waveform wf;
|
||||
+ char wfhw[WFHWSIZE];
|
||||
|
||||
- pwm->state = *state;
|
||||
+ BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
|
||||
- /*
|
||||
- * only do this after pwm->state was applied as some
|
||||
- * implementations of .get_state depend on this
|
||||
- */
|
||||
- pwm_apply_debug(pwm, state);
|
||||
+ pwm_state2wf(state, &wf);
|
||||
+
|
||||
+ /*
|
||||
+ * The rounding is wrong here for states with inverted polarity.
|
||||
+ * While .apply() rounds down duty_cycle (which represents the
|
||||
+ * time from the start of the period to the inner edge),
|
||||
+ * .round_waveform_tohw() rounds down the time the PWM is high.
|
||||
+ * Can be fixed if the need arises, until reported otherwise
|
||||
+ * let's assume that consumers don't care.
|
||||
+ */
|
||||
+
|
||||
+ err = __pwm_round_waveform_tohw(chip, pwm, &wf, &wfhw);
|
||||
+ if (err) {
|
||||
+ if (err > 0)
|
||||
+ /*
|
||||
+ * This signals an invalid request, typically
|
||||
+ * the requested period (or duty_offset) is
|
||||
+ * smaller than possible with the hardware.
|
||||
+ */
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG)) {
|
||||
+ struct pwm_waveform wf_rounded;
|
||||
+
|
||||
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (!pwm_check_rounding(&wf, &wf_rounded))
|
||||
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
+ wf.duty_length_ns, wf.period_length_ns, wf.duty_offset_ns,
|
||||
+ wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
|
||||
+ }
|
||||
+
|
||||
+ err = __pwm_write_waveform(chip, pwm, &wfhw);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ pwm->state = *state;
|
||||
+
|
||||
+ } else {
|
||||
+ err = ops->apply(chip, pwm, state);
|
||||
+ trace_pwm_apply(pwm, state, err);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ pwm->state = *state;
|
||||
+
|
||||
+ /*
|
||||
+ * only do this after pwm->state was applied as some
|
||||
+ * implementations of .get_state() depend on this
|
||||
+ */
|
||||
+ pwm_apply_debug(pwm, state);
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -296,6 +445,41 @@ int pwm_apply_atomic(struct pwm_device *
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_apply_atomic);
|
||||
|
||||
+static int pwm_get_state_hw(struct pwm_device *pwm, struct pwm_state *state)
|
||||
+{
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+ int ret = -EOPNOTSUPP;
|
||||
+
|
||||
+ if (ops->read_waveform) {
|
||||
+ char wfhw[WFHWSIZE];
|
||||
+ struct pwm_waveform wf;
|
||||
+
|
||||
+ BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
+
|
||||
+ scoped_guard(pwmchip, chip) {
|
||||
+
|
||||
+ ret = __pwm_read_waveform(chip, pwm, &wfhw);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ pwm_wf2state(&wf, state);
|
||||
+
|
||||
+ } else if (ops->get_state) {
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ ret = ops->get_state(chip, pwm, state);
|
||||
+
|
||||
+ trace_pwm_get(pwm, state, ret);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* pwm_adjust_config() - adjust the current PWM config to the PWM arguments
|
||||
* @pwm: PWM device
|
||||
@@ -439,7 +623,7 @@ err_get_device:
|
||||
}
|
||||
}
|
||||
|
||||
- if (ops->get_state) {
|
||||
+ if (ops->read_waveform || ops->get_state) {
|
||||
/*
|
||||
* Zero-initialize state because most drivers are unaware of
|
||||
* .usage_power. The other members of state are supposed to be
|
||||
@@ -449,11 +633,7 @@ err_get_device:
|
||||
*/
|
||||
struct pwm_state state = { 0, };
|
||||
|
||||
- scoped_guard(pwmchip, chip)
|
||||
- err = ops->get_state(chip, pwm, &state);
|
||||
-
|
||||
- trace_pwm_get(pwm, &state, err);
|
||||
-
|
||||
+ err = pwm_get_state_hw(pwm, &state);
|
||||
if (!err)
|
||||
pwm->state = state;
|
||||
|
||||
@@ -1140,12 +1320,24 @@ static bool pwm_ops_check(const struct p
|
||||
{
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
|
||||
- if (!ops->apply)
|
||||
- return false;
|
||||
+ if (ops->write_waveform) {
|
||||
+ if (!ops->round_waveform_tohw ||
|
||||
+ !ops->round_waveform_fromhw ||
|
||||
+ !ops->write_waveform)
|
||||
+ return false;
|
||||
+
|
||||
+ if (WFHWSIZE < ops->sizeof_wfhw) {
|
||||
+ dev_warn(pwmchip_parent(chip), "WFHWSIZE < %zu\n", ops->sizeof_wfhw);
|
||||
+ return false;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (!ops->apply)
|
||||
+ return false;
|
||||
|
||||
- if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state)
|
||||
- dev_warn(pwmchip_parent(chip),
|
||||
- "Please implement the .get_state() callback\n");
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state)
|
||||
+ dev_warn(pwmchip_parent(chip),
|
||||
+ "Please implement the .get_state() callback\n");
|
||||
+ }
|
||||
|
||||
return true;
|
||||
}
|
||||
--- a/include/linux/pwm.h
|
||||
+++ b/include/linux/pwm.h
|
||||
@@ -49,6 +49,31 @@ enum {
|
||||
PWMF_EXPORTED = 1,
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * struct pwm_waveform - description of a PWM waveform
|
||||
+ * @period_length_ns: PWM period
|
||||
+ * @duty_length_ns: PWM duty cycle
|
||||
+ * @duty_offset_ns: offset of the rising edge from the period's start
|
||||
+ *
|
||||
+ * This is a representation of a PWM waveform alternative to struct pwm_state
|
||||
+ * below. It's more expressive than struct pwm_state as it contains a
|
||||
+ * duty_offset_ns and so can represent offsets other than zero (with .polarity =
|
||||
+ * PWM_POLARITY_NORMAL) and period - duty_cycle (.polarity =
|
||||
+ * PWM_POLARITY_INVERSED).
|
||||
+ *
|
||||
+ * Note there is no explicit bool for enabled. A "disabled" PWM is represented
|
||||
+ * by .period_length_ns = 0. Note further that the behaviour of a "disabled" PWM
|
||||
+ * is undefined. Depending on the hardware's capabilities it might drive the
|
||||
+ * active or inactive level, go high-z or even continue to toggle.
|
||||
+ *
|
||||
+ * The unit for all three members is nanoseconds.
|
||||
+ */
|
||||
+struct pwm_waveform {
|
||||
+ u64 period_length_ns;
|
||||
+ u64 duty_length_ns;
|
||||
+ u64 duty_offset_ns;
|
||||
+};
|
||||
+
|
||||
/*
|
||||
* struct pwm_state - state of a PWM channel
|
||||
* @period: PWM period (in nanoseconds)
|
||||
@@ -259,6 +284,17 @@ struct pwm_ops {
|
||||
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
|
||||
int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_capture *result, unsigned long timeout);
|
||||
+
|
||||
+ size_t sizeof_wfhw;
|
||||
+ int (*round_waveform_tohw)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const struct pwm_waveform *wf, void *wfhw);
|
||||
+ int (*round_waveform_fromhw)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const void *wfhw, struct pwm_waveform *wf);
|
||||
+ int (*read_waveform)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ void *wfhw);
|
||||
+ int (*write_waveform)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const void *wfhw);
|
||||
+
|
||||
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state);
|
||||
int (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
@ -0,0 +1,323 @@
|
||||
From 6c5126c6406d1c31e91f5b925c621c1c785366be Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Fri, 20 Sep 2024 10:57:59 +0200
|
||||
Subject: [PATCH] pwm: Provide new consumer API functions for waveforms
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Provide API functions for consumers to work with waveforms.
|
||||
|
||||
Note that one relevant difference between pwm_get_state() and
|
||||
pwm_get_waveform*() is that the latter yields the actually configured
|
||||
hardware state, while the former yields the last state passed to
|
||||
pwm_apply*() and so doesn't account for hardware specific rounding.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Tested-by: Trevor Gamblin <tgamblin@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/6c97d27682853f603e18e9196043886dd671845d.1726819463.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 261 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/pwm.h | 6 +-
|
||||
2 files changed, 266 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -49,6 +49,30 @@ static void pwmchip_unlock(struct pwm_ch
|
||||
|
||||
DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T))
|
||||
|
||||
+static bool pwm_wf_valid(const struct pwm_waveform *wf)
|
||||
+{
|
||||
+ /*
|
||||
+ * For now restrict waveforms to period_length_ns <= S64_MAX to provide
|
||||
+ * some space for future extensions. One possibility is to simplify
|
||||
+ * representing waveforms with inverted polarity using negative values
|
||||
+ * somehow.
|
||||
+ */
|
||||
+ if (wf->period_length_ns > S64_MAX)
|
||||
+ return false;
|
||||
+
|
||||
+ if (wf->duty_length_ns > wf->period_length_ns)
|
||||
+ return false;
|
||||
+
|
||||
+ /*
|
||||
+ * .duty_offset_ns is supposed to be smaller than .period_length_ns, apart
|
||||
+ * from the corner case .duty_offset_ns == 0 && .period_length_ns == 0.
|
||||
+ */
|
||||
+ if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns)
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state)
|
||||
{
|
||||
if (wf->period_length_ns) {
|
||||
@@ -95,6 +119,29 @@ static void pwm_state2wf(const struct pw
|
||||
}
|
||||
}
|
||||
|
||||
+static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b)
|
||||
+{
|
||||
+ if (a->period_length_ns > b->period_length_ns)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (a->period_length_ns < b->period_length_ns)
|
||||
+ return -1;
|
||||
+
|
||||
+ if (a->duty_length_ns > b->duty_length_ns)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (a->duty_length_ns < b->duty_length_ns)
|
||||
+ return -1;
|
||||
+
|
||||
+ if (a->duty_offset_ns > b->duty_offset_ns)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (a->duty_offset_ns < b->duty_offset_ns)
|
||||
+ return -1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static bool pwm_check_rounding(const struct pwm_waveform *wf,
|
||||
const struct pwm_waveform *wf_rounded)
|
||||
{
|
||||
@@ -145,6 +192,220 @@ static int __pwm_write_waveform(struct p
|
||||
|
||||
#define WFHWSIZE 20
|
||||
|
||||
+/**
|
||||
+ * pwm_round_waveform_might_sleep - Query hardware capabilities
|
||||
+ * Cannot be used in atomic context.
|
||||
+ * @pwm: PWM device
|
||||
+ * @wf: waveform to round and output parameter
|
||||
+ *
|
||||
+ * Typically a given waveform cannot be implemented exactly by hardware, e.g.
|
||||
+ * because hardware only supports coarse period resolution or no duty_offset.
|
||||
+ * This function returns the actually implemented waveform if you pass wf to
|
||||
+ * pwm_set_waveform_might_sleep now.
|
||||
+ *
|
||||
+ * Note however that the world doesn't stop turning when you call it, so when
|
||||
+ * doing
|
||||
+ *
|
||||
+ * pwm_round_waveform_might_sleep(mypwm, &wf);
|
||||
+ * pwm_set_waveform_might_sleep(mypwm, &wf, true);
|
||||
+ *
|
||||
+ * the latter might fail, e.g. because an input clock changed its rate between
|
||||
+ * these two calls and the waveform determined by
|
||||
+ * pwm_round_waveform_might_sleep() cannot be implemented any more.
|
||||
+ *
|
||||
+ * Returns 0 on success, 1 if there is no valid hardware configuration matching
|
||||
+ * the input waveform under the PWM rounding rules or a negative errno.
|
||||
+ */
|
||||
+int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
|
||||
+{
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+ struct pwm_waveform wf_req = *wf;
|
||||
+ char wfhw[WFHWSIZE];
|
||||
+ int ret_tohw, ret_fromhw;
|
||||
+
|
||||
+ BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
+
|
||||
+ if (!pwm_wf_valid(wf))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw);
|
||||
+ if (ret_tohw < 0)
|
||||
+ return ret_tohw;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw > 1)
|
||||
+ dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_tohw: requested %llu/%llu [+%llu], return value %d\n",
|
||||
+ wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
|
||||
+
|
||||
+ ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf);
|
||||
+ if (ret_fromhw < 0)
|
||||
+ return ret_fromhw;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_fromhw > 0)
|
||||
+ dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_fromhw: requested %llu/%llu [+%llu], return value %d\n",
|
||||
+ wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) &&
|
||||
+ ret_tohw == 0 && !pwm_check_rounding(&wf_req, wf))
|
||||
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
+ wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns,
|
||||
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns);
|
||||
+
|
||||
+ return ret_tohw;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep);
|
||||
+
|
||||
+/**
|
||||
+ * pwm_get_waveform_might_sleep - Query hardware about current configuration
|
||||
+ * Cannot be used in atomic context.
|
||||
+ * @pwm: PWM device
|
||||
+ * @wf: output parameter
|
||||
+ *
|
||||
+ * Stores the current configuration of the PWM in @wf. Note this is the
|
||||
+ * equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform.
|
||||
+ */
|
||||
+int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
|
||||
+{
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+ char wfhw[WFHWSIZE];
|
||||
+ int err;
|
||||
+
|
||||
+ BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
+
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ err = __pwm_read_waveform(chip, pwm, &wfhw);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep);
|
||||
+
|
||||
+/* Called with the pwmchip lock held */
|
||||
+static int __pwm_set_waveform(struct pwm_device *pwm,
|
||||
+ const struct pwm_waveform *wf,
|
||||
+ bool exact)
|
||||
+{
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+ const struct pwm_ops *ops = chip->ops;
|
||||
+ char wfhw[WFHWSIZE];
|
||||
+ struct pwm_waveform wf_rounded;
|
||||
+ int err;
|
||||
+
|
||||
+ BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
+
|
||||
+ if (!pwm_wf_valid(wf))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ err = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) {
|
||||
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && !pwm_check_rounding(wf, &wf_rounded))
|
||||
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
|
||||
+ wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
|
||||
+
|
||||
+ if (exact && pwmwfcmp(wf, &wf_rounded)) {
|
||||
+ dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n",
|
||||
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
|
||||
+ wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
|
||||
+
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ err = __pwm_write_waveform(chip, pwm, &wfhw);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ /* update .state */
|
||||
+ pwm_wf2state(wf, &pwm->state);
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) {
|
||||
+ struct pwm_waveform wf_set;
|
||||
+
|
||||
+ err = __pwm_read_waveform(chip, pwm, &wfhw);
|
||||
+ if (err)
|
||||
+ /* maybe ignore? */
|
||||
+ return err;
|
||||
+
|
||||
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set);
|
||||
+ if (err)
|
||||
+ /* maybe ignore? */
|
||||
+ return err;
|
||||
+
|
||||
+ if (pwmwfcmp(&wf_set, &wf_rounded) != 0)
|
||||
+ dev_err(&chip->dev,
|
||||
+ "Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\n",
|
||||
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
|
||||
+ wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns,
|
||||
+ wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pwm_set_waveform_might_sleep - Apply a new waveform
|
||||
+ * Cannot be used in atomic context.
|
||||
+ * @pwm: PWM device
|
||||
+ * @wf: The waveform to apply
|
||||
+ * @exact: If true no rounding is allowed
|
||||
+ *
|
||||
+ * Typically a requested waveform cannot be implemented exactly, e.g. because
|
||||
+ * you requested .period_length_ns = 100 ns, but the hardware can only set
|
||||
+ * periods that are a multiple of 8.5 ns. With that hardware passing exact =
|
||||
+ * true results in pwm_set_waveform_might_sleep() failing and returning 1. If
|
||||
+ * exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger
|
||||
+ * than the requested value).
|
||||
+ * Note that even with exact = true, some rounding by less than 1 is
|
||||
+ * possible/needed. In the above example requesting .period_length_ns = 94 and
|
||||
+ * exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
+ */
|
||||
+int pwm_set_waveform_might_sleep(struct pwm_device *pwm,
|
||||
+ const struct pwm_waveform *wf, bool exact)
|
||||
+{
|
||||
+ struct pwm_chip *chip = pwm->chip;
|
||||
+ int err;
|
||||
+
|
||||
+ might_sleep();
|
||||
+
|
||||
+ guard(pwmchip)(chip);
|
||||
+
|
||||
+ if (!chip->operational)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) {
|
||||
+ /*
|
||||
+ * Catch any drivers that have been marked as atomic but
|
||||
+ * that will sleep anyway.
|
||||
+ */
|
||||
+ non_block_start();
|
||||
+ err = __pwm_set_waveform(pwm, wf, exact);
|
||||
+ non_block_end();
|
||||
+ } else {
|
||||
+ err = __pwm_set_waveform(pwm, wf, exact);
|
||||
+ }
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep);
|
||||
+
|
||||
static void pwm_apply_debug(struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
--- a/include/linux/pwm.h
|
||||
+++ b/include/linux/pwm.h
|
||||
@@ -358,7 +358,11 @@ static inline void pwmchip_set_drvdata(s
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM)
|
||||
-/* PWM user APIs */
|
||||
+
|
||||
+/* PWM consumer APIs */
|
||||
+int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf);
|
||||
+int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf);
|
||||
+int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact);
|
||||
int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state);
|
||||
int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state);
|
||||
int pwm_adjust_config(struct pwm_device *pwm);
|
||||
@ -0,0 +1,236 @@
|
||||
From 1afd01db1a76cdd1d96696e3790d66c79621784c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Fri, 20 Sep 2024 10:58:00 +0200
|
||||
Subject: [PATCH] pwm: Add tracing for waveform callbacks
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds trace events for the recently introduced waveform callbacks.
|
||||
With the introduction of some helper macros consistency among the
|
||||
different events is ensured.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/1d71879b0de3bf01459c7a9d0f040d43eb5ace56.1726819463.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 24 +++++--
|
||||
include/trace/events/pwm.h | 134 ++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 146 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -164,30 +164,46 @@ static int __pwm_round_waveform_tohw(str
|
||||
const struct pwm_waveform *wf, void *wfhw)
|
||||
{
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
+ int ret;
|
||||
|
||||
- return ops->round_waveform_tohw(chip, pwm, wf, wfhw);
|
||||
+ ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw);
|
||||
+ trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const void *wfhw, struct pwm_waveform *wf)
|
||||
{
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf);
|
||||
+ trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret);
|
||||
|
||||
- return ops->round_waveform_fromhw(chip, pwm, wfhw, wf);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw)
|
||||
{
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
+ int ret;
|
||||
|
||||
- return ops->read_waveform(chip, pwm, wfhw);
|
||||
+ ret = ops->read_waveform(chip, pwm, wfhw);
|
||||
+ trace_pwm_read_waveform(pwm, wfhw, ret);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw)
|
||||
{
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = ops->write_waveform(chip, pwm, wfhw);
|
||||
+ trace_pwm_write_waveform(pwm, wfhw, ret);
|
||||
|
||||
- return ops->write_waveform(chip, pwm, wfhw);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
#define WFHWSIZE 20
|
||||
--- a/include/trace/events/pwm.h
|
||||
+++ b/include/trace/events/pwm.h
|
||||
@@ -8,15 +8,135 @@
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
+#define TP_PROTO_pwm(args...) \
|
||||
+ TP_PROTO(struct pwm_device *pwm, args)
|
||||
+
|
||||
+#define TP_ARGS_pwm(args...) \
|
||||
+ TP_ARGS(pwm, args)
|
||||
+
|
||||
+#define TP_STRUCT__entry_pwm(args...) \
|
||||
+ TP_STRUCT__entry( \
|
||||
+ __field(unsigned int, chipid) \
|
||||
+ __field(unsigned int, hwpwm) \
|
||||
+ args)
|
||||
+
|
||||
+#define TP_fast_assign_pwm(args...) \
|
||||
+ TP_fast_assign( \
|
||||
+ __entry->chipid = pwm->chip->id; \
|
||||
+ __entry->hwpwm = pwm->hwpwm; \
|
||||
+ args)
|
||||
+
|
||||
+#define TP_printk_pwm(fmt, args...) \
|
||||
+ TP_printk("pwmchip%u.%u: " fmt, __entry->chipid, __entry->hwpwm, args)
|
||||
+
|
||||
+#define __field_pwmwf(wf) \
|
||||
+ __field(u64, wf ## _period_length_ns) \
|
||||
+ __field(u64, wf ## _duty_length_ns) \
|
||||
+ __field(u64, wf ## _duty_offset_ns) \
|
||||
+
|
||||
+#define fast_assign_pwmwf(wf) \
|
||||
+ __entry->wf ## _period_length_ns = wf->period_length_ns; \
|
||||
+ __entry->wf ## _duty_length_ns = wf->duty_length_ns; \
|
||||
+ __entry->wf ## _duty_offset_ns = wf->duty_offset_ns
|
||||
+
|
||||
+#define printk_pwmwf_format(wf) \
|
||||
+ "%lld/%lld [+%lld]"
|
||||
+
|
||||
+#define printk_pwmwf_formatargs(wf) \
|
||||
+ __entry->wf ## _duty_length_ns, __entry->wf ## _period_length_ns, __entry->wf ## _duty_offset_ns
|
||||
+
|
||||
+TRACE_EVENT(pwm_round_waveform_tohw,
|
||||
+
|
||||
+ TP_PROTO_pwm(const struct pwm_waveform *wf, void *wfhw, int err),
|
||||
+
|
||||
+ TP_ARGS_pwm(wf, wfhw, err),
|
||||
+
|
||||
+ TP_STRUCT__entry_pwm(
|
||||
+ __field_pwmwf(wf)
|
||||
+ __field(void *, wfhw)
|
||||
+ __field(int, err)
|
||||
+ ),
|
||||
+
|
||||
+ TP_fast_assign_pwm(
|
||||
+ fast_assign_pwmwf(wf);
|
||||
+ __entry->wfhw = wfhw;
|
||||
+ __entry->err = err;
|
||||
+ ),
|
||||
+
|
||||
+ TP_printk_pwm(printk_pwmwf_format(wf) " > %p err=%d",
|
||||
+ printk_pwmwf_formatargs(wf), __entry->wfhw, __entry->err)
|
||||
+);
|
||||
+
|
||||
+TRACE_EVENT(pwm_round_waveform_fromhw,
|
||||
+
|
||||
+ TP_PROTO_pwm(const void *wfhw, struct pwm_waveform *wf, int err),
|
||||
+
|
||||
+ TP_ARGS_pwm(wfhw, wf, err),
|
||||
+
|
||||
+ TP_STRUCT__entry_pwm(
|
||||
+ __field(const void *, wfhw)
|
||||
+ __field_pwmwf(wf)
|
||||
+ __field(int, err)
|
||||
+ ),
|
||||
+
|
||||
+ TP_fast_assign_pwm(
|
||||
+ __entry->wfhw = wfhw;
|
||||
+ fast_assign_pwmwf(wf);
|
||||
+ __entry->err = err;
|
||||
+ ),
|
||||
+
|
||||
+ TP_printk_pwm("%p > " printk_pwmwf_format(wf) " err=%d",
|
||||
+ __entry->wfhw, printk_pwmwf_formatargs(wf), __entry->err)
|
||||
+);
|
||||
+
|
||||
+TRACE_EVENT(pwm_read_waveform,
|
||||
+
|
||||
+ TP_PROTO_pwm(void *wfhw, int err),
|
||||
+
|
||||
+ TP_ARGS_pwm(wfhw, err),
|
||||
+
|
||||
+ TP_STRUCT__entry_pwm(
|
||||
+ __field(void *, wfhw)
|
||||
+ __field(int, err)
|
||||
+ ),
|
||||
+
|
||||
+ TP_fast_assign_pwm(
|
||||
+ __entry->wfhw = wfhw;
|
||||
+ __entry->err = err;
|
||||
+ ),
|
||||
+
|
||||
+ TP_printk_pwm("%p err=%d",
|
||||
+ __entry->wfhw, __entry->err)
|
||||
+);
|
||||
+
|
||||
+TRACE_EVENT(pwm_write_waveform,
|
||||
+
|
||||
+ TP_PROTO_pwm(const void *wfhw, int err),
|
||||
+
|
||||
+ TP_ARGS_pwm(wfhw, err),
|
||||
+
|
||||
+ TP_STRUCT__entry_pwm(
|
||||
+ __field(const void *, wfhw)
|
||||
+ __field(int, err)
|
||||
+ ),
|
||||
+
|
||||
+ TP_fast_assign_pwm(
|
||||
+ __entry->wfhw = wfhw;
|
||||
+ __entry->err = err;
|
||||
+ ),
|
||||
+
|
||||
+ TP_printk_pwm("%p err=%d",
|
||||
+ __entry->wfhw, __entry->err)
|
||||
+);
|
||||
+
|
||||
+
|
||||
DECLARE_EVENT_CLASS(pwm,
|
||||
|
||||
TP_PROTO(struct pwm_device *pwm, const struct pwm_state *state, int err),
|
||||
|
||||
TP_ARGS(pwm, state, err),
|
||||
|
||||
- TP_STRUCT__entry(
|
||||
- __field(unsigned int, chipid)
|
||||
- __field(unsigned int, hwpwm)
|
||||
+ TP_STRUCT__entry_pwm(
|
||||
__field(u64, period)
|
||||
__field(u64, duty_cycle)
|
||||
__field(enum pwm_polarity, polarity)
|
||||
@@ -24,9 +144,7 @@ DECLARE_EVENT_CLASS(pwm,
|
||||
__field(int, err)
|
||||
),
|
||||
|
||||
- TP_fast_assign(
|
||||
- __entry->chipid = pwm->chip->id;
|
||||
- __entry->hwpwm = pwm->hwpwm;
|
||||
+ TP_fast_assign_pwm(
|
||||
__entry->period = state->period;
|
||||
__entry->duty_cycle = state->duty_cycle;
|
||||
__entry->polarity = state->polarity;
|
||||
@@ -34,8 +152,8 @@ DECLARE_EVENT_CLASS(pwm,
|
||||
__entry->err = err;
|
||||
),
|
||||
|
||||
- TP_printk("pwmchip%u.%u: period=%llu duty_cycle=%llu polarity=%d enabled=%d err=%d",
|
||||
- __entry->chipid, __entry->hwpwm, __entry->period, __entry->duty_cycle,
|
||||
+ TP_printk_pwm("period=%llu duty_cycle=%llu polarity=%d enabled=%d err=%d",
|
||||
+ __entry->period, __entry->duty_cycle,
|
||||
__entry->polarity, __entry->enabled, __entry->err)
|
||||
|
||||
);
|
||||
@ -0,0 +1,367 @@
|
||||
From 65406de2b0d059d44472ad6f3f88a9b4a9894833 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Fri, 20 Sep 2024 10:58:03 +0200
|
||||
Subject: [PATCH] pwm: Reorder symbols in core.c
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This moves pwm_get() and friends above the functions handling
|
||||
registration of pwmchips. The motivation is that character device
|
||||
support needs pwm_get() and pwm_put() and so ideally is defined below
|
||||
these and when a pwmchip is registered this registers the character
|
||||
device. So the natural order is
|
||||
|
||||
pwm_get() and friend
|
||||
pwm character device symbols
|
||||
pwm_chip functions
|
||||
|
||||
. The advantage of having these in their natural order is that static
|
||||
functions don't need to be forward declared.
|
||||
|
||||
Note that the diff that git produces for this change some functions are
|
||||
moved down instead. This is technically equivalent, but not how this
|
||||
change was created.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/193b3d933294da34e020650bff93b778de46b1c5.1726819463.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 312 ++++++++++++++++++++++-----------------------
|
||||
1 file changed, 156 insertions(+), 156 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -1619,132 +1619,6 @@ static bool pwm_ops_check(const struct p
|
||||
return true;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * __pwmchip_add() - register a new PWM chip
|
||||
- * @chip: the PWM chip to add
|
||||
- * @owner: reference to the module providing the chip.
|
||||
- *
|
||||
- * Register a new PWM chip. @owner is supposed to be THIS_MODULE, use the
|
||||
- * pwmchip_add wrapper to do this right.
|
||||
- *
|
||||
- * Returns: 0 on success or a negative error code on failure.
|
||||
- */
|
||||
-int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
|
||||
-{
|
||||
- int ret;
|
||||
-
|
||||
- if (!chip || !pwmchip_parent(chip) || !chip->ops || !chip->npwm)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- /*
|
||||
- * a struct pwm_chip must be allocated using (devm_)pwmchip_alloc,
|
||||
- * otherwise the embedded struct device might disappear too early
|
||||
- * resulting in memory corruption.
|
||||
- * Catch drivers that were not converted appropriately.
|
||||
- */
|
||||
- if (!chip->uses_pwmchip_alloc)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- if (!pwm_ops_check(chip))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- chip->owner = owner;
|
||||
-
|
||||
- if (chip->atomic)
|
||||
- spin_lock_init(&chip->atomic_lock);
|
||||
- else
|
||||
- mutex_init(&chip->nonatomic_lock);
|
||||
-
|
||||
- guard(mutex)(&pwm_lock);
|
||||
-
|
||||
- ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- chip->id = ret;
|
||||
-
|
||||
- dev_set_name(&chip->dev, "pwmchip%u", chip->id);
|
||||
-
|
||||
- if (IS_ENABLED(CONFIG_OF))
|
||||
- of_pwmchip_add(chip);
|
||||
-
|
||||
- scoped_guard(pwmchip, chip)
|
||||
- chip->operational = true;
|
||||
-
|
||||
- ret = device_add(&chip->dev);
|
||||
- if (ret)
|
||||
- goto err_device_add;
|
||||
-
|
||||
- return 0;
|
||||
-
|
||||
-err_device_add:
|
||||
- scoped_guard(pwmchip, chip)
|
||||
- chip->operational = false;
|
||||
-
|
||||
- if (IS_ENABLED(CONFIG_OF))
|
||||
- of_pwmchip_remove(chip);
|
||||
-
|
||||
- idr_remove(&pwm_chips, chip->id);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(__pwmchip_add);
|
||||
-
|
||||
-/**
|
||||
- * pwmchip_remove() - remove a PWM chip
|
||||
- * @chip: the PWM chip to remove
|
||||
- *
|
||||
- * Removes a PWM chip.
|
||||
- */
|
||||
-void pwmchip_remove(struct pwm_chip *chip)
|
||||
-{
|
||||
- pwmchip_sysfs_unexport(chip);
|
||||
-
|
||||
- scoped_guard(mutex, &pwm_lock) {
|
||||
- unsigned int i;
|
||||
-
|
||||
- scoped_guard(pwmchip, chip)
|
||||
- chip->operational = false;
|
||||
-
|
||||
- for (i = 0; i < chip->npwm; ++i) {
|
||||
- struct pwm_device *pwm = &chip->pwms[i];
|
||||
-
|
||||
- if (test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
|
||||
- dev_warn(&chip->dev, "Freeing requested PWM #%u\n", i);
|
||||
- if (pwm->chip->ops->free)
|
||||
- pwm->chip->ops->free(pwm->chip, pwm);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (IS_ENABLED(CONFIG_OF))
|
||||
- of_pwmchip_remove(chip);
|
||||
-
|
||||
- idr_remove(&pwm_chips, chip->id);
|
||||
- }
|
||||
-
|
||||
- device_del(&chip->dev);
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(pwmchip_remove);
|
||||
-
|
||||
-static void devm_pwmchip_remove(void *data)
|
||||
-{
|
||||
- struct pwm_chip *chip = data;
|
||||
-
|
||||
- pwmchip_remove(chip);
|
||||
-}
|
||||
-
|
||||
-int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner)
|
||||
-{
|
||||
- int ret;
|
||||
-
|
||||
- ret = __pwmchip_add(chip, owner);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip);
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(__devm_pwmchip_add);
|
||||
-
|
||||
static struct device_link *pwm_device_link_add(struct device *dev,
|
||||
struct pwm_device *pwm)
|
||||
{
|
||||
@@ -1923,36 +1797,6 @@ static DEFINE_MUTEX(pwm_lookup_lock);
|
||||
static LIST_HEAD(pwm_lookup_list);
|
||||
|
||||
/**
|
||||
- * pwm_add_table() - register PWM device consumers
|
||||
- * @table: array of consumers to register
|
||||
- * @num: number of consumers in table
|
||||
- */
|
||||
-void pwm_add_table(struct pwm_lookup *table, size_t num)
|
||||
-{
|
||||
- guard(mutex)(&pwm_lookup_lock);
|
||||
-
|
||||
- while (num--) {
|
||||
- list_add_tail(&table->list, &pwm_lookup_list);
|
||||
- table++;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * pwm_remove_table() - unregister PWM device consumers
|
||||
- * @table: array of consumers to unregister
|
||||
- * @num: number of consumers in table
|
||||
- */
|
||||
-void pwm_remove_table(struct pwm_lookup *table, size_t num)
|
||||
-{
|
||||
- guard(mutex)(&pwm_lookup_lock);
|
||||
-
|
||||
- while (num--) {
|
||||
- list_del(&table->list);
|
||||
- table++;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
* pwm_get() - look up and request a PWM device
|
||||
* @dev: device for PWM consumer
|
||||
* @con_id: consumer name
|
||||
@@ -2178,6 +2022,162 @@ struct pwm_device *devm_fwnode_pwm_get(s
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get);
|
||||
|
||||
+/**
|
||||
+ * __pwmchip_add() - register a new PWM chip
|
||||
+ * @chip: the PWM chip to add
|
||||
+ * @owner: reference to the module providing the chip.
|
||||
+ *
|
||||
+ * Register a new PWM chip. @owner is supposed to be THIS_MODULE, use the
|
||||
+ * pwmchip_add wrapper to do this right.
|
||||
+ *
|
||||
+ * Returns: 0 on success or a negative error code on failure.
|
||||
+ */
|
||||
+int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!chip || !pwmchip_parent(chip) || !chip->ops || !chip->npwm)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /*
|
||||
+ * a struct pwm_chip must be allocated using (devm_)pwmchip_alloc,
|
||||
+ * otherwise the embedded struct device might disappear too early
|
||||
+ * resulting in memory corruption.
|
||||
+ * Catch drivers that were not converted appropriately.
|
||||
+ */
|
||||
+ if (!chip->uses_pwmchip_alloc)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!pwm_ops_check(chip))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ chip->owner = owner;
|
||||
+
|
||||
+ if (chip->atomic)
|
||||
+ spin_lock_init(&chip->atomic_lock);
|
||||
+ else
|
||||
+ mutex_init(&chip->nonatomic_lock);
|
||||
+
|
||||
+ guard(mutex)(&pwm_lock);
|
||||
+
|
||||
+ ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ chip->id = ret;
|
||||
+
|
||||
+ dev_set_name(&chip->dev, "pwmchip%u", chip->id);
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_OF))
|
||||
+ of_pwmchip_add(chip);
|
||||
+
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = true;
|
||||
+
|
||||
+ ret = device_add(&chip->dev);
|
||||
+ if (ret)
|
||||
+ goto err_device_add;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_device_add:
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = false;
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_OF))
|
||||
+ of_pwmchip_remove(chip);
|
||||
+
|
||||
+ idr_remove(&pwm_chips, chip->id);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(__pwmchip_add);
|
||||
+
|
||||
+/**
|
||||
+ * pwmchip_remove() - remove a PWM chip
|
||||
+ * @chip: the PWM chip to remove
|
||||
+ *
|
||||
+ * Removes a PWM chip.
|
||||
+ */
|
||||
+void pwmchip_remove(struct pwm_chip *chip)
|
||||
+{
|
||||
+ pwmchip_sysfs_unexport(chip);
|
||||
+
|
||||
+ scoped_guard(mutex, &pwm_lock) {
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ scoped_guard(pwmchip, chip)
|
||||
+ chip->operational = false;
|
||||
+
|
||||
+ for (i = 0; i < chip->npwm; ++i) {
|
||||
+ struct pwm_device *pwm = &chip->pwms[i];
|
||||
+
|
||||
+ if (test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
|
||||
+ dev_warn(&chip->dev, "Freeing requested PWM #%u\n", i);
|
||||
+ if (pwm->chip->ops->free)
|
||||
+ pwm->chip->ops->free(pwm->chip, pwm);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_OF))
|
||||
+ of_pwmchip_remove(chip);
|
||||
+
|
||||
+ idr_remove(&pwm_chips, chip->id);
|
||||
+ }
|
||||
+
|
||||
+ device_del(&chip->dev);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(pwmchip_remove);
|
||||
+
|
||||
+static void devm_pwmchip_remove(void *data)
|
||||
+{
|
||||
+ struct pwm_chip *chip = data;
|
||||
+
|
||||
+ pwmchip_remove(chip);
|
||||
+}
|
||||
+
|
||||
+int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = __pwmchip_add(chip, owner);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(__devm_pwmchip_add);
|
||||
+
|
||||
+/**
|
||||
+ * pwm_add_table() - register PWM device consumers
|
||||
+ * @table: array of consumers to register
|
||||
+ * @num: number of consumers in table
|
||||
+ */
|
||||
+void pwm_add_table(struct pwm_lookup *table, size_t num)
|
||||
+{
|
||||
+ guard(mutex)(&pwm_lookup_lock);
|
||||
+
|
||||
+ while (num--) {
|
||||
+ list_add_tail(&table->list, &pwm_lookup_list);
|
||||
+ table++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pwm_remove_table() - unregister PWM device consumers
|
||||
+ * @table: array of consumers to unregister
|
||||
+ * @num: number of consumers in table
|
||||
+ */
|
||||
+void pwm_remove_table(struct pwm_lookup *table, size_t num)
|
||||
+{
|
||||
+ guard(mutex)(&pwm_lookup_lock);
|
||||
+
|
||||
+ while (num--) {
|
||||
+ list_del(&table->list);
|
||||
+ table++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -0,0 +1,117 @@
|
||||
From 4f3d1be4c2f8a22470f3625cbc778ba2e2130def Mon Sep 17 00:00:00 2001
|
||||
From: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
Date: Thu, 14 Nov 2024 02:18:32 +0900
|
||||
Subject: [PATCH] compiler.h: add const_true()
|
||||
|
||||
__builtin_constant_p() is known for not always being able to produce
|
||||
constant expression [1] which led to the introduction of
|
||||
__is_constexpr() [2]. Because of its dependency on
|
||||
__builtin_constant_p(), statically_true() suffers from the same
|
||||
issues.
|
||||
|
||||
For example:
|
||||
|
||||
void foo(int a)
|
||||
{
|
||||
/* fail on GCC */
|
||||
BUILD_BUG_ON_ZERO(statically_true(a));
|
||||
|
||||
/* fail on both clang and GCC */
|
||||
static char arr[statically_true(a) ? 1 : 2];
|
||||
}
|
||||
|
||||
For the same reasons why __is_constexpr() was created to cover
|
||||
__builtin_constant_p() edge cases, __is_constexpr() can be used to
|
||||
resolve statically_true() limitations.
|
||||
|
||||
Note that, somehow, GCC is not always able to fold this:
|
||||
|
||||
__is_constexpr(x) && (x)
|
||||
|
||||
It is OK in BUILD_BUG_ON_ZERO() but not in array declarations nor in
|
||||
static_assert():
|
||||
|
||||
void bar(int a)
|
||||
{
|
||||
/* success */
|
||||
BUILD_BUG_ON_ZERO(__is_constexpr(a) && (a));
|
||||
|
||||
/* fail on GCC */
|
||||
static char arr[__is_constexpr(a) && (a) ? 1 : 2];
|
||||
|
||||
/* fail on GCC */
|
||||
static_assert(__is_constexpr(a) && (a));
|
||||
}
|
||||
|
||||
Encapsulating the expression in a __builtin_choose_expr() switch
|
||||
resolves all these failed tests.
|
||||
|
||||
Define a new const_true() macro which, by making use of the
|
||||
__builtin_choose_expr() and __is_constexpr(x) combo, always produces a
|
||||
constant expression.
|
||||
|
||||
It should be noted that statically_true() is the only one able to fold
|
||||
tautological expressions in which at least one on the operands is not a
|
||||
constant expression. For example:
|
||||
|
||||
statically_true(true || var)
|
||||
statically_true(var == var)
|
||||
statically_true(var * 0 + 1)
|
||||
statically_true(!(var * 8 % 4))
|
||||
|
||||
always evaluates to true, whereas all of these would be false under
|
||||
const_true() if var is not a constant expression [3].
|
||||
|
||||
For this reason, usage of const_true() should be the exception.
|
||||
Reflect in the documentation that const_true() is less powerful and
|
||||
that statically_true() is the overall preferred solution.
|
||||
|
||||
[1] __builtin_constant_p cannot resolve to const when optimizing
|
||||
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=19449
|
||||
|
||||
[2] commit 3c8ba0d61d04 ("kernel.h: Retain constant expression output for max()/min()")
|
||||
Link: https://git.kernel.org/torvalds/c/3c8ba0d61d04
|
||||
|
||||
[3] https://godbolt.org/z/c61PMxqbK
|
||||
|
||||
CC: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
CC: Rasmus Villemoes <linux@rasmusvillemoes.dk>
|
||||
CC: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
||||
Reviewed-by: Yury Norov <yury.norov@gmail.com>,
|
||||
Signed-off-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
|
||||
Signed-off-by: Yury Norov <yury.norov@gmail.com>
|
||||
---
|
||||
include/linux/compiler.h | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
--- a/include/linux/compiler.h
|
||||
+++ b/include/linux/compiler.h
|
||||
@@ -295,6 +295,28 @@ static inline void *offset_to_ptr(const
|
||||
#define statically_true(x) (__builtin_constant_p(x) && (x))
|
||||
|
||||
/*
|
||||
+ * Similar to statically_true() but produces a constant expression
|
||||
+ *
|
||||
+ * To be used in conjunction with macros, such as BUILD_BUG_ON_ZERO(),
|
||||
+ * which require their input to be a constant expression and for which
|
||||
+ * statically_true() would otherwise fail.
|
||||
+ *
|
||||
+ * This is a trade-off: const_true() requires all its operands to be
|
||||
+ * compile time constants. Else, it would always returns false even on
|
||||
+ * the most trivial cases like:
|
||||
+ *
|
||||
+ * true || non_const_var
|
||||
+ *
|
||||
+ * On the opposite, statically_true() is able to fold more complex
|
||||
+ * tautologies and will return true on expressions such as:
|
||||
+ *
|
||||
+ * !(non_const_var * 8 % 4)
|
||||
+ *
|
||||
+ * For the general case, statically_true() is better.
|
||||
+ */
|
||||
+#define const_true(x) __builtin_choose_expr(__is_constexpr(x), x, false)
|
||||
+
|
||||
+/*
|
||||
* This is needed in functions which generate the stack canary, see
|
||||
* arch/x86/kernel/smpboot.c::start_secondary() for an example.
|
||||
*/
|
||||
@ -0,0 +1,99 @@
|
||||
From da6b353786997c0ffa67127355ad1d54ed3324c2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Thu, 23 Jan 2025 18:27:07 +0100
|
||||
Subject: [PATCH] pwm: Ensure callbacks exist before calling them
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
If one of the waveform functions is called for a chip that only supports
|
||||
.apply(), we want that an error code is returned and not a NULL pointer
|
||||
exception.
|
||||
|
||||
Fixes: 6c5126c6406d ("pwm: Provide new consumer API functions for waveforms")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Tested-by: Trevor Gamblin <tgamblin@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/20250123172709.391349-2-u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 13 +++++++++++--
|
||||
include/linux/pwm.h | 17 +++++++++++++++++
|
||||
2 files changed, 28 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -242,6 +242,9 @@ int pwm_round_waveform_might_sleep(struc
|
||||
|
||||
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
|
||||
+ if (!pwmchip_supports_waveform(chip))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
if (!pwm_wf_valid(wf))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -294,6 +297,9 @@ int pwm_get_waveform_might_sleep(struct
|
||||
|
||||
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
|
||||
+ if (!pwmchip_supports_waveform(chip) || !ops->read_waveform)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
guard(pwmchip)(chip);
|
||||
|
||||
if (!chip->operational)
|
||||
@@ -320,6 +326,9 @@ static int __pwm_set_waveform(struct pwm
|
||||
|
||||
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
|
||||
+ if (!pwmchip_supports_waveform(chip))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
if (!pwm_wf_valid(wf))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -592,7 +601,7 @@ static int __pwm_apply(struct pwm_device
|
||||
state->usage_power == pwm->state.usage_power)
|
||||
return 0;
|
||||
|
||||
- if (ops->write_waveform) {
|
||||
+ if (pwmchip_supports_waveform(chip)) {
|
||||
struct pwm_waveform wf;
|
||||
char wfhw[WFHWSIZE];
|
||||
|
||||
@@ -728,7 +737,7 @@ static int pwm_get_state_hw(struct pwm_d
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
- if (ops->read_waveform) {
|
||||
+ if (pwmchip_supports_waveform(chip) && ops->read_waveform) {
|
||||
char wfhw[WFHWSIZE];
|
||||
struct pwm_waveform wf;
|
||||
|
||||
--- a/include/linux/pwm.h
|
||||
+++ b/include/linux/pwm.h
|
||||
@@ -342,6 +342,23 @@ struct pwm_chip {
|
||||
struct pwm_device pwms[] __counted_by(npwm);
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * pwmchip_supports_waveform() - checks if the given chip supports waveform callbacks
|
||||
+ * @chip: The pwm_chip to test
|
||||
+ *
|
||||
+ * Returns true iff the pwm chip support the waveform functions like
|
||||
+ * pwm_set_waveform_might_sleep() and pwm_round_waveform_might_sleep()
|
||||
+ */
|
||||
+static inline bool pwmchip_supports_waveform(struct pwm_chip *chip)
|
||||
+{
|
||||
+ /*
|
||||
+ * only check for .write_waveform(). If that is available,
|
||||
+ * .round_waveform_tohw() and .round_waveform_fromhw() asserted to be
|
||||
+ * available, too, in pwmchip_add().
|
||||
+ */
|
||||
+ return chip->ops->write_waveform != NULL;
|
||||
+}
|
||||
+
|
||||
static inline struct device *pwmchip_parent(const struct pwm_chip *chip)
|
||||
{
|
||||
return chip->dev.parent;
|
||||
@ -0,0 +1,61 @@
|
||||
From 895fe4537cc8586f51abb5c66524efaa42c29883 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Thu, 6 Feb 2025 13:06:25 +0100
|
||||
Subject: [PATCH] pwm: Add upgrade path to #pwm-cells = <3> for users of
|
||||
of_pwm_single_xlate()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The PWM chip on PXA only has a single output. Back when the device tree
|
||||
binding was defined it was considered a good idea to not pass the PWM
|
||||
line index as is done for all other PWM types as it would be always zero
|
||||
anyhow and so doesn't add any value.
|
||||
|
||||
However for consistency reasons it is nice when all PWMs use the same
|
||||
binding. For that reason let of_pwm_single_xlate() (i.e. the function
|
||||
that implements the PXA behaviour) behave in the same way as
|
||||
of_pwm_xlate_with_flags() for 3 (or more) parameters. With that in
|
||||
place, the pxa-pwm binding can be updated to #pwm-cells = <3> without
|
||||
breaking old device trees that stick to #pwm-cells = <1>.
|
||||
|
||||
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
|
||||
Tested-by: Duje Mihanović <duje.mihanovic@skole.hr>
|
||||
Reviewed-by: Daniel Mack <daniel@zonque.org>
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/b33a84d3f073880e94fc303cd32ebe095eb5ce46.1738842938.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 16 ++++++++++++++++
|
||||
1 file changed, 16 insertions(+)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -988,11 +988,27 @@ of_pwm_xlate_with_flags(struct pwm_chip
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
|
||||
|
||||
+/*
|
||||
+ * This callback is used for PXA PWM chips that only have a single PWM line.
|
||||
+ * For such chips you could argue that passing the line number (i.e. the first
|
||||
+ * parameter in the common case) is useless as it's always zero. So compared to
|
||||
+ * the default xlate function of_pwm_xlate_with_flags() the first parameter is
|
||||
+ * the default period and the second are flags.
|
||||
+ *
|
||||
+ * Note that if #pwm-cells = <3>, the semantic is the same as for
|
||||
+ * of_pwm_xlate_with_flags() to allow converting the affected driver to
|
||||
+ * #pwm-cells = <3> without breaking the legacy binding.
|
||||
+ *
|
||||
+ * Don't use for new drivers.
|
||||
+ */
|
||||
struct pwm_device *
|
||||
of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
|
||||
+ if (args->args_count >= 3)
|
||||
+ return of_pwm_xlate_with_flags(chip, args);
|
||||
+
|
||||
pwm = pwm_request_from_chip(chip, 0, NULL);
|
||||
if (IS_ERR(pwm))
|
||||
return pwm;
|
||||
@ -0,0 +1,71 @@
|
||||
From 00e53d0f4baedd72196b65f00698b2a5a537dc2b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Sat, 5 Apr 2025 11:27:12 +0200
|
||||
Subject: [PATCH] pwm: Let pwm_set_waveform() succeed even if lowlevel driver
|
||||
rounded up
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Waveform parameters are supposed to be rounded down to the next value
|
||||
possible for the hardware. However when a requested value is too small,
|
||||
.round_waveform_tohw() is supposed to pick the next bigger value and
|
||||
return 1. Let pwm_set_waveform() behave in the same way.
|
||||
|
||||
This creates consistency between pwm_set_waveform_might_sleep() with
|
||||
exact=false and pwm_round_waveform_might_sleep() +
|
||||
pwm_set_waveform_might_sleep() with exact=true.
|
||||
|
||||
The PWM_DEBUG rounding check has to be adapted to only trigger if no
|
||||
uprounding happend.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Tested-by: Trevor Gamblin <tgamblin@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/353dc6ae31be815e41fd3df89c257127ca0d1a09.1743844730.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 13 +++++++------
|
||||
1 file changed, 7 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -322,7 +322,7 @@ static int __pwm_set_waveform(struct pwm
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
char wfhw[WFHWSIZE];
|
||||
struct pwm_waveform wf_rounded;
|
||||
- int err;
|
||||
+ int err, ret_tohw;
|
||||
|
||||
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
|
||||
|
||||
@@ -332,16 +332,16 @@ static int __pwm_set_waveform(struct pwm
|
||||
if (!pwm_wf_valid(wf))
|
||||
return -EINVAL;
|
||||
|
||||
- err = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw);
|
||||
- if (err)
|
||||
- return err;
|
||||
+ ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw);
|
||||
+ if (ret_tohw < 0)
|
||||
+ return ret_tohw;
|
||||
|
||||
if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) {
|
||||
err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- if (IS_ENABLED(CONFIG_PWM_DEBUG) && !pwm_check_rounding(wf, &wf_rounded))
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw == 0 && !pwm_check_rounding(wf, &wf_rounded))
|
||||
dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
|
||||
wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
|
||||
@@ -382,7 +382,8 @@ static int __pwm_set_waveform(struct pwm
|
||||
wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns,
|
||||
wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns);
|
||||
}
|
||||
- return 0;
|
||||
+
|
||||
+ return ret_tohw;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -0,0 +1,54 @@
|
||||
From 96d20cfd16e779923153f7347b0bef6b3c7606ce Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Sat, 5 Apr 2025 11:27:17 +0200
|
||||
Subject: [PATCH] pwm: Do stricter return value checking for
|
||||
.round_waveform_tohw()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The .round_waveform_tohw() is supposed to return 0 if the request could
|
||||
be rounded down to match the hardware capabilities and return 1 if
|
||||
rounding down wasn't possible.
|
||||
|
||||
Expand the PWM_DEBUG check to not only assert proper downrounding if 0
|
||||
was returned but also check that it was actually rounded up when the
|
||||
callback signalled uprounding.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/dfb824ae37f99df068c752d48cbd163c044a74fb.1743844730.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 12 ++++++------
|
||||
1 file changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -270,10 +270,10 @@ int pwm_round_waveform_might_sleep(struc
|
||||
wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PWM_DEBUG) &&
|
||||
- ret_tohw == 0 && !pwm_check_rounding(&wf_req, wf))
|
||||
- dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
+ (ret_tohw == 0) != pwm_check_rounding(&wf_req, wf))
|
||||
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n",
|
||||
wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns,
|
||||
- wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns);
|
||||
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, ret_tohw);
|
||||
|
||||
return ret_tohw;
|
||||
}
|
||||
@@ -341,10 +341,10 @@ static int __pwm_set_waveform(struct pwm
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw == 0 && !pwm_check_rounding(wf, &wf_rounded))
|
||||
- dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
|
||||
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(wf, &wf_rounded))
|
||||
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n",
|
||||
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
|
||||
- wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
|
||||
+ wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, ret_tohw);
|
||||
|
||||
if (exact && pwmwfcmp(wf, &wf_rounded)) {
|
||||
dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n",
|
||||
@ -0,0 +1,38 @@
|
||||
From e463b05d10da12b13d03f41a407e2ad043af158f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Tue, 8 Apr 2025 16:23:54 +0200
|
||||
Subject: [PATCH] pwm: Better document return value of
|
||||
pwm_round_waveform_might_sleep()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Better explain how pwm_round_waveform_might_sleep() (and so the
|
||||
respective lowlevel driver callback) is supposed to round and the
|
||||
meaning of the return value.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/db84abf1e82e4498fc0e7c318d2673771d0039fe.1744120697.git.ukleinek@kernel.org
|
||||
[ukleinek: Fix a rst formatting issue reported by Stephen Rothwell]
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -229,8 +229,12 @@ static int __pwm_write_waveform(struct p
|
||||
* these two calls and the waveform determined by
|
||||
* pwm_round_waveform_might_sleep() cannot be implemented any more.
|
||||
*
|
||||
- * Returns 0 on success, 1 if there is no valid hardware configuration matching
|
||||
- * the input waveform under the PWM rounding rules or a negative errno.
|
||||
+ * Usually all values passed in @wf are rounded down to the nearest possible
|
||||
+ * value (in the order period_length_ns, duty_length_ns and then
|
||||
+ * duty_offset_ns). Only if this isn't possible, a value might grow.
|
||||
+ *
|
||||
+ * Returns 0 on success, 1 if at least one value had to be rounded up or a
|
||||
+ * negative errno.
|
||||
*/
|
||||
int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
|
||||
{
|
||||
@ -0,0 +1,157 @@
|
||||
From 7f8ce4d88b42fcbd3350370ec4d02e00979fc5a9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Thu, 17 Apr 2025 20:16:11 +0200
|
||||
Subject: [PATCH] pwm: Fix various formatting issues in kernel-doc
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add Return and (where interesting) Context sections, fix some formatting
|
||||
and drop documenting the internal function __pwm_apply().
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/20250417181611.2693599-2-u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 42 +++++++++++++++++++++++++++---------------
|
||||
include/linux/pwm.h | 8 +++++---
|
||||
2 files changed, 32 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -216,14 +216,14 @@ static int __pwm_write_waveform(struct p
|
||||
*
|
||||
* Typically a given waveform cannot be implemented exactly by hardware, e.g.
|
||||
* because hardware only supports coarse period resolution or no duty_offset.
|
||||
- * This function returns the actually implemented waveform if you pass wf to
|
||||
- * pwm_set_waveform_might_sleep now.
|
||||
+ * This function returns the actually implemented waveform if you pass @wf to
|
||||
+ * pwm_set_waveform_might_sleep() now.
|
||||
*
|
||||
* Note however that the world doesn't stop turning when you call it, so when
|
||||
- * doing
|
||||
+ * doing::
|
||||
*
|
||||
- * pwm_round_waveform_might_sleep(mypwm, &wf);
|
||||
- * pwm_set_waveform_might_sleep(mypwm, &wf, true);
|
||||
+ * pwm_round_waveform_might_sleep(mypwm, &wf);
|
||||
+ * pwm_set_waveform_might_sleep(mypwm, &wf, true);
|
||||
*
|
||||
* the latter might fail, e.g. because an input clock changed its rate between
|
||||
* these two calls and the waveform determined by
|
||||
@@ -233,8 +233,9 @@ static int __pwm_write_waveform(struct p
|
||||
* value (in the order period_length_ns, duty_length_ns and then
|
||||
* duty_offset_ns). Only if this isn't possible, a value might grow.
|
||||
*
|
||||
- * Returns 0 on success, 1 if at least one value had to be rounded up or a
|
||||
+ * Returns: 0 on success, 1 if at least one value had to be rounded up or a
|
||||
* negative errno.
|
||||
+ * Context: May sleep.
|
||||
*/
|
||||
int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
|
||||
{
|
||||
@@ -291,6 +292,9 @@ EXPORT_SYMBOL_GPL(pwm_round_waveform_mig
|
||||
*
|
||||
* Stores the current configuration of the PWM in @wf. Note this is the
|
||||
* equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform.
|
||||
+ *
|
||||
+ * Returns: 0 on success or a negative errno
|
||||
+ * Context: May sleep.
|
||||
*/
|
||||
int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
|
||||
{
|
||||
@@ -399,13 +403,17 @@ static int __pwm_set_waveform(struct pwm
|
||||
*
|
||||
* Typically a requested waveform cannot be implemented exactly, e.g. because
|
||||
* you requested .period_length_ns = 100 ns, but the hardware can only set
|
||||
- * periods that are a multiple of 8.5 ns. With that hardware passing exact =
|
||||
+ * periods that are a multiple of 8.5 ns. With that hardware passing @exact =
|
||||
* true results in pwm_set_waveform_might_sleep() failing and returning 1. If
|
||||
- * exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger
|
||||
+ * @exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger
|
||||
* than the requested value).
|
||||
- * Note that even with exact = true, some rounding by less than 1 is
|
||||
+ * Note that even with @exact = true, some rounding by less than 1 ns is
|
||||
* possible/needed. In the above example requesting .period_length_ns = 94 and
|
||||
- * exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
+ * @exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
+ *
|
||||
+ * Returns: 0 on success, 1 if was rounded up (if !@exact) or no perfect match was
|
||||
+ * possible (if @exact), or a negative errno
|
||||
+ * Context: May sleep.
|
||||
*/
|
||||
int pwm_set_waveform_might_sleep(struct pwm_device *pwm,
|
||||
const struct pwm_waveform *wf, bool exact)
|
||||
@@ -565,11 +573,6 @@ static bool pwm_state_valid(const struct
|
||||
return true;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * __pwm_apply() - atomically apply a new state to a PWM device
|
||||
- * @pwm: PWM device
|
||||
- * @state: new state to apply
|
||||
- */
|
||||
static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
struct pwm_chip *chip;
|
||||
@@ -678,6 +681,9 @@ static int __pwm_apply(struct pwm_device
|
||||
* Cannot be used in atomic context.
|
||||
* @pwm: PWM device
|
||||
* @state: new state to apply
|
||||
+ *
|
||||
+ * Returns: 0 on success, or a negative errno
|
||||
+ * Context: May sleep.
|
||||
*/
|
||||
int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
@@ -719,6 +725,9 @@ EXPORT_SYMBOL_GPL(pwm_apply_might_sleep)
|
||||
* Not all PWM devices support this function, check with pwm_might_sleep().
|
||||
* @pwm: PWM device
|
||||
* @state: new state to apply
|
||||
+ *
|
||||
+ * Returns: 0 on success, or a negative errno
|
||||
+ * Context: Any
|
||||
*/
|
||||
int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state)
|
||||
{
|
||||
@@ -778,6 +787,9 @@ static int pwm_get_state_hw(struct pwm_d
|
||||
* This function will adjust the PWM config to the PWM arguments provided
|
||||
* by the DT or PWM lookup table. This is particularly useful to adapt
|
||||
* the bootloader config to the Linux one.
|
||||
+ *
|
||||
+ * Returns: 0 on success or a negative error code on failure.
|
||||
+ * Context: May sleep.
|
||||
*/
|
||||
int pwm_adjust_config(struct pwm_device *pwm)
|
||||
{
|
||||
--- a/include/linux/pwm.h
|
||||
+++ b/include/linux/pwm.h
|
||||
@@ -218,6 +218,8 @@ static inline void pwm_init_state(const
|
||||
*
|
||||
* pwm_get_state(pwm, &state);
|
||||
* duty = pwm_get_relative_duty_cycle(&state, 100);
|
||||
+ *
|
||||
+ * Returns: rounded relative duty cycle multiplied by @scale
|
||||
*/
|
||||
static inline unsigned int
|
||||
pwm_get_relative_duty_cycle(const struct pwm_state *state, unsigned int scale)
|
||||
@@ -244,8 +246,8 @@ pwm_get_relative_duty_cycle(const struct
|
||||
* pwm_set_relative_duty_cycle(&state, 50, 100);
|
||||
* pwm_apply_might_sleep(pwm, &state);
|
||||
*
|
||||
- * This functions returns -EINVAL if @duty_cycle and/or @scale are
|
||||
- * inconsistent (@scale == 0 or @duty_cycle > @scale).
|
||||
+ * Returns: 0 on success or ``-EINVAL`` if @duty_cycle and/or @scale are
|
||||
+ * inconsistent (@scale == 0 or @duty_cycle > @scale)
|
||||
*/
|
||||
static inline int
|
||||
pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
|
||||
@@ -346,7 +348,7 @@ struct pwm_chip {
|
||||
* pwmchip_supports_waveform() - checks if the given chip supports waveform callbacks
|
||||
* @chip: The pwm_chip to test
|
||||
*
|
||||
- * Returns true iff the pwm chip support the waveform functions like
|
||||
+ * Returns: true iff the pwm chip support the waveform functions like
|
||||
* pwm_set_waveform_might_sleep() and pwm_round_waveform_might_sleep()
|
||||
*/
|
||||
static inline bool pwmchip_supports_waveform(struct pwm_chip *chip)
|
||||
@ -0,0 +1,64 @@
|
||||
From e866834c8baabc33b431902beeeb0c94dfbc1024 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Wed, 30 Apr 2025 13:55:58 +0200
|
||||
Subject: [PATCH] pwm: Let pwm_set_waveform_might_sleep() fail for exact but
|
||||
impossible requests
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Up to now pwm_set_waveform_might_sleep() returned 1 for exact requests
|
||||
that couldn't be served exactly. In contrast to
|
||||
pwm_round_waveform_might_sleep() and pwm_set_waveform_might_sleep() with
|
||||
exact = false this is an error condition. So simplify handling for
|
||||
callers of pwm_set_waveform_might_sleep() by returning -EDOM instead of
|
||||
1 in this case.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/20538a46719584dafd8a1395c886780a97dcdf79.1746010245.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 21 ++++++++++++++++-----
|
||||
1 file changed, 16 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -404,15 +404,16 @@ static int __pwm_set_waveform(struct pwm
|
||||
* Typically a requested waveform cannot be implemented exactly, e.g. because
|
||||
* you requested .period_length_ns = 100 ns, but the hardware can only set
|
||||
* periods that are a multiple of 8.5 ns. With that hardware passing @exact =
|
||||
- * true results in pwm_set_waveform_might_sleep() failing and returning 1. If
|
||||
- * @exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger
|
||||
- * than the requested value).
|
||||
+ * true results in pwm_set_waveform_might_sleep() failing and returning -EDOM.
|
||||
+ * If @exact = false you get a period of 93.5 ns (i.e. the biggest period not
|
||||
+ * bigger than the requested value).
|
||||
* Note that even with @exact = true, some rounding by less than 1 ns is
|
||||
* possible/needed. In the above example requesting .period_length_ns = 94 and
|
||||
* @exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
*
|
||||
- * Returns: 0 on success, 1 if was rounded up (if !@exact) or no perfect match was
|
||||
- * possible (if @exact), or a negative errno
|
||||
+ * Returns: 0 on success, 1 if was rounded up (if !@exact), -EDOM if setting
|
||||
+ * failed due to the exact waveform not being possible (if @exact), or a
|
||||
+ * different negative errno on failure.
|
||||
* Context: May sleep.
|
||||
*/
|
||||
int pwm_set_waveform_might_sleep(struct pwm_device *pwm,
|
||||
@@ -440,6 +441,16 @@ int pwm_set_waveform_might_sleep(struct
|
||||
err = __pwm_set_waveform(pwm, wf, exact);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * map err == 1 to -EDOM for exact requests. Also make sure that -EDOM is
|
||||
+ * only returned in exactly that case. Note that __pwm_set_waveform()
|
||||
+ * should never return -EDOM which justifies the unlikely().
|
||||
+ */
|
||||
+ if (unlikely(err == -EDOM))
|
||||
+ err = -EINVAL;
|
||||
+ else if (exact && err == 1)
|
||||
+ err = -EDOM;
|
||||
+
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep);
|
||||
@ -0,0 +1,58 @@
|
||||
From 164c4ac754abaf9643815d09001cc7d81042d624 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Wed, 30 Apr 2025 13:55:59 +0200
|
||||
Subject: [PATCH] pwm: Let pwm_set_waveform_might_sleep() return 0 instead of 1
|
||||
after rounding up
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
While telling the caller of pwm_set_waveform_might_sleep() if the
|
||||
request was completed by rounding down only or (some) rounding up gives
|
||||
additional information, it makes usage this function needlessly hard and
|
||||
the additional information is not used. A prove for that is that
|
||||
currently both users of this function just pass the returned value up to
|
||||
their caller even though a positive value isn't intended there.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/528cc3bbd9e35dea8646b1bcc0fbfe6c498bb4ed.1746010245.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 14 ++++++++------
|
||||
1 file changed, 8 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -411,9 +411,8 @@ static int __pwm_set_waveform(struct pwm
|
||||
* possible/needed. In the above example requesting .period_length_ns = 94 and
|
||||
* @exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
*
|
||||
- * Returns: 0 on success, 1 if was rounded up (if !@exact), -EDOM if setting
|
||||
- * failed due to the exact waveform not being possible (if @exact), or a
|
||||
- * different negative errno on failure.
|
||||
+ * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not
|
||||
+ * being possible (if @exact), or a different negative errno on failure.
|
||||
* Context: May sleep.
|
||||
*/
|
||||
int pwm_set_waveform_might_sleep(struct pwm_device *pwm,
|
||||
@@ -442,14 +441,17 @@ int pwm_set_waveform_might_sleep(struct
|
||||
}
|
||||
|
||||
/*
|
||||
- * map err == 1 to -EDOM for exact requests. Also make sure that -EDOM is
|
||||
- * only returned in exactly that case. Note that __pwm_set_waveform()
|
||||
- * should never return -EDOM which justifies the unlikely().
|
||||
+ * map err == 1 to -EDOM for exact requests and 0 for !exact ones. Also
|
||||
+ * make sure that -EDOM is only returned in exactly that case. Note that
|
||||
+ * __pwm_set_waveform() should never return -EDOM which justifies the
|
||||
+ * unlikely().
|
||||
*/
|
||||
if (unlikely(err == -EDOM))
|
||||
err = -EINVAL;
|
||||
else if (exact && err == 1)
|
||||
err = -EDOM;
|
||||
+ else if (err == 1)
|
||||
+ err = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
From d041b76ac9fb9e60e7cdb0265ed9d8b6058a88bf Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@baylibre.com>
|
||||
Date: Wed, 30 Apr 2025 13:56:00 +0200
|
||||
Subject: [PATCH] pwm: Formally describe the procedure used to pick a hardware
|
||||
waveform setting
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This serves as specification for both, PWM consumers and the respective
|
||||
callback for lowlevel drivers.
|
||||
|
||||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
|
||||
Link: https://lore.kernel.org/r/d2916bfa70274961ded26b07ab6998c36b90e69a.1746010245.git.u.kleine-koenig@baylibre.com
|
||||
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
|
||||
---
|
||||
drivers/pwm/core.c | 24 +++++++++++++++++++++++-
|
||||
1 file changed, 23 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/pwm/core.c
|
||||
+++ b/drivers/pwm/core.c
|
||||
@@ -231,7 +231,9 @@ static int __pwm_write_waveform(struct p
|
||||
*
|
||||
* Usually all values passed in @wf are rounded down to the nearest possible
|
||||
* value (in the order period_length_ns, duty_length_ns and then
|
||||
- * duty_offset_ns). Only if this isn't possible, a value might grow.
|
||||
+ * duty_offset_ns). Only if this isn't possible, a value might grow. See the
|
||||
+ * documentation for pwm_set_waveform_might_sleep() for a more formal
|
||||
+ * description.
|
||||
*
|
||||
* Returns: 0 on success, 1 if at least one value had to be rounded up or a
|
||||
* negative errno.
|
||||
@@ -411,6 +413,26 @@ static int __pwm_set_waveform(struct pwm
|
||||
* possible/needed. In the above example requesting .period_length_ns = 94 and
|
||||
* @exact = true, you get the hardware configured with period = 93.5 ns.
|
||||
*
|
||||
+ * Let C be the set of possible hardware configurations for a given PWM device,
|
||||
+ * consisting of tuples (p, d, o) where p is the period length, d is the duty
|
||||
+ * length and o the duty offset.
|
||||
+ *
|
||||
+ * The following algorithm is implemented to pick the hardware setting
|
||||
+ * (p, d, o) ∈ C for a given request (p', d', o') with @exact = false::
|
||||
+ *
|
||||
+ * p = max( { ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C ∧ ṗ ≤ p' } ∪ { min({ ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C }) })
|
||||
+ * d = max( { ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C ∧ ḋ ≤ d' } ∪ { min({ ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C }) })
|
||||
+ * o = max( { ȯ | (p, d, ȯ) ∈ C ∧ ȯ ≤ o' } ∪ { min({ ȯ | (p, d, ȯ) ∈ C }) })
|
||||
+ *
|
||||
+ * In words: The chosen period length is the maximal possible period length not
|
||||
+ * bigger than the requested period length and if that doesn't exist, the
|
||||
+ * minimal period length. The chosen duty length is the maximal possible duty
|
||||
+ * length that is compatible with the chosen period length and isn't bigger than
|
||||
+ * the requested duty length. Again if such a value doesn't exist, the minimal
|
||||
+ * duty length compatible with the chosen period is picked. After that the duty
|
||||
+ * offset compatible with the chosen period and duty length is chosen in the
|
||||
+ * same way.
|
||||
+ *
|
||||
* Returns: 0 on success, -EDOM if setting failed due to the exact waveform not
|
||||
* being possible (if @exact), or a different negative errno on failure.
|
||||
* Context: May sleep.
|
||||
@ -0,0 +1,877 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v3 2/5] mfd: Add Rockchip mfpwm driver
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Date: Mon, 27 Oct 2025 18:11:57 +0100
|
||||
Message-Id: <20251027-rk3576-pwm-v3-2-654a5cb1e3f8@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
With the Rockchip RK3576, the PWM IP used by Rockchip has changed
|
||||
substantially. Looking at both the downstream pwm-rockchip driver as
|
||||
well as the mainline pwm-rockchip driver made it clear that with all its
|
||||
additional features and its differences from previous IP revisions, it
|
||||
is best supported in a new driver.
|
||||
|
||||
This brings us to the question as to what such a new driver should be.
|
||||
To me, it soon became clear that it should actually be several new
|
||||
drivers, most prominently when Uwe Kleine-König let me know that I
|
||||
should not implement the pwm subsystem's capture callback, but instead
|
||||
write a counter driver for this functionality.
|
||||
|
||||
Combined with the other as-of-yet unimplemented functionality of this
|
||||
new IP, it became apparent that it needs to be spread across several
|
||||
subsystems.
|
||||
|
||||
For this reason, we add a new MFD core driver, called mfpwm (short for
|
||||
"Multi-function PWM"). This "parent" driver makes sure that only one
|
||||
device function driver is using the device at a time, and is in charge
|
||||
of registering the MFD cell devices for the individual device functions
|
||||
offered by the device.
|
||||
|
||||
An acquire/release pattern is used to guarantee that device function
|
||||
drivers don't step on each other's toes.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
MAINTAINERS | 2 +
|
||||
drivers/mfd/Kconfig | 15 ++
|
||||
drivers/mfd/Makefile | 1 +
|
||||
drivers/mfd/rockchip-mfpwm.c | 340 +++++++++++++++++++++++++++
|
||||
include/linux/mfd/rockchip-mfpwm.h | 454 +++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 812 insertions(+)
|
||||
|
||||
--- a/drivers/mfd/Kconfig
|
||||
+++ b/drivers/mfd/Kconfig
|
||||
@@ -1244,6 +1244,21 @@ config MFD_RC5T583
|
||||
Additional drivers must be enabled in order to use the
|
||||
different functionality of the device.
|
||||
|
||||
+config MFD_ROCKCHIP_MFPWM
|
||||
+ tristate "Rockchip multi-function PWM controller"
|
||||
+ depends on OF
|
||||
+ depends on HAS_IOMEM
|
||||
+ depends on COMMON_CLK
|
||||
+ select MFD_CORE
|
||||
+ help
|
||||
+ Some Rockchip SoCs, such as the RK3576, use a PWM controller that has
|
||||
+ several different functions, such as generating PWM waveforms but also
|
||||
+ counting waveforms.
|
||||
+
|
||||
+ This driver manages the overall device, and selects between different
|
||||
+ functionalities at runtime as needed. Drivers for them are implemented
|
||||
+ in their respective subsystems.
|
||||
+
|
||||
config MFD_RK8XX
|
||||
tristate
|
||||
select MFD_CORE
|
||||
--- a/drivers/mfd/Makefile
|
||||
+++ b/drivers/mfd/Makefile
|
||||
@@ -227,6 +227,7 @@ obj-$(CONFIG_MFD_PALMAS) += palmas.o
|
||||
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
|
||||
obj-$(CONFIG_MFD_NTXEC) += ntxec.o
|
||||
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
|
||||
+obj-$(CONFIG_MFD_ROCKCHIP_MFPWM) += rockchip-mfpwm.o
|
||||
obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o
|
||||
obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o
|
||||
obj-$(CONFIG_MFD_RK8XX_SPI) += rk8xx-spi.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/mfd/rockchip-mfpwm.c
|
||||
@@ -0,0 +1,340 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (c) 2025 Collabora Ltd.
|
||||
+ *
|
||||
+ * A driver to manage all the different functionalities exposed by Rockchip's
|
||||
+ * PWMv4 hardware.
|
||||
+ *
|
||||
+ * This driver is chiefly focused on guaranteeing non-concurrent operation
|
||||
+ * between the different device functions, as well as setting the clocks.
|
||||
+ * It registers the device function platform devices, e.g. PWM output or
|
||||
+ * PWM capture.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/array_size.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/mfd/core.h>
|
||||
+#include <linux/mfd/rockchip-mfpwm.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/overflow.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+/**
|
||||
+ * struct rockchip_mfpwm - private mfpwm driver instance state struct
|
||||
+ * @pdev: pointer to this instance's &struct platform_device
|
||||
+ * @base: pointer to the memory mapped registers of this device
|
||||
+ * @pwm_clk: pointer to the PLL clock the PWM signal may be derived from
|
||||
+ * @osc_clk: pointer to the fixed crystal the PWM signal may be derived from
|
||||
+ * @rc_clk: pointer to the RC oscillator the PWM signal may be derived from
|
||||
+ * @chosen_clk: a clk-mux of pwm_clk, osc_clk and rc_clk
|
||||
+ * @pclk: pointer to the APB bus clock needed for mmio register access
|
||||
+ * @active_func: pointer to the currently active device function, or %NULL if no
|
||||
+ * device function is currently actively using any of the shared
|
||||
+ * resources. May only be checked/modified with @state_lock held.
|
||||
+ * @acquire_cnt: number of times @active_func has currently mfpwm_acquire()'d
|
||||
+ * it. Must only be checked or modified while holding @state_lock.
|
||||
+ * @state_lock: this lock is held while either the active device function, the
|
||||
+ * enable register, or the chosen clock is being changed.
|
||||
+ * @irq: the IRQ number of this device
|
||||
+ */
|
||||
+struct rockchip_mfpwm {
|
||||
+ struct platform_device *pdev;
|
||||
+ void __iomem *base;
|
||||
+ struct clk *pwm_clk;
|
||||
+ struct clk *osc_clk;
|
||||
+ struct clk *rc_clk;
|
||||
+ struct clk *chosen_clk;
|
||||
+ struct clk *pclk;
|
||||
+ struct rockchip_mfpwm_func *active_func;
|
||||
+ unsigned int acquire_cnt;
|
||||
+ spinlock_t state_lock;
|
||||
+ int irq;
|
||||
+};
|
||||
+
|
||||
+static atomic_t subdev_id = ATOMIC_INIT(0);
|
||||
+
|
||||
+static inline struct rockchip_mfpwm *to_rockchip_mfpwm(struct platform_device *pdev)
|
||||
+{
|
||||
+ return platform_get_drvdata(pdev);
|
||||
+}
|
||||
+
|
||||
+static int mfpwm_check_pwmf(const struct rockchip_mfpwm_func *pwmf,
|
||||
+ const char *fname)
|
||||
+{
|
||||
+ struct device *dev = &pwmf->parent->pdev->dev;
|
||||
+
|
||||
+ if (IS_ERR_OR_NULL(pwmf)) {
|
||||
+ dev_warn(dev, "called %s with an erroneous handle, no effect\n",
|
||||
+ fname);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ERR_OR_NULL(pwmf->parent)) {
|
||||
+ dev_warn(dev, "called %s with an erroneous mfpwm_func parent, no effect\n",
|
||||
+ fname);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+__attribute__((nonnull))
|
||||
+static int mfpwm_do_acquire(struct rockchip_mfpwm_func *pwmf)
|
||||
+{
|
||||
+ struct rockchip_mfpwm *mfpwm = pwmf->parent;
|
||||
+ unsigned int cnt;
|
||||
+
|
||||
+ if (mfpwm->active_func && pwmf->id != mfpwm->active_func->id)
|
||||
+ return -EBUSY;
|
||||
+
|
||||
+ if (!mfpwm->active_func)
|
||||
+ mfpwm->active_func = pwmf;
|
||||
+
|
||||
+ if (!check_add_overflow(mfpwm->acquire_cnt, 1, &cnt)) {
|
||||
+ mfpwm->acquire_cnt = cnt;
|
||||
+ } else {
|
||||
+ dev_warn(&mfpwm->pdev->dev, "prevented acquire counter overflow in %s\n",
|
||||
+ __func__);
|
||||
+ return -EOVERFLOW;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(&mfpwm->pdev->dev, "%d acquired mfpwm, acquires now at %u\n",
|
||||
+ pwmf->id, mfpwm->acquire_cnt);
|
||||
+
|
||||
+ return clk_enable(mfpwm->pclk);
|
||||
+}
|
||||
+
|
||||
+int mfpwm_acquire(struct rockchip_mfpwm_func *pwmf)
|
||||
+{
|
||||
+ struct rockchip_mfpwm *mfpwm;
|
||||
+ unsigned long flags;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ ret = mfpwm_check_pwmf(pwmf, "mfpwm_acquire");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mfpwm = pwmf->parent;
|
||||
+ dev_dbg(&mfpwm->pdev->dev, "%d is attempting to acquire\n", pwmf->id);
|
||||
+
|
||||
+ if (!spin_trylock_irqsave(&mfpwm->state_lock, flags))
|
||||
+ return -EBUSY;
|
||||
+
|
||||
+ ret = mfpwm_do_acquire(pwmf);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&mfpwm->state_lock, flags);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_NS_GPL(mfpwm_acquire, ROCKCHIP_MFPWM);
|
||||
+
|
||||
+__attribute__((nonnull))
|
||||
+static void mfpwm_do_release(const struct rockchip_mfpwm_func *pwmf)
|
||||
+{
|
||||
+ struct rockchip_mfpwm *mfpwm = pwmf->parent;
|
||||
+
|
||||
+ if (!mfpwm->active_func)
|
||||
+ return;
|
||||
+
|
||||
+ if (mfpwm->active_func->id != pwmf->id)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * No need to check_sub_overflow here, !mfpwm->active_func above catches
|
||||
+ * this type of problem already.
|
||||
+ */
|
||||
+ mfpwm->acquire_cnt--;
|
||||
+
|
||||
+ if (!mfpwm->acquire_cnt)
|
||||
+ mfpwm->active_func = NULL;
|
||||
+
|
||||
+ clk_disable(mfpwm->pclk);
|
||||
+}
|
||||
+
|
||||
+void mfpwm_release(const struct rockchip_mfpwm_func *pwmf)
|
||||
+{
|
||||
+ struct rockchip_mfpwm *mfpwm;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ if (mfpwm_check_pwmf(pwmf, "mfpwm_release"))
|
||||
+ return;
|
||||
+
|
||||
+ mfpwm = pwmf->parent;
|
||||
+
|
||||
+ spin_lock_irqsave(&mfpwm->state_lock, flags);
|
||||
+ mfpwm_do_release(pwmf);
|
||||
+ dev_dbg(&mfpwm->pdev->dev, "%d released mfpwm, acquires now at %u\n",
|
||||
+ pwmf->id, mfpwm->acquire_cnt);
|
||||
+ spin_unlock_irqrestore(&mfpwm->state_lock, flags);
|
||||
+}
|
||||
+EXPORT_SYMBOL_NS_GPL(mfpwm_release, ROCKCHIP_MFPWM);
|
||||
+
|
||||
+/**
|
||||
+ * mfpwm_register_subdev - register a single mfpwm_func
|
||||
+ * @mfpwm: pointer to the parent &struct rockchip_mfpwm
|
||||
+ * @name: sub-device name string
|
||||
+ *
|
||||
+ * Allocate a single &struct mfpwm_func, fill its members with appropriate data,
|
||||
+ * and register a new mfd cell.
|
||||
+ *
|
||||
+ * Returns: 0 on success, negative errno on error
|
||||
+ */
|
||||
+static int mfpwm_register_subdev(struct rockchip_mfpwm *mfpwm,
|
||||
+ const char *name)
|
||||
+{
|
||||
+ struct rockchip_mfpwm_func *func;
|
||||
+ struct mfd_cell cell = {};
|
||||
+
|
||||
+ func = devm_kzalloc(&mfpwm->pdev->dev, sizeof(*func), GFP_KERNEL);
|
||||
+ if (IS_ERR(func))
|
||||
+ return PTR_ERR(func);
|
||||
+ func->irq = mfpwm->irq;
|
||||
+ func->parent = mfpwm;
|
||||
+ func->id = atomic_inc_return(&subdev_id);
|
||||
+ func->base = mfpwm->base;
|
||||
+ func->core = mfpwm->chosen_clk;
|
||||
+ cell.name = name;
|
||||
+ cell.platform_data = func;
|
||||
+ cell.pdata_size = sizeof(*func);
|
||||
+ // cell.ignore_resource_conflicts = true;
|
||||
+ // cell.resources = mfpwm->pdev->resource;
|
||||
+ // cell.num_resources = mfpwm->pdev->num_resources;
|
||||
+
|
||||
+ return devm_mfd_add_devices(&mfpwm->pdev->dev, func->id, &cell, 1, NULL,
|
||||
+ 0, NULL);
|
||||
+}
|
||||
+
|
||||
+static int mfpwm_register_subdevs(struct rockchip_mfpwm *mfpwm)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = mfpwm_register_subdev(mfpwm, "pwm-rockchip-v4");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = mfpwm_register_subdev(mfpwm, "rockchip-pwm-capture");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_mfpwm_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct rockchip_mfpwm *mfpwm;
|
||||
+ char *clk_mux_name;
|
||||
+ const char *mux_p_names[3];
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mfpwm = devm_kzalloc(&pdev->dev, sizeof(*mfpwm), GFP_KERNEL);
|
||||
+ if (IS_ERR(mfpwm))
|
||||
+ return PTR_ERR(mfpwm);
|
||||
+
|
||||
+ mfpwm->pdev = pdev;
|
||||
+
|
||||
+ spin_lock_init(&mfpwm->state_lock);
|
||||
+
|
||||
+ mfpwm->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(mfpwm->base))
|
||||
+ return dev_err_probe(dev, PTR_ERR(mfpwm->base),
|
||||
+ "failed to ioremap address\n");
|
||||
+
|
||||
+ mfpwm->pclk = devm_clk_get_prepared(dev, "pclk");
|
||||
+ if (IS_ERR(mfpwm->pclk))
|
||||
+ return dev_err_probe(dev, PTR_ERR(mfpwm->pclk),
|
||||
+ "couldn't get and prepare 'pclk' clock\n");
|
||||
+
|
||||
+ mfpwm->irq = platform_get_irq(pdev, 0);
|
||||
+ if (mfpwm->irq < 0)
|
||||
+ return dev_err_probe(dev, mfpwm->irq, "couldn't get irq 0\n");
|
||||
+
|
||||
+ mfpwm->pwm_clk = devm_clk_get_prepared(dev, "pwm");
|
||||
+ if (IS_ERR(mfpwm->pwm_clk))
|
||||
+ return dev_err_probe(dev, PTR_ERR(mfpwm->pwm_clk),
|
||||
+ "couldn't get and prepare 'pwm' clock\n");
|
||||
+
|
||||
+ mfpwm->osc_clk = devm_clk_get_prepared(dev, "osc");
|
||||
+ if (IS_ERR(mfpwm->osc_clk))
|
||||
+ return dev_err_probe(dev, PTR_ERR(mfpwm->osc_clk),
|
||||
+ "couldn't get and prepare 'osc' clock\n");
|
||||
+
|
||||
+ mfpwm->rc_clk = devm_clk_get_prepared(dev, "rc");
|
||||
+ if (IS_ERR(mfpwm->rc_clk))
|
||||
+ return dev_err_probe(dev, PTR_ERR(mfpwm->rc_clk),
|
||||
+ "couldn't get and prepare 'rc' clock\n");
|
||||
+
|
||||
+ clk_mux_name = devm_kasprintf(dev, GFP_KERNEL, "%s_chosen", dev_name(dev));
|
||||
+ if (!clk_mux_name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ mux_p_names[0] = __clk_get_name(mfpwm->pwm_clk);
|
||||
+ mux_p_names[1] = __clk_get_name(mfpwm->osc_clk);
|
||||
+ mux_p_names[2] = __clk_get_name(mfpwm->rc_clk);
|
||||
+ mfpwm->chosen_clk = clk_register_mux(dev, clk_mux_name, mux_p_names,
|
||||
+ ARRAY_SIZE(mux_p_names),
|
||||
+ CLK_SET_RATE_PARENT,
|
||||
+ mfpwm->base + PWMV4_REG_CLK_CTRL,
|
||||
+ PWMV4_CLK_SRC_SHIFT, PWMV4_CLK_SRC_WIDTH,
|
||||
+ CLK_MUX_HIWORD_MASK, NULL);
|
||||
+ ret = clk_prepare(mfpwm->chosen_clk);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to prepare PWM clock mux: %pe\n",
|
||||
+ ERR_PTR(ret));
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ platform_set_drvdata(pdev, mfpwm);
|
||||
+
|
||||
+ ret = mfpwm_register_subdevs(mfpwm);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to register sub-devices: %pe\n",
|
||||
+ ERR_PTR(ret));
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rockchip_mfpwm_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rockchip_mfpwm *mfpwm = to_rockchip_mfpwm(pdev);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&mfpwm->state_lock, flags);
|
||||
+
|
||||
+ if (mfpwm->chosen_clk) {
|
||||
+ clk_unprepare(mfpwm->chosen_clk);
|
||||
+ clk_unregister_mux(mfpwm->chosen_clk);
|
||||
+ }
|
||||
+
|
||||
+ spin_unlock_irqrestore(&mfpwm->state_lock, flags);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id rockchip_mfpwm_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "rockchip,rk3576-pwm",
|
||||
+ },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rockchip_mfpwm_of_match);
|
||||
+
|
||||
+static struct platform_driver rockchip_mfpwm_driver = {
|
||||
+ .driver = {
|
||||
+ .name = KBUILD_MODNAME,
|
||||
+ .of_match_table = rockchip_mfpwm_of_match,
|
||||
+ },
|
||||
+ .probe = rockchip_mfpwm_probe,
|
||||
+ .remove = rockchip_mfpwm_remove,
|
||||
+};
|
||||
+module_platform_driver(rockchip_mfpwm_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
|
||||
+MODULE_DESCRIPTION("Rockchip MFPWM Driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
--- /dev/null
|
||||
+++ b/include/linux/mfd/rockchip-mfpwm.h
|
||||
@@ -0,0 +1,454 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
+/*
|
||||
+ * Copyright (c) 2025 Collabora Ltd.
|
||||
+ *
|
||||
+ * Common header file for all the Rockchip Multi-function PWM controller
|
||||
+ * drivers that are spread across subsystems.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
+ */
|
||||
+
|
||||
+#ifndef __SOC_ROCKCHIP_MFPWM_H__
|
||||
+#define __SOC_ROCKCHIP_MFPWM_H__
|
||||
+
|
||||
+#include <linux/bits.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/hw_bitfield.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+struct rockchip_mfpwm;
|
||||
+
|
||||
+/**
|
||||
+ * struct rockchip_mfpwm_func - struct representing a single function driver
|
||||
+ *
|
||||
+ * @id: unique id for this function driver instance
|
||||
+ * @base: pointer to start of MMIO registers
|
||||
+ * @parent: a pointer to the parent mfpwm struct
|
||||
+ * @irq: the shared IRQ gotten from the parent mfpwm device
|
||||
+ * @core: a pointer to the clk mux that drives this channel's PWM
|
||||
+ */
|
||||
+struct rockchip_mfpwm_func {
|
||||
+ int id;
|
||||
+ void __iomem *base;
|
||||
+ struct rockchip_mfpwm *parent;
|
||||
+ int irq;
|
||||
+ struct clk *core;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * PWMV4 Register Definitions
|
||||
+ * --------------------------
|
||||
+ *
|
||||
+ * Attributes:
|
||||
+ * RW - Read-Write
|
||||
+ * RO - Read-Only
|
||||
+ * WO - Write-Only
|
||||
+ * W1T - Write high, Self-clearing
|
||||
+ * W1C - Write high to clear interrupt
|
||||
+ *
|
||||
+ * Bit ranges to be understood with Verilog-like semantics,
|
||||
+ * e.g. [03:00] is 4 bits: 0, 1, 2 and 3.
|
||||
+ *
|
||||
+ * All registers must be accessed with 32-bit width accesses only
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_VERSION 0x000
|
||||
+/*
|
||||
+ * VERSION Register Description
|
||||
+ * [31:24] RO | Hardware Major Version
|
||||
+ * [23:16] RO | Hardware Minor Version
|
||||
+ * [15:15] RO | Reserved
|
||||
+ * [14:14] RO | Hardware supports biphasic counters
|
||||
+ * [13:13] RO | Hardware supports filters
|
||||
+ * [12:12] RO | Hardware supports waveform generation
|
||||
+ * [11:11] RO | Hardware supports counter
|
||||
+ * [10:10] RO | Hardware supports frequency metering
|
||||
+ * [09:09] RO | Hardware supports power key functionality
|
||||
+ * [08:08] RO | Hardware supports infrared transmissions
|
||||
+ * [07:04] RO | Channel index of this instance
|
||||
+ * [03:00] RO | Number of channels the base instance supports
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_ENABLE 0x004
|
||||
+/*
|
||||
+ * ENABLE Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:06] RO | Reserved
|
||||
+ * [05:05] RW | PWM Channel Counter Read Enable, 1 = enabled
|
||||
+ */
|
||||
+#define PWMV4_CHN_CNT_RD_EN(v) FIELD_PREP_WM16(BIT(5), (v))
|
||||
+/*
|
||||
+ * [04:04] W1T | PWM Globally Joined Control Enable
|
||||
+ * 1 = this PWM channel will be enabled by a global pwm enable
|
||||
+ * bit instead of the PWM Enable bit.
|
||||
+ */
|
||||
+#define PWMV4_GLOBAL_CTRL_EN(v) FIELD_PREP_WM16(BIT(4), (v))
|
||||
+/*
|
||||
+ * [03:03] RW | Force Clock Enable
|
||||
+ * 0 = disabled, if the PWM channel is inactive then so is the
|
||||
+ * clock prescale module
|
||||
+ */
|
||||
+#define PWMV4_FORCE_CLK_EN(v) FIELD_PREP_WM16(BIT(3), (v))
|
||||
+/*
|
||||
+ * [02:02] W1T | PWM Control Update Enable
|
||||
+ * 1 = enabled, commits modifications of _CTRL, _PERIOD, _DUTY and
|
||||
+ * _OFFSET registers once 1 is written to it
|
||||
+ */
|
||||
+#define PWMV4_CTRL_UPDATE_EN FIELD_PREP_WM16_CONST(BIT(2), 1)
|
||||
+/*
|
||||
+ * [01:01] RW | PWM Enable, 1 = enabled
|
||||
+ * If in one-shot mode, clears after end of operation
|
||||
+ */
|
||||
+#define PWMV4_EN_MASK BIT(1)
|
||||
+#define PWMV4_EN(v) FIELD_PREP_WM16(PWMV4_EN_MASK, \
|
||||
+ ((v) ? 1 : 0))
|
||||
+/*
|
||||
+ * [00:00] RW | PWM Clock Enable, 1 = enabled
|
||||
+ * If in one-shot mode, clears after end of operation
|
||||
+ */
|
||||
+#define PWMV4_CLK_EN_MASK BIT(0)
|
||||
+#define PWMV4_CLK_EN(v) FIELD_PREP_WM16(PWMV4_CLK_EN_MASK, \
|
||||
+ ((v) ? 1 : 0))
|
||||
+#define PWMV4_EN_BOTH_MASK (PWMV4_EN_MASK | PWMV4_CLK_EN_MASK)
|
||||
+static inline __pure bool rockchip_pwm_v4_is_enabled(unsigned int val)
|
||||
+{
|
||||
+ return (val & PWMV4_EN_BOTH_MASK);
|
||||
+}
|
||||
+
|
||||
+#define PWMV4_REG_CLK_CTRL 0x008
|
||||
+/*
|
||||
+ * CLK_CTRL Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:15] RW | Clock Global Selection
|
||||
+ * 0 = current channel scale clock
|
||||
+ * 1 = global channel scale clock
|
||||
+ */
|
||||
+#define PWMV4_CLK_GLOBAL(v) FIELD_PREP_WM16(BIT(15), (v))
|
||||
+/*
|
||||
+ * [14:13] RW | Clock Source Selection
|
||||
+ * 0 = Clock from PLL, frequency can be configured
|
||||
+ * 1 = Clock from crystal oscillator, frequency is fixed
|
||||
+ * 2 = Clock from RC oscillator, frequency is fixed
|
||||
+ * 3 = Reserved
|
||||
+ * NOTE: The purpose for this clock-mux-outside-CRU construct is
|
||||
+ * to let the SoC go into a sleep state with the PWM
|
||||
+ * hardware still having a clock signal for IR input, which
|
||||
+ * can then wake up the SoC.
|
||||
+ */
|
||||
+#define PWMV4_CLK_SRC_PLL 0x0U
|
||||
+#define PWMV4_CLK_SRC_CRYSTAL 0x1U
|
||||
+#define PWMV4_CLK_SRC_RC 0x2U
|
||||
+#define PWMV4_CLK_SRC_SHIFT 13
|
||||
+#define PWMV4_CLK_SRC_WIDTH 2
|
||||
+/*
|
||||
+ * [12:04] RW | Scale Factor to apply to pre-scaled clock
|
||||
+ * 1 <= v <= 256, v means clock divided by 2*v
|
||||
+ */
|
||||
+#define PWMV4_CLK_SCALE_F(v) FIELD_PREP_WM16(GENMASK(12, 4), (v))
|
||||
+/*
|
||||
+ * [03:03] RO | Reserved
|
||||
+ * [02:00] RW | Prescale Factor
|
||||
+ * v here means the input clock is divided by pow(2, v)
|
||||
+ */
|
||||
+#define PWMV4_CLK_PRESCALE_F(v) FIELD_PREP_WM16(GENMASK(2, 0), (v))
|
||||
+
|
||||
+#define PWMV4_REG_CTRL 0x00C
|
||||
+/*
|
||||
+ * CTRL Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:09] RO | Reserved
|
||||
+ * [08:06] RW | PWM Input Channel Selection
|
||||
+ * By default, the channel selects its own input, but writing v
|
||||
+ * here selects PWM input from channel v instead.
|
||||
+ */
|
||||
+#define PWMV4_CTRL_IN_SEL(v) FIELD_PREP_WM16(GENMASK(8, 6), (v))
|
||||
+/* [05:05] RW | Aligned Mode, 0 = Valid, 1 = Invalid */
|
||||
+#define PWMV4_CTRL_UNALIGNED(v) FIELD_PREP_WM16(BIT(5), (v))
|
||||
+/* [04:04] RW | Output Mode, 0 = Left Aligned, 1 = Centre Aligned */
|
||||
+#define PWMV4_LEFT_ALIGNED 0x0U
|
||||
+#define PWMV4_CENTRE_ALIGNED 0x1U
|
||||
+#define PWMV4_CTRL_OUT_MODE(v) FIELD_PREP_WM16(BIT(4), (v))
|
||||
+/*
|
||||
+ * [03:03] RW | Inactive Polarity for when the channel is either disabled or
|
||||
+ * has completed outputting the entire waveform in one-shot mode.
|
||||
+ * 0 = Negative, 1 = Positive
|
||||
+ */
|
||||
+#define PWMV4_POLARITY_N 0x0U
|
||||
+#define PWMV4_POLARITY_P 0x1U
|
||||
+#define PWMV4_INACTIVE_POL(v) FIELD_PREP_WM16(BIT(3), (v))
|
||||
+/*
|
||||
+ * [02:02] RW | Duty Cycle Polarity to use at the start of the waveform.
|
||||
+ * 0 = Negative, 1 = Positive
|
||||
+ */
|
||||
+#define PWMV4_DUTY_POL_SHIFT 2
|
||||
+#define PWMV4_DUTY_POL_MASK BIT(PWMV4_DUTY_POL_SHIFT)
|
||||
+#define PWMV4_DUTY_POL(v) FIELD_PREP_WM16(PWMV4_DUTY_POL_MASK, \
|
||||
+ (v))
|
||||
+/*
|
||||
+ * [01:00] RW | PWM Mode
|
||||
+ * 0 = One-shot mode, PWM generates waveform RPT times
|
||||
+ * 1 = Continuous mode
|
||||
+ * 2 = Capture mode, PWM measures cycles of input waveform
|
||||
+ * 3 = Reserved
|
||||
+ */
|
||||
+#define PWMV4_MODE_ONESHOT 0x0U
|
||||
+#define PWMV4_MODE_CONT 0x1U
|
||||
+#define PWMV4_MODE_CAPTURE 0x2U
|
||||
+#define PWMV4_MODE_MASK GENMASK(1, 0)
|
||||
+#define PWMV4_MODE(v) FIELD_PREP_WM16(PWMV4_MODE_MASK, (v))
|
||||
+#define PWMV4_CTRL_COM_FLAGS (PWMV4_INACTIVE_POL(PWMV4_POLARITY_N) | \
|
||||
+ PWMV4_DUTY_POL(PWMV4_POLARITY_P) | \
|
||||
+ PWMV4_CTRL_OUT_MODE(PWMV4_LEFT_ALIGNED) | \
|
||||
+ PWMV4_CTRL_UNALIGNED(true))
|
||||
+#define PWMV4_CTRL_CONT_FLAGS (PWMV4_MODE(PWMV4_MODE_CONT) | \
|
||||
+ PWMV4_CTRL_COM_FLAGS)
|
||||
+#define PWMV4_CTRL_CAP_FLAGS (PWMV4_MODE(PWMV4_MODE_CAPTURE) | \
|
||||
+ PWMV4_CTRL_COM_FLAGS)
|
||||
+
|
||||
+#define PWMV4_REG_PERIOD 0x010
|
||||
+/*
|
||||
+ * PERIOD Register Description
|
||||
+ * [31:00] RW | Period of the output waveform
|
||||
+ * Constraints: should be even if CTRL_OUT_MODE is CENTRE_ALIGNED
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_DUTY 0x014
|
||||
+/*
|
||||
+ * DUTY Register Description
|
||||
+ * [31:00] RW | Duty cycle of the output waveform
|
||||
+ * Constraints: should be even if CTRL_OUT_MODE is CENTRE_ALIGNED
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_OFFSET 0x018
|
||||
+/*
|
||||
+ * OFFSET Register Description
|
||||
+ * [31:00] RW | Offset of the output waveform, based on the PWM clock
|
||||
+ * Constraints: 0 <= v <= (PERIOD - DUTY)
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_RPT 0x01C
|
||||
+/*
|
||||
+ * RPT Register Description
|
||||
+ * [31:16] RW | Second dimensional of the effective number of waveform
|
||||
+ * repetitions. Increases by one every first dimensional times.
|
||||
+ * Value `n` means `n + 1` repetitions. The final number of
|
||||
+ * repetitions of the waveform in one-shot mode is:
|
||||
+ * `(first_dimensional + 1) * (second_dimensional + 1)`
|
||||
+ * [15:00] RW | First dimensional of the effective number of waveform
|
||||
+ * repetitions. Value `n` means `n + 1` repetitions.
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_FILTER_CTRL 0x020
|
||||
+/*
|
||||
+ * FILTER_CTRL Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:10] RO | Reserved
|
||||
+ * [09:04] RW | Filter window number
|
||||
+ * [03:01] RO | Reserved
|
||||
+ * [00:00] RW | Filter Enable, 0 = disabled, 1 = enabled
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_CNT 0x024
|
||||
+/*
|
||||
+ * CNT Register Description
|
||||
+ * [31:00] RO | Current value of the PWM Channel 0 counter in pwm clock cycles,
|
||||
+ * 0 <= v <= 2^32-1
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_ENABLE_DELAY 0x028
|
||||
+/*
|
||||
+ * ENABLE_DELAY Register Description
|
||||
+ * [31:16] RO | Reserved
|
||||
+ * [15:00] RW | PWM enable delay, in an unknown unit but probably cycles
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_HPC 0x02C
|
||||
+/*
|
||||
+ * HPC Register Description
|
||||
+ * [31:00] RW | Number of effective high polarity cycles of the input waveform
|
||||
+ * in capture mode. Based on the PWM clock. 0 <= v <= 2^32-1
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_LPC 0x030
|
||||
+/*
|
||||
+ * LPC Register Description
|
||||
+ * [31:00] RW | Number of effective low polarity cycles of the input waveform
|
||||
+ * in capture mode. Based on the PWM clock. 0 <= v <= 2^32-1
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_BIPHASIC_CNT_CTRL0 0x040
|
||||
+/*
|
||||
+ * BIPHASIC_CNT_CTRL0 Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:10] RO | Reserved
|
||||
+ * [09:09] RW | Biphasic Counter Phase Edge Selection for mode 0,
|
||||
+ * 0 = rising edge (posedge), 1 = falling edge (negedge)
|
||||
+ * [08:08] RW | Biphasic Counter Clock force enable, 1 = force enable
|
||||
+ * [07:07] W1T | Synchronous Enable
|
||||
+ * [06:06] W1T | Mode Switch
|
||||
+ * 0 = Normal Mode, 1 = Switch timer clock and measured clock
|
||||
+ * Constraints: "Biphasic Counter Mode" must be 0 if this is 1
|
||||
+ * [05:03] RW | Biphasic Counter Mode
|
||||
+ * 0x0 = Mode 0, 0x1 = Mode 1, 0x2 = Mode 2, 0x3 = Mode 3,
|
||||
+ * 0x4 = Mode 4, 0x5 = Reserved
|
||||
+ * [02:02] RW | Biphasic Counter Clock Selection
|
||||
+ * 0 = clock is from PLL and frequency can be configured
|
||||
+ * 1 = clock is from crystal oscillator and frequency is fixed
|
||||
+ * [01:01] RW | Biphasic Counter Continuous Mode
|
||||
+ * [00:00] W1T | Biphasic Counter Enable
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_BIPHASIC_CNT_CTRL1 0x044
|
||||
+/*
|
||||
+ * BIPHASIC_CNT_CTRL1 Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:11] RO | Reserved
|
||||
+ * [10:04] RW | Biphasic Counter Filter Window Number
|
||||
+ * [03:01] RO | Reserved
|
||||
+ * [00:00] RW | Biphasic Counter Filter Enable
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_BIPHASIC_CNT_TIMER 0x048
|
||||
+/*
|
||||
+ * BIPHASIC_CNT_TIMER Register Description
|
||||
+ * [31:00] RW | Biphasic Counter Timer Value, in number of biphasic counter
|
||||
+ * timer clock cycles
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_BIPHASIC_CNT_RES 0x04C
|
||||
+/*
|
||||
+ * BIPHASIC_CNT_RES Register Description
|
||||
+ * [31:00] RO | Biphasic Counter Result Value
|
||||
+ * Constraints: Can only be read after INTSTS[9] is asserted
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_BIPHASIC_CNT_RES_S 0x050
|
||||
+/*
|
||||
+ * BIPHASIC_CNT_RES_S Register Description
|
||||
+ * [31:00] RO | Biphasic Counter Result Value with synchronised processing
|
||||
+ * Can be read in real-time if BIPHASIC_CNT_CTRL0[7] was set to 1
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_INTSTS 0x070
|
||||
+/*
|
||||
+ * INTSTS Register Description
|
||||
+ * [31:10] RO | Reserved
|
||||
+ * [09:09] W1C | Biphasic Counter Interrupt Status, 1 = interrupt asserted
|
||||
+ * [08:08] W1C | Waveform Middle Interrupt Status, 1 = interrupt asserted
|
||||
+ * [07:07] W1C | Waveform Max Interrupt Status, 1 = interrupt asserted
|
||||
+ * [06:06] W1C | IR Transmission End Interrupt Status, 1 = interrupt asserted
|
||||
+ * [05:05] W1C | Power Key Match Interrupt Status, 1 = interrupt asserted
|
||||
+ * [04:04] W1C | Frequency Meter Interrupt Status, 1 = interrupt asserted
|
||||
+ * [03:03] W1C | Reload Interrupt Status, 1 = interrupt asserted
|
||||
+ * [02:02] W1C | Oneshot End Interrupt Status, 1 = interrupt asserted
|
||||
+ * [01:01] W1C | HPC Capture Interrupt Status, 1 = interrupt asserted
|
||||
+ * [00:00] W1C | LPC Capture Interrupt Status, 1 = interrupt asserted
|
||||
+ */
|
||||
+#define PWMV4_INT_LPC BIT(0)
|
||||
+#define PWMV4_INT_HPC BIT(1)
|
||||
+#define PWMV4_INT_LPC_W(v) FIELD_PREP_WM16(PWMV4_INT_LPC, \
|
||||
+ ((v) ? 1 : 0))
|
||||
+#define PWMV4_INT_HPC_W(v) FIELD_PREP_WM16(PWMV4_INT_HPC, \
|
||||
+ ((v) ? 1 : 0))
|
||||
+
|
||||
+#define PWMV4_REG_INT_EN 0x074
|
||||
+/*
|
||||
+ * INT_EN Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:10] RO | Reserved
|
||||
+ * [09:09] RW | Biphasic Counter Interrupt Enable, 1 = enabled
|
||||
+ * [08:08] W1C | Waveform Middle Interrupt Enable, 1 = enabled
|
||||
+ * [07:07] W1C | Waveform Max Interrupt Enable, 1 = enabled
|
||||
+ * [06:06] W1C | IR Transmission End Interrupt Enable, 1 = enabled
|
||||
+ * [05:05] W1C | Power Key Match Interrupt Enable, 1 = enabled
|
||||
+ * [04:04] W1C | Frequency Meter Interrupt Enable, 1 = enabled
|
||||
+ * [03:03] W1C | Reload Interrupt Enable, 1 = enabled
|
||||
+ * [02:02] W1C | Oneshot End Interrupt Enable, 1 = enabled
|
||||
+ * [01:01] W1C | HPC Capture Interrupt Enable, 1 = enabled
|
||||
+ * [00:00] W1C | LPC Capture Interrupt Enable, 1 = enabled
|
||||
+ */
|
||||
+
|
||||
+#define PWMV4_REG_INT_MASK 0x078
|
||||
+/*
|
||||
+ * INT_MASK Register Description
|
||||
+ * [31:16] WO | Write Enable Mask for the lower half of the register
|
||||
+ * Set bit `n` here to 1 if you wish to modify bit `n >> 16` in
|
||||
+ * the same write operation
|
||||
+ * [15:10] RO | Reserved
|
||||
+ * [09:09] RW | Biphasic Counter Interrupt Masked, 1 = masked
|
||||
+ * [08:08] W1C | Waveform Middle Interrupt Masked, 1 = masked
|
||||
+ * [07:07] W1C | Waveform Max Interrupt Masked, 1 = masked
|
||||
+ * [06:06] W1C | IR Transmission End Interrupt Masked, 1 = masked
|
||||
+ * [05:05] W1C | Power Key Match Interrupt Masked, 1 = masked
|
||||
+ * [04:04] W1C | Frequency Meter Interrupt Masked, 1 = masked
|
||||
+ * [03:03] W1C | Reload Interrupt Masked, 1 = masked
|
||||
+ * [02:02] W1C | Oneshot End Interrupt Masked, 1 = masked
|
||||
+ * [01:01] W1C | HPC Capture Interrupt Masked, 1 = masked
|
||||
+ * [00:00] W1C | LPC Capture Interrupt Masked, 1 = masked
|
||||
+ */
|
||||
+
|
||||
+static inline u32 mfpwm_reg_read(void __iomem *base, u32 reg)
|
||||
+{
|
||||
+ return readl(base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void mfpwm_reg_write(void __iomem *base, u32 reg, u32 val)
|
||||
+{
|
||||
+ writel(val, base + reg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mfpwm_acquire - try becoming the active mfpwm function device
|
||||
+ * @pwmf: pointer to the calling driver instance's &struct rockchip_mfpwm_func
|
||||
+ *
|
||||
+ * mfpwm device "function" drivers must call this function before doing anything
|
||||
+ * that either modifies or relies on the parent device's state, such as clocks,
|
||||
+ * enabling/disabling outputs, modifying shared regs etc.
|
||||
+ *
|
||||
+ * The return statues should always be checked.
|
||||
+ *
|
||||
+ * All mfpwm_acquire() calls must be balanced with corresponding mfpwm_release()
|
||||
+ * calls once the device is no longer making changes that affect other devices,
|
||||
+ * or stops producing user-visible effects that depend on the current device
|
||||
+ * state being kept as-is. (e.g. after the PWM output signal is stopped)
|
||||
+ *
|
||||
+ * The same device function may mfpwm_acquire() multiple times while it already
|
||||
+ * is active, i.e. it is re-entrant, though it needs to balance this with the
|
||||
+ * same number of mfpwm_release() calls.
|
||||
+ *
|
||||
+ * Context: This function does not sleep.
|
||||
+ *
|
||||
+ * Return:
|
||||
+ * * %0 - success
|
||||
+ * * %-EBUSY - a different device function is active
|
||||
+ * * %-EOVERFLOW - the acquire counter is at its maximum
|
||||
+ */
|
||||
+extern int __must_check mfpwm_acquire(struct rockchip_mfpwm_func *pwmf);
|
||||
+
|
||||
+/**
|
||||
+ * mfpwm_release - drop usage of active mfpwm device function by 1
|
||||
+ * @pwmf: pointer to the calling driver instance's &struct rockchip_mfpwm_func
|
||||
+ *
|
||||
+ * This is the balancing call to mfpwm_acquire(). If no users of the device
|
||||
+ * function remain, set the mfpwm device to have no active device function,
|
||||
+ * allowing other device functions to claim it.
|
||||
+ */
|
||||
+extern void mfpwm_release(const struct rockchip_mfpwm_func *pwmf);
|
||||
+
|
||||
+#endif /* __SOC_ROCKCHIP_MFPWM_H__ */
|
||||
@ -0,0 +1,416 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v3 3/5] pwm: Add rockchip PWMv4 driver
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Date: Mon, 27 Oct 2025 18:11:58 +0100
|
||||
Message-Id: <20251027-rk3576-pwm-v3-3-654a5cb1e3f8@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The Rockchip RK3576 brings with it a new PWM IP, in downstream code
|
||||
referred to as "v4". This new IP is different enough from the previous
|
||||
Rockchip IP that I felt it necessary to add a new driver for it, instead
|
||||
of shoehorning it in the old one.
|
||||
|
||||
Add this new driver, based on the PWM core's waveform APIs. Its platform
|
||||
device is registered by the parent mfpwm driver, from which it also
|
||||
receives a little platform data struct, so that mfpwm can guarantee that
|
||||
all the platform device drivers spread across different subsystems for
|
||||
this specific hardware IP do not interfere with each other.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
MAINTAINERS | 1 +
|
||||
drivers/pwm/Kconfig | 13 ++
|
||||
drivers/pwm/Makefile | 1 +
|
||||
drivers/pwm/pwm-rockchip-v4.c | 353 ++++++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 368 insertions(+)
|
||||
|
||||
--- a/drivers/pwm/Kconfig
|
||||
+++ b/drivers/pwm/Kconfig
|
||||
@@ -551,6 +551,19 @@ config PWM_RZ_MTU3
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-rz-mtu3.
|
||||
|
||||
+config PWM_ROCKCHIP_V4
|
||||
+ tristate "Rockchip PWM v4 support"
|
||||
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
+ depends on HAS_IOMEM
|
||||
+ depends on MFD_ROCKCHIP_MFPWM
|
||||
+ help
|
||||
+ Generic PWM framework driver for the PWM controller found on
|
||||
+ later Rockchip SoCs such as the RK3576.
|
||||
+
|
||||
+ Uses the Rockchip Multi-function PWM controller driver infrastructure
|
||||
+ to guarantee fearlessly concurrent operation with other functions of
|
||||
+ the same device implemented by drivers in other subsystems.
|
||||
+
|
||||
config PWM_SAMSUNG
|
||||
tristate "Samsung PWM support"
|
||||
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
|
||||
--- a/drivers/pwm/Makefile
|
||||
+++ b/drivers/pwm/Makefile
|
||||
@@ -49,6 +49,7 @@ obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm
|
||||
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
|
||||
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
|
||||
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
|
||||
+obj-$(CONFIG_PWM_ROCKCHIP_V4) += pwm-rockchip-v4.o
|
||||
obj-$(CONFIG_PWM_RZ_MTU3) += pwm-rz-mtu3.o
|
||||
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
|
||||
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/pwm/pwm-rockchip-v4.c
|
||||
@@ -0,0 +1,353 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (c) 2025 Collabora Ltd.
|
||||
+ *
|
||||
+ * A Pulse-Width-Modulation (PWM) generator driver for the generators found in
|
||||
+ * Rockchip SoCs such as the RK3576, internally referred to as "PWM v4". Uses
|
||||
+ * the MFPWM infrastructure to guarantee exclusive use over the device without
|
||||
+ * other functions of the device from different drivers interfering with its
|
||||
+ * operation while it's active.
|
||||
+ *
|
||||
+ * Technical Reference Manual: Chapter 31 of the RK3506 TRM Part 1, a SoC which
|
||||
+ * uses the same PWM hardware and has a publicly available TRM.
|
||||
+ * https://opensource.rock-chips.com/images/3/36/Rockchip_RK3506_TRM_Part_1_V1.2-20250811.pdf
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
+ *
|
||||
+ * Limitations:
|
||||
+ * - When the output is disabled, it will end abruptly without letting the
|
||||
+ * current period complete.
|
||||
+ * TODO: This can be fixed in the driver in the future by having the enable-
|
||||
+ * to-disable transition switch to oneshot mode with one repetition,
|
||||
+ * and then disable the pwmclk and release mfpwm when the oneshot
|
||||
+ * complete interrupt fires.
|
||||
+ * - When the output is disabled, the pin will remain driven to whatever state
|
||||
+ * it last had.
|
||||
+ * - Adjustments to the duty cycle will only take effect during the next period.
|
||||
+ * - Adjustments to the period length will only take effect during the next
|
||||
+ * period.
|
||||
+ * - offset should be between 0 and (period - duty)
|
||||
+ */
|
||||
+
|
||||
+#include <linux/math64.h>
|
||||
+#include <linux/mfd/rockchip-mfpwm.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pwm.h>
|
||||
+
|
||||
+struct rockchip_pwm_v4 {
|
||||
+ struct rockchip_mfpwm_func *pwmf;
|
||||
+ struct pwm_chip chip;
|
||||
+};
|
||||
+
|
||||
+struct rockchip_pwm_v4_wf {
|
||||
+ u32 period;
|
||||
+ u32 duty;
|
||||
+ u32 offset;
|
||||
+ u8 enable;
|
||||
+};
|
||||
+
|
||||
+static inline struct rockchip_pwm_v4 *to_rockchip_pwm_v4(struct pwm_chip *chip)
|
||||
+{
|
||||
+ return pwmchip_get_drvdata(chip);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * rockchip_pwm_v4_round_single - convert a PWM parameter to hardware
|
||||
+ * @rate: clock rate of the PWM clock, as per clk_get_rate
|
||||
+ * @in_val: parameter in nanoseconds to convert
|
||||
+ *
|
||||
+ * Returns the rounded value, saturating at U32_MAX if too large
|
||||
+ */
|
||||
+static u32 rockchip_pwm_v4_round_single(unsigned long rate, u64 in_val)
|
||||
+{
|
||||
+ u64 tmp;
|
||||
+
|
||||
+ tmp = mul_u64_u64_div_u64(rate, in_val, NSEC_PER_SEC);
|
||||
+ if (tmp > U32_MAX)
|
||||
+ tmp = U32_MAX;
|
||||
+
|
||||
+ return (u32)tmp;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * rockchip_pwm_v4_round_params - convert PWM parameters to hardware
|
||||
+ * @rate: PWM clock rate to do the calculations at
|
||||
+ * @duty: PWM duty cycle in nanoseconds
|
||||
+ * @period: PWM period in nanoseconds
|
||||
+ * @offset: PWM offset in nanoseconds
|
||||
+ * @out_duty: pointer to where the rounded duty value should be stored
|
||||
+ * @out_period: pointer to where the rounded period value should be stored
|
||||
+ * @out_offset: pointer to where the rounded offset value should be stored
|
||||
+ *
|
||||
+ * Convert nanosecond-based duty/period/offset parameters to the PWM hardware's
|
||||
+ * native rounded representation in number of cycles at clock rate @rate. Should
|
||||
+ * any of the input parameters be out of range for the hardware, the
|
||||
+ * corresponding output parameter is the maximum permissible value for said
|
||||
+ * parameter with considerations to the others.
|
||||
+ */
|
||||
+static void rockchip_pwm_v4_round_params(unsigned long rate, u64 duty,
|
||||
+ u64 period, u64 offset, u32 *out_duty,
|
||||
+ u32 *out_period, u32 *out_offset)
|
||||
+{
|
||||
+ *out_period = rockchip_pwm_v4_round_single(rate, period);
|
||||
+
|
||||
+ *out_duty = rockchip_pwm_v4_round_single(rate, duty);
|
||||
+
|
||||
+ /* As per TRM, PWM_OFFSET: "The value ranges from 0 to (period-duty)" */
|
||||
+ *out_offset = rockchip_pwm_v4_round_single(rate, offset);
|
||||
+ if (*out_offset > (*out_period - *out_duty))
|
||||
+ *out_offset = *out_period - *out_duty;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_v4_round_wf_tohw(struct pwm_chip *chip,
|
||||
+ struct pwm_device *pwm,
|
||||
+ const struct pwm_waveform *wf,
|
||||
+ void *_wfhw)
|
||||
+{
|
||||
+ struct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);
|
||||
+ struct rockchip_pwm_v4_wf *wfhw = _wfhw;
|
||||
+ unsigned long rate;
|
||||
+
|
||||
+ rate = clk_get_rate(pc->pwmf->core);
|
||||
+
|
||||
+ rockchip_pwm_v4_round_params(rate, wf->duty_length_ns,
|
||||
+ wf->period_length_ns, wf->duty_offset_ns,
|
||||
+ &wfhw->duty, &wfhw->period, &wfhw->offset);
|
||||
+
|
||||
+ if (wf->period_length_ns > 0)
|
||||
+ wfhw->enable = PWMV4_EN_BOTH_MASK;
|
||||
+ else
|
||||
+ wfhw->enable = 0;
|
||||
+
|
||||
+ dev_dbg(&chip->dev,
|
||||
+ "tohw: duty %llu -> %u, period %llu -> %u, offset %llu -> %u, rate %lu\n",
|
||||
+ wf->duty_length_ns, wfhw->duty, wf->period_length_ns,
|
||||
+ wfhw->period, wf->duty_offset_ns, wfhw->offset, rate);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_v4_round_wf_fromhw(struct pwm_chip *chip,
|
||||
+ struct pwm_device *pwm,
|
||||
+ const void *_wfhw,
|
||||
+ struct pwm_waveform *wf)
|
||||
+{
|
||||
+ struct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);
|
||||
+ const struct rockchip_pwm_v4_wf *wfhw = _wfhw;
|
||||
+ unsigned long rate;
|
||||
+
|
||||
+ rate = clk_get_rate(pc->pwmf->core);
|
||||
+
|
||||
+ if (rockchip_pwm_v4_is_enabled(wfhw->enable)) {
|
||||
+ if (!rate)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ wf->period_length_ns = (u64)wfhw->period * NSEC_PER_SEC / rate;
|
||||
+ wf->duty_length_ns = (u64)wfhw->duty * NSEC_PER_SEC / rate;
|
||||
+ wf->duty_offset_ns = (u64)wfhw->offset * NSEC_PER_SEC / rate;
|
||||
+ } else {
|
||||
+ wf->period_length_ns = 0;
|
||||
+ wf->duty_length_ns = 0;
|
||||
+ wf->duty_offset_ns = 0;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(&chip->dev,
|
||||
+ "fromhw: duty %u -> %llu, period %u -> %llu, offset %u -> %llu, rate %lu\n",
|
||||
+ wfhw->duty, wf->duty_length_ns, wfhw->period,
|
||||
+ wf->period_length_ns, wfhw->offset, wf->duty_offset_ns, rate);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_v4_read_wf(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ void *_wfhw)
|
||||
+{
|
||||
+ struct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);
|
||||
+ struct rockchip_pwm_v4_wf *wfhw = _wfhw;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+
|
||||
+ ret = mfpwm_acquire(pc->pwmf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ wfhw->period = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_PERIOD);
|
||||
+ wfhw->duty = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_DUTY);
|
||||
+ wfhw->offset = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_OFFSET);
|
||||
+ wfhw->enable = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_ENABLE) & PWMV4_EN_BOTH_MASK;
|
||||
+
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_v4_write_wf(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ const void *_wfhw)
|
||||
+{
|
||||
+ struct rockchip_pwm_v4 *pc = to_rockchip_pwm_v4(chip);
|
||||
+ const struct rockchip_pwm_v4_wf *wfhw = _wfhw;
|
||||
+ bool was_enabled = false;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ ret = mfpwm_acquire(pc->pwmf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ was_enabled = rockchip_pwm_v4_is_enabled(mfpwm_reg_read(pc->pwmf->base,
|
||||
+ PWMV4_REG_ENABLE));
|
||||
+
|
||||
+ /*
|
||||
+ * "But Nicolas", you ask with valid concerns, "why would you enable the
|
||||
+ * PWM before setting all the parameter registers?"
|
||||
+ *
|
||||
+ * Excellent question, Mr. Reader M. Strawman! The RK3576 TRM Part 1
|
||||
+ * Section 34.6.3 specifies that this is the intended order of writes.
|
||||
+ * Doing the PWM_EN and PWM_CLK_EN writes after the params but before
|
||||
+ * the CTRL_UPDATE_EN, or even after the CTRL_UPDATE_EN, results in
|
||||
+ * erratic behaviour where repeated turning on and off of the PWM may
|
||||
+ * not turn it off under all circumstances. This is also why we don't
|
||||
+ * use relaxed writes; it's not worth the footgun.
|
||||
+ */
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
|
||||
+ FIELD_PREP_WM16(PWMV4_EN_BOTH_MASK, wfhw->enable));
|
||||
+
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_PERIOD, wfhw->period);
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_DUTY, wfhw->duty);
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_OFFSET, wfhw->offset);
|
||||
+
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_CTRL, PWMV4_CTRL_CONT_FLAGS);
|
||||
+
|
||||
+ /* Commit new configuration to hardware output. */
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
|
||||
+ PWMV4_CTRL_UPDATE_EN);
|
||||
+
|
||||
+ if (rockchip_pwm_v4_is_enabled(wfhw->enable)) {
|
||||
+ if (!was_enabled) {
|
||||
+ dev_dbg(&chip->dev, "Enabling PWM output\n");
|
||||
+ ret = clk_enable(pc->pwmf->core);
|
||||
+ if (ret)
|
||||
+ goto err_mfpwm_release;
|
||||
+ ret = clk_rate_exclusive_get(pc->pwmf->core);
|
||||
+ if (ret) {
|
||||
+ clk_disable(pc->pwmf->core);
|
||||
+ goto err_mfpwm_release;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Output should be on now, acquire device to guarantee
|
||||
+ * exclusion with other device functions while it's on.
|
||||
+ */
|
||||
+ ret = mfpwm_acquire(pc->pwmf);
|
||||
+ if (ret)
|
||||
+ goto err_mfpwm_release;
|
||||
+ }
|
||||
+ } else if (was_enabled) {
|
||||
+ dev_dbg(&chip->dev, "Disabling PWM output\n");
|
||||
+ clk_rate_exclusive_put(pc->pwmf->core);
|
||||
+ clk_disable(pc->pwmf->core);
|
||||
+ /* Output is off now, extra release to balance extra acquire */
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+ }
|
||||
+
|
||||
+err_mfpwm_release:
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct pwm_ops rockchip_pwm_v4_ops = {
|
||||
+ .sizeof_wfhw = sizeof(struct rockchip_pwm_v4_wf),
|
||||
+ .round_waveform_tohw = rockchip_pwm_v4_round_wf_tohw,
|
||||
+ .round_waveform_fromhw = rockchip_pwm_v4_round_wf_fromhw,
|
||||
+ .read_waveform = rockchip_pwm_v4_read_wf,
|
||||
+ .write_waveform = rockchip_pwm_v4_write_wf,
|
||||
+};
|
||||
+
|
||||
+static bool rockchip_pwm_v4_on_and_continuous(struct rockchip_pwm_v4 *pc)
|
||||
+{
|
||||
+ bool en;
|
||||
+ u32 val;
|
||||
+
|
||||
+ en = rockchip_pwm_v4_is_enabled(mfpwm_reg_read(pc->pwmf->base,
|
||||
+ PWMV4_REG_ENABLE));
|
||||
+ val = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_CTRL);
|
||||
+
|
||||
+ return en && ((val & PWMV4_MODE_MASK) == PWMV4_MODE_CONT);
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_v4_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rockchip_mfpwm_func *pwmf = dev_get_platdata(&pdev->dev);
|
||||
+ struct rockchip_pwm_v4 *pc;
|
||||
+ struct pwm_chip *chip;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ /*
|
||||
+ * For referencing the PWM in the DT to work, we need the parent MFD
|
||||
+ * device's OF node. Code shamelessly adapted from `drivers/pci/of.c`'s
|
||||
+ * pci_set_of_node(), which does this for bus reasons.
|
||||
+ */
|
||||
+ dev->parent->of_node_reused = true;
|
||||
+ device_set_node(dev,
|
||||
+ of_fwnode_handle(no_free_ptr(dev->parent->of_node)));
|
||||
+
|
||||
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*pc));
|
||||
+ if (IS_ERR(chip))
|
||||
+ return PTR_ERR(chip);
|
||||
+
|
||||
+ pc = to_rockchip_pwm_v4(chip);
|
||||
+ pc->pwmf = pwmf;
|
||||
+
|
||||
+ ret = mfpwm_acquire(pwmf);
|
||||
+ if (ret == -EBUSY)
|
||||
+ dev_warn(dev, "PWM hardware already in use, can't check initial state\n");
|
||||
+ else if (ret < 0)
|
||||
+ return dev_err_probe(dev, ret, "Couldn't acquire mfpwm in probe\n");
|
||||
+
|
||||
+ if (!rockchip_pwm_v4_on_and_continuous(pc))
|
||||
+ mfpwm_release(pwmf);
|
||||
+ else {
|
||||
+ dev_dbg(dev, "PWM was already on at probe time\n");
|
||||
+ ret = clk_enable(pwmf->core);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "Enabling pwm clock failed\n");
|
||||
+ ret = clk_rate_exclusive_get(pc->pwmf->core);
|
||||
+ if (ret) {
|
||||
+ clk_disable(pwmf->core);
|
||||
+ return dev_err_probe(dev, ret, "Protecting pwm clock failed\n");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ platform_set_drvdata(pdev, chip);
|
||||
+
|
||||
+ chip->ops = &rockchip_pwm_v4_ops;
|
||||
+
|
||||
+ ret = devm_pwmchip_add(dev, chip);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct platform_device_id rockchip_pwm_v4_ids[] = {
|
||||
+ { .name = "pwm-rockchip-v4", },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(platform, rockchip_pwm_v4_ids);
|
||||
+
|
||||
+static struct platform_driver rockchip_pwm_v4_driver = {
|
||||
+ .probe = rockchip_pwm_v4_probe,
|
||||
+ .driver = {
|
||||
+ .name = "pwm-rockchip-v4",
|
||||
+ },
|
||||
+ .id_table = rockchip_pwm_v4_ids,
|
||||
+};
|
||||
+module_platform_driver(rockchip_pwm_v4_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
|
||||
+MODULE_DESCRIPTION("Rockchip PWMv4 Driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_IMPORT_NS("ROCKCHIP_MFPWM");
|
||||
+MODULE_ALIAS("platform:pwm-rockchip-v4");
|
||||
@ -0,0 +1,365 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v3 4/5] counter: Add rockchip-pwm-capture driver
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Date: Mon, 27 Oct 2025 18:11:59 +0100
|
||||
Message-Id: <20251027-rk3576-pwm-v3-4-654a5cb1e3f8@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Among many other things, Rockchip's new PWMv4 IP in the RK3576 supports
|
||||
PWM capture functionality.
|
||||
|
||||
Add a basic driver for this that works to expose HPC/LPC counts and
|
||||
state change events to userspace through the counter framework. It's
|
||||
quite basic, but works well enough to demonstrate the device function
|
||||
exclusion stuff that mfpwm does, in order to eventually support all the
|
||||
functions of this device in drivers within their appropriate subsystems,
|
||||
without them interfering with each other.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
MAINTAINERS | 1 +
|
||||
drivers/counter/Kconfig | 13 ++
|
||||
drivers/counter/Makefile | 1 +
|
||||
drivers/counter/rockchip-pwm-capture.c | 306 +++++++++++++++++++++++++++++++++
|
||||
4 files changed, 321 insertions(+)
|
||||
|
||||
--- a/drivers/counter/Kconfig
|
||||
+++ b/drivers/counter/Kconfig
|
||||
@@ -90,6 +90,19 @@ config MICROCHIP_TCB_CAPTURE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called microchip-tcb-capture.
|
||||
|
||||
+config ROCKCHIP_PWM_CAPTURE
|
||||
+ tristate "Rockchip PWM Counter Capture driver"
|
||||
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
+ depends on MFD_ROCKCHIP_MFPWM
|
||||
+ depends on HAS_IOMEM
|
||||
+ help
|
||||
+ Generic counter framework driver for the multi-function PWM on
|
||||
+ Rockchip SoCs such as the RK3576.
|
||||
+
|
||||
+ Uses the Rockchip Multi-function PWM controller driver infrastructure
|
||||
+ to guarantee exclusive operation with other functions of the same
|
||||
+ device implemented by drivers in other subsystems.
|
||||
+
|
||||
config RZ_MTU3_CNT
|
||||
tristate "Renesas RZ/G2L MTU3a counter driver"
|
||||
depends on RZ_MTU3
|
||||
--- a/drivers/counter/Makefile
|
||||
+++ b/drivers/counter/Makefile
|
||||
@@ -17,3 +17,4 @@ obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec
|
||||
obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o
|
||||
obj-$(CONFIG_INTEL_QEP) += intel-qep.o
|
||||
obj-$(CONFIG_TI_ECAP_CAPTURE) += ti-ecap-capture.o
|
||||
+obj-$(CONFIG_ROCKCHIP_PWM_CAPTURE) += rockchip-pwm-capture.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/counter/rockchip-pwm-capture.c
|
||||
@@ -0,0 +1,306 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (c) 2025 Collabora Ltd.
|
||||
+ *
|
||||
+ * A counter driver for the Pulse-Width-Modulation (PWM) hardware found on
|
||||
+ * Rockchip SoCs such as the RK3576, internally referred to as "PWM v4". It
|
||||
+ * allows for measuring the high cycles and low cycles of a PWM signal through
|
||||
+ * the generic counter framework, while guaranteeing exclusive use over the
|
||||
+ * MFPWM device while the counter is enabled.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/cleanup.h>
|
||||
+#include <linux/counter.h>
|
||||
+#include <linux/devm-helpers.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/mfd/rockchip-mfpwm.h>
|
||||
+#include <linux/mod_devicetable.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#define RKPWMC_INT_MASK (PWMV4_INT_LPC | PWMV4_INT_HPC)
|
||||
+
|
||||
+struct rockchip_pwm_capture {
|
||||
+ struct rockchip_mfpwm_func *pwmf;
|
||||
+ struct counter_device *counter;
|
||||
+ bool is_enabled;
|
||||
+ spinlock_t enable_lock;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Channel 0 receives a state changed notification whenever the LPC interrupt
|
||||
+ * fires.
|
||||
+ *
|
||||
+ * Channel 1 receives a state changed notification whenever the HPC interrupt
|
||||
+ * fires.
|
||||
+ */
|
||||
+static struct counter_signal rkpwmc_signals[] = {
|
||||
+ {
|
||||
+ .id = 0,
|
||||
+ .name = "Channel 0"
|
||||
+ },
|
||||
+ {
|
||||
+ .id = 1,
|
||||
+ .name = "Channel 1"
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const enum counter_synapse_action rkpwmc_hpc_lpc_actions[] = {
|
||||
+ COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
+
|
||||
+};
|
||||
+
|
||||
+static struct counter_synapse rkpwmc_pwm_synapses[] = {
|
||||
+ {
|
||||
+ .actions_list = rkpwmc_hpc_lpc_actions,
|
||||
+ .num_actions = ARRAY_SIZE(rkpwmc_hpc_lpc_actions),
|
||||
+ .signal = &rkpwmc_signals[0]
|
||||
+ },
|
||||
+ {
|
||||
+ .actions_list = rkpwmc_hpc_lpc_actions,
|
||||
+ .num_actions = ARRAY_SIZE(rkpwmc_hpc_lpc_actions),
|
||||
+ .signal = &rkpwmc_signals[1]
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const enum counter_function rkpwmc_functions[] = {
|
||||
+ COUNTER_FUNCTION_INCREASE,
|
||||
+};
|
||||
+
|
||||
+static int rkpwmc_enable_read(struct counter_device *counter,
|
||||
+ struct counter_count *count,
|
||||
+ u8 *enable)
|
||||
+{
|
||||
+ struct rockchip_pwm_capture *pc = counter_priv(counter);
|
||||
+
|
||||
+ guard(spinlock)(&pc->enable_lock);
|
||||
+
|
||||
+ *enable = pc->is_enabled;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkpwmc_enable_write(struct counter_device *counter,
|
||||
+ struct counter_count *count,
|
||||
+ u8 enable)
|
||||
+{
|
||||
+ struct rockchip_pwm_capture *pc = counter_priv(counter);
|
||||
+ int ret;
|
||||
+
|
||||
+ guard(spinlock)(&pc->enable_lock);
|
||||
+
|
||||
+ if (!!enable != pc->is_enabled) {
|
||||
+ ret = mfpwm_acquire(pc->pwmf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (enable) {
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
|
||||
+ PWMV4_EN(false));
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_CTRL,
|
||||
+ PWMV4_CTRL_CAP_FLAGS);
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,
|
||||
+ PWMV4_INT_LPC_W(true) |
|
||||
+ PWMV4_INT_HPC_W(true));
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
|
||||
+ PWMV4_EN(true) | PWMV4_CLK_EN(true));
|
||||
+
|
||||
+ ret = clk_enable(pc->pwmf->core);
|
||||
+ if (ret)
|
||||
+ goto err_release;
|
||||
+
|
||||
+ ret = clk_rate_exclusive_get(pc->pwmf->core);
|
||||
+ if (ret)
|
||||
+ goto err_disable_pwm_clk;
|
||||
+
|
||||
+ ret = mfpwm_acquire(pc->pwmf);
|
||||
+ if (ret)
|
||||
+ goto err_unprotect_pwm_clk;
|
||||
+
|
||||
+ pc->is_enabled = true;
|
||||
+ } else {
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,
|
||||
+ PWMV4_INT_LPC_W(false) |
|
||||
+ PWMV4_INT_HPC_W(false));
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
|
||||
+ PWMV4_EN(false) | PWMV4_CLK_EN(false));
|
||||
+ clk_rate_exclusive_put(pc->pwmf->core);
|
||||
+ clk_disable(pc->pwmf->core);
|
||||
+ pc->is_enabled = false;
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+ }
|
||||
+
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_unprotect_pwm_clk:
|
||||
+ clk_rate_exclusive_put(pc->pwmf->core);
|
||||
+err_disable_pwm_clk:
|
||||
+ clk_disable(pc->pwmf->core);
|
||||
+err_release:
|
||||
+ mfpwm_release(pc->pwmf);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static struct counter_comp rkpwmc_ext[] = {
|
||||
+ COUNTER_COMP_ENABLE(rkpwmc_enable_read, rkpwmc_enable_write),
|
||||
+};
|
||||
+
|
||||
+enum rkpwmc_count_id {
|
||||
+ COUNT_LPC = 0,
|
||||
+ COUNT_HPC = 1,
|
||||
+};
|
||||
+
|
||||
+static struct counter_count rkpwmc_counts[] = {
|
||||
+ {
|
||||
+ .id = COUNT_LPC,
|
||||
+ .name = "PWM core clock cycles during which the signal was low",
|
||||
+ .functions_list = rkpwmc_functions,
|
||||
+ .num_functions = ARRAY_SIZE(rkpwmc_functions),
|
||||
+ .synapses = rkpwmc_pwm_synapses,
|
||||
+ .num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),
|
||||
+ .ext = rkpwmc_ext,
|
||||
+ .num_ext = ARRAY_SIZE(rkpwmc_ext),
|
||||
+ },
|
||||
+ {
|
||||
+ .id = COUNT_HPC,
|
||||
+ .name = "PWM core clock cycles during which the signal was high",
|
||||
+ .functions_list = rkpwmc_functions,
|
||||
+ .num_functions = ARRAY_SIZE(rkpwmc_functions),
|
||||
+ .synapses = rkpwmc_pwm_synapses,
|
||||
+ .num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),
|
||||
+ .ext = rkpwmc_ext,
|
||||
+ .num_ext = ARRAY_SIZE(rkpwmc_ext),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int rkpwmc_count_read(struct counter_device *counter,
|
||||
+ struct counter_count *count, u64 *value)
|
||||
+{
|
||||
+ struct rockchip_pwm_capture *pc = counter_priv(counter);
|
||||
+
|
||||
+ guard(spinlock)(&pc->enable_lock);
|
||||
+
|
||||
+ if (!pc->is_enabled) {
|
||||
+ *value = 0;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ switch (count->id) {
|
||||
+ case COUNT_LPC:
|
||||
+ *value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_LPC);
|
||||
+ return 0;
|
||||
+ case COUNT_HPC:
|
||||
+ *value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_HPC);
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static const struct counter_ops rkpwmc_ops = {
|
||||
+ .count_read = rkpwmc_count_read,
|
||||
+};
|
||||
+
|
||||
+static irqreturn_t rkpwmc_irq_handler(int irq, void *data)
|
||||
+{
|
||||
+ struct rockchip_pwm_capture *pc = data;
|
||||
+ u32 intsts;
|
||||
+ u32 clr = 0;
|
||||
+
|
||||
+ intsts = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_INTSTS);
|
||||
+
|
||||
+ if (!(intsts & RKPWMC_INT_MASK))
|
||||
+ return IRQ_NONE;
|
||||
+
|
||||
+ if (intsts & PWMV4_INT_LPC) {
|
||||
+ clr |= PWMV4_INT_LPC;
|
||||
+ counter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
|
||||
+ }
|
||||
+
|
||||
+ if (intsts & PWMV4_INT_HPC) {
|
||||
+ clr |= PWMV4_INT_HPC;
|
||||
+ counter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 1);
|
||||
+ }
|
||||
+
|
||||
+ if (clr)
|
||||
+ mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INTSTS, clr);
|
||||
+
|
||||
+ /* If other interrupt status bits are set, they're not for this driver */
|
||||
+ if (intsts != clr)
|
||||
+ return IRQ_NONE;
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int rockchip_pwm_capture_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rockchip_mfpwm_func *pwmf = dev_get_platdata(&pdev->dev);
|
||||
+ struct rockchip_pwm_capture *pc;
|
||||
+ struct counter_device *counter;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Set our (still unset) OF node to the parent MFD device's OF node */
|
||||
+ pdev->dev.parent->of_node_reused = true;
|
||||
+ device_set_node(&pdev->dev,
|
||||
+ of_fwnode_handle(no_free_ptr(pdev->dev.parent->of_node)));
|
||||
+
|
||||
+ counter = devm_counter_alloc(&pdev->dev, sizeof(*pc));
|
||||
+ if (IS_ERR(counter))
|
||||
+ return PTR_ERR(counter);
|
||||
+
|
||||
+ pc = counter_priv(counter);
|
||||
+ pc->pwmf = pwmf;
|
||||
+ spin_lock_init(&pc->enable_lock);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, pc);
|
||||
+
|
||||
+ ret = devm_request_irq(&pdev->dev, pwmf->irq, rkpwmc_irq_handler,
|
||||
+ IRQF_SHARED, pdev->name, pc);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(&pdev->dev, ret, "Failed requesting IRQ\n");
|
||||
+
|
||||
+ counter->name = pdev->name;
|
||||
+ counter->signals = rkpwmc_signals;
|
||||
+ counter->num_signals = ARRAY_SIZE(rkpwmc_signals);
|
||||
+ counter->ops = &rkpwmc_ops;
|
||||
+ counter->counts = rkpwmc_counts;
|
||||
+ counter->num_counts = ARRAY_SIZE(rkpwmc_counts);
|
||||
+
|
||||
+ pc->counter = counter;
|
||||
+
|
||||
+ ret = devm_counter_add(&pdev->dev, counter);
|
||||
+ if (ret < 0)
|
||||
+ return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct platform_device_id rockchip_pwm_capture_id_table[] = {
|
||||
+ { .name = "rockchip-pwm-capture", },
|
||||
+ { /* sentinel */ },
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(platform, rockchip_pwm_capture_id_table);
|
||||
+
|
||||
+static struct platform_driver rockchip_pwm_capture_driver = {
|
||||
+ .probe = rockchip_pwm_capture_probe,
|
||||
+ .id_table = rockchip_pwm_capture_id_table,
|
||||
+ .driver = {
|
||||
+ .name = "rockchip-pwm-capture",
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(rockchip_pwm_capture_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
|
||||
+MODULE_DESCRIPTION("Rockchip PWM Counter Capture Driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_IMPORT_NS("ROCKCHIP_MFPWM");
|
||||
+MODULE_IMPORT_NS("COUNTER");
|
||||
+MODULE_ALIAS("platform:rockchip-pwm-capture");
|
||||
@ -0,0 +1,243 @@
|
||||
From git@z Thu Jan 1 00:00:00 1970
|
||||
Subject: [PATCH v3 5/5] arm64: dts: rockchip: add PWM nodes to RK3576 SoC
|
||||
dtsi
|
||||
From: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
Date: Mon, 27 Oct 2025 18:12:00 +0100
|
||||
Message-Id: <20251027-rk3576-pwm-v3-5-654a5cb1e3f8@collabora.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
The RK3576 SoC features three distinct PWM controllers, with variable
|
||||
numbers of channels. Add each channel as a separate node to the SoC's
|
||||
device tree, as they don't really overlap in register ranges.
|
||||
|
||||
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
|
||||
---
|
||||
arch/arm64/boot/dts/rockchip/rk3576.dtsi | 208 +++++++++++++++++++++++++++++++
|
||||
1 file changed, 208 insertions(+)
|
||||
|
||||
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
|
||||
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
|
||||
@@ -1042,6 +1042,32 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
+ pwm0_2ch_0: pwm@27330000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x27330000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PMU1PWM>, <&cru PCLK_PMU1PWM>,
|
||||
+ <&cru CLK_PMU1PWM_OSC>, <&cru CLK_PMU1PWM_RC>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm0m0_ch0>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm0_2ch_1: pwm@27331000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x27331000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PMU1PWM>, <&cru PCLK_PMU1PWM>,
|
||||
+ <&cru CLK_PMU1PWM_OSC>, <&cru CLK_PMU1PWM_RC>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm0m0_ch1>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
pmu: power-management@27380000 {
|
||||
compatible = "rockchip,rk3576-pmu", "syscon", "simple-mfd";
|
||||
reg = <0x0 0x27380000 0x0 0x800>;
|
||||
@@ -2492,6 +2518,188 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
+ pwm1_6ch_0: pwm@2add0000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add0000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch0>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm1_6ch_1: pwm@2add1000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add1000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch1>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm1_6ch_2: pwm@2add2000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add2000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch2>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm1_6ch_3: pwm@2add3000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add3000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch3>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm1_6ch_4: pwm@2add4000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add4000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch4>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm1_6ch_5: pwm@2add5000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2add5000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
|
||||
+ <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm1m0_ch5>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_0: pwm@2ade0000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade0000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch0>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_1: pwm@2ade1000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade1000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch1>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_2: pwm@2ade2000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade2000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch2>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_3: pwm@2ade3000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade3000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch3>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_4: pwm@2ade4000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade4000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch4>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_5: pwm@2ade5000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade5000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch5>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_6: pwm@2ade6000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade6000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch6>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ pwm2_8ch_7: pwm@2ade7000 {
|
||||
+ compatible = "rockchip,rk3576-pwm";
|
||||
+ reg = <0x0 0x2ade7000 0x0 0x1000>;
|
||||
+ clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
|
||||
+ <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
|
||||
+ clock-names = "pwm", "pclk", "osc", "rc";
|
||||
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&pwm2m0_ch7>;
|
||||
+ #pwm-cells = <3>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
saradc: adc@2ae00000 {
|
||||
compatible = "rockchip,rk3576-saradc", "rockchip,rk3588-saradc";
|
||||
reg = <0x0 0x2ae00000 0x0 0x10000>;
|
||||
@ -1,6 +1,6 @@
|
||||
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
|
||||
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
|
||||
@@ -1927,11 +1927,13 @@
|
||||
@@ -1953,11 +1953,13 @@
|
||||
};
|
||||
|
||||
rng: rng@2a410000 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user