From aee4d8e15eff4ef006b5ff65195b32a86c652e5b Mon Sep 17 00:00:00 2001 From: anchorage <7d578vix8hzw@opayq.net> Date: Fri, 7 Oct 2022 22:39:54 +0300 Subject: [PATCH] pwm: Add new pwm driver for ARM Macs Arm macs have a PWM controller that is hooked up to the keyboard backlight. This patch adds the controller driver and device tree bindings using pwm-leds. --- .../devicetree/bindings/pwm/pwm-apple.yaml | 41 +++++ arch/arm64/boot/dts/apple/t600x-die0.dtsi | 9 ++ .../arm64/boot/dts/apple/t600x-j314-j316.dtsi | 18 +++ arch/arm64/boot/dts/apple/t8103-j293.dts | 16 ++ arch/arm64/boot/dts/apple/t8103-j313.dts | 16 ++ arch/arm64/boot/dts/apple/t8103.dtsi | 10 ++ arch/arm64/boot/dts/apple/t8112-j413.dts | 16 ++ arch/arm64/boot/dts/apple/t8112-j493.dts | 16 ++ arch/arm64/boot/dts/apple/t8112.dtsi | 11 ++ drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-apple.c | 141 ++++++++++++++++++ 12 files changed, 307 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-apple.yaml create mode 100644 drivers/pwm/pwm-apple.c diff --git a/Documentation/devicetree/bindings/pwm/pwm-apple.yaml b/Documentation/devicetree/bindings/pwm/pwm-apple.yaml new file mode 100644 index 00000000000000..fd751acec4eb98 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-apple.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-apple.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple FPWM controller + +maintainers: [] + +description: |+ + PWM controller used for keyboard backlight on ARM Macs + +properties: + compatible: + const: apple,s5l-fpwm + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#pwm-cells": + const: 2 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + fpwm1: fpwm@235044000 { + compatible = "apple,s5l-fpwm"; + reg = <0x2 0x35044000 0x0 0x4000>; + power-domains = <&ps_fpwm1>; + clocks = <&clkref>; + #pwm-cells = <2>; + status = "disabled"; + }; diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 4c203778aa891e..715fff34ad936a 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -274,6 +274,15 @@ status = "disabled"; }; + fpwm0: fpwm@39b030000 { + compatible = "apple,s5l-fpwm"; + reg = <0x3 0x9b030000 0x0 0x4000>; + power-domains = <&ps_fpwm0>; + clocks = <&clkref>; + #pwm-cells = <2>; + status = "disabled"; + }; + serial0: serial@39b200000 { compatible = "apple,s5l-uart"; reg = <0x3 0x9b200000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi index bbe66ea64f0910..9a79091a2e0d5f 100644 --- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi @@ -9,6 +9,8 @@ * Copyright The Asahi Linux Contributors */ +#include + / { aliases { serial0 = &serial0; @@ -339,4 +341,20 @@ }; }; }; + pwmleds { + compatible = "pwm-leds"; + kbd-backlight { + pwms = <&fpwm0 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + pwm-names = "kbd-backlight"; + }; + }; +}; + +&fpwm0 { + status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts index 3a5ae54f0b9f7b..6237ce42793930 100644 --- a/arch/arm64/boot/dts/apple/t8103-j293.dts +++ b/arch/arm64/boot/dts/apple/t8103-j293.dts @@ -171,8 +171,24 @@ }; }; }; + pwmleds { + compatible = "pwm-leds"; + kbd-backlight { + pwms = <&fpwm1 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + pwm-names = "kbd-backlight"; + }; + }; }; &framebuffer0 { backlight = <&backlight>; }; + +&fpwm1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts index b51f651d23263c..94d55f0afd83d6 100644 --- a/arch/arm64/boot/dts/apple/t8103-j313.dts +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts @@ -148,8 +148,24 @@ }; }; }; + pwmleds { + compatible = "pwm-leds"; + kbd-backlight { + pwms = <&fpwm1 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + pwm-names = "kbd-backlight"; + }; + }; }; &framebuffer0 { backlight = <&backlight>; }; + +&fpwm1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index afb0688cb1c2f9..b9fee327361e31 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -12,6 +12,7 @@ #include #include #include +#include / { compatible = "apple,t8103", "apple,arm-platform"; @@ -396,6 +397,15 @@ status = "disabled"; /* only used in J293/J313 */ }; + fpwm1: fpwm@235044000 { + compatible = "apple,s5l-fpwm"; + reg = <0x2 0x35044000 0x0 0x4000>; + power-domains = <&ps_fpwm1>; + clocks = <&clkref>; + #pwm-cells = <2>; + status = "disabled"; + }; + serial0: serial@235200000 { compatible = "apple,s5l-uart"; reg = <0x2 0x35200000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts index 0d4306a39fe8f6..7709c66ed850c1 100644 --- a/arch/arm64/boot/dts/apple/t8112-j413.dts +++ b/arch/arm64/boot/dts/apple/t8112-j413.dts @@ -169,6 +169,18 @@ }; #endif }; + pwmleds { + compatible = "pwm-leds"; + kbd-backlight { + pwms = <&fpwm1 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + pwm-names = "kbd-backlight"; + }; + }; }; &framebuffer0 { @@ -180,3 +192,7 @@ firmware-name = "apple/tpmtfw-j413.bin"; }; }; + +&fpwm1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts index aa8c045cd1aced..92b75c3c325aa9 100644 --- a/arch/arm64/boot/dts/apple/t8112-j493.dts +++ b/arch/arm64/boot/dts/apple/t8112-j493.dts @@ -158,6 +158,18 @@ }; #endif }; + pwmleds { + compatible = "pwm-leds"; + kbd-backlight { + pwms = <&fpwm1 0 40000>; + label = "kbd_backlight"; + function = LED_FUNCTION_KBD_BACKLIGHT; + color = ; + max-brightness = <255>; + default-state = "keep"; + pwm-names = "kbd-backlight"; + }; + }; }; &framebuffer0 { @@ -169,3 +181,7 @@ firmware-name = "apple/tpmtfw-j493.bin"; }; }; + +&fpwm1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index 852937ec80ea18..dda491f3d3298b 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -12,6 +12,7 @@ #include #include #include +#include / { compatible = "apple,t8112", "apple,arm-platform"; @@ -416,6 +417,16 @@ status = "disabled"; }; + fpwm1: fpwm@235044000 { + compatible = "apple,s5l-fpwm"; + reg = <0x2 0x35044000 0x0 0x4000>; + power-domains = <&ps_fpwm1>; + clocks = <&clkref>; + #pwm-cells = <2>; + status = "disabled"; + }; + + serial0: serial@235200000 { compatible = "apple,s5l-uart"; reg = <0x2 0x35200000 0x0 0x1000>; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 904de8d61828a6..3ffb5654916171 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -51,6 +51,18 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_APPLE + tristate "Apple SoC PWM support" + depends on ARCH_APPLE || COMPILE_TEST + help + Generic PWM framework driver for PWM controller present on + Apple SoCs + + Say Y here if you have an ARM Apple laptop, otherwise say N + + To compile this driver as a module, choose M here: the module + will be called pwm-apple. + config PWM_ATMEL tristate "Atmel PWM support" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 5c08bdb817b4c5..bf0c04ce704e7a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_APPLE) += pwm-apple.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o diff --git a/drivers/pwm/pwm-apple.c b/drivers/pwm/pwm-apple.c new file mode 100644 index 00000000000000..3e8f2ddb05c045 --- /dev/null +++ b/drivers/pwm/pwm-apple.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Apple SoC PWM controller + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PWM_CONTROL 0x00 +#define PWM_APPLY_MAGIC 0x4239 +#define PWM_ON_CYCLES 0x1c +#define PWM_OFF_CYCLES 0x18 + +#define CTRL_ENABLE BIT(0) +#define CTRL_MODE BIT(2) +#define CTRL_UPDATE BIT(5) +#define CTRL_TRIGGER BIT(9) +#define CTRL_INVERT BIT(10) +#define CTRL_OUTPUT_ENABLE BIT(14) + +struct apple_pwm { + struct pwm_chip chip; + void __iomem *base; + struct clk *clk; + u64 clkrate; +}; + +static int apple_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct apple_pwm *fpwm; + u64 on_cycles, off_cycles; + + fpwm = container_of(chip, struct apple_pwm, chip); + on_cycles = fpwm->clkrate * state->duty_cycle / NSEC_PER_SEC; + off_cycles = (fpwm->clkrate * state->period / NSEC_PER_SEC) - on_cycles; + if (state->enabled) { + writel(on_cycles, fpwm->base + PWM_ON_CYCLES); + writel(off_cycles, fpwm->base + PWM_OFF_CYCLES); + writel(CTRL_ENABLE | CTRL_OUTPUT_ENABLE | CTRL_UPDATE, + fpwm->base + PWM_CONTROL); + } else { + writel(0, fpwm->base + PWM_CONTROL); + } + return 0; +} + +static void apple_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct apple_pwm *fpwm; + u32 on_cycles, off_cycles, ctrl; + + fpwm = container_of(chip, struct apple_pwm, chip); + + ctrl = readl(fpwm->base + PWM_CONTROL); + on_cycles = readl(fpwm->base + PWM_ON_CYCLES); + off_cycles = readl(fpwm->base + PWM_OFF_CYCLES); + + state->enabled = (ctrl & CTRL_ENABLE) && (ctrl & CTRL_OUTPUT_ENABLE); + state->polarity = PWM_POLARITY_NORMAL; + state->duty_cycle = on_cycles / fpwm->clkrate * NSEC_PER_SEC; + state->period = (off_cycles / fpwm->clkrate * NSEC_PER_SEC) + + state->duty_cycle; +} + +static const struct pwm_ops apple_pwm_ops = { + .apply = apple_pwm_apply, + .get_state = apple_pwm_get_state, + .owner = THIS_MODULE, +}; + +static void apple_pwm_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static int apple_pwm_probe(struct platform_device *pdev) +{ + struct apple_pwm *pwm; + int ret; + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->base)) + return PTR_ERR(pwm->base); + + platform_set_drvdata(pdev, pwm); + + pwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + + ret = clk_prepare_enable(pwm->clk); + if (ret) + return ret; + + pwm->clkrate = clk_get_rate(pwm->clk); + + ret = devm_add_action_or_reset(&pdev->dev, + apple_pwm_clk_disable_unprepare, pwm->clk); + if (ret) + return ret; + + pwm->chip.dev = &pdev->dev; + pwm->chip.npwm = 1; + pwm->chip.ops = &apple_pwm_ops; + + ret = devm_pwmchip_add(&pdev->dev, &pwm->chip); + return ret; +} + +static const struct of_device_id apple_pwm_of_match[] = { + { .compatible = "apple,s5l-fpwm" }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_pwm_of_match); + +static struct platform_driver apple_pwm_driver = { + .probe = apple_pwm_probe, + .driver = { + .name = "apple-pwm", + .owner = THIS_MODULE, + .of_match_table = apple_pwm_of_match, + }, +}; +module_platform_driver(apple_pwm_driver); + +MODULE_DESCRIPTION("Apple SoC PWM driver"); +MODULE_LICENSE("GPL");