-
-
Save blazra/95700324d86c5159f128877cc684f668 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi | |
index 4c203778aa89..c24f0afabe14 100644 | |
--- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi | |
+++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi | |
@@ -513,3 +513,11 @@ mca: mca@9b600000 { | |
#sound-dai-cells = <1>; | |
apple,nclusters = <4>; | |
}; | |
+ | |
+ fpwm0: pwm@0x39b030000 { | |
+ compatible = "apple,t6000-fpwm", "apple,fpwm"; | |
+ reg = <0x3 0x9b030000 0x0 0x4000>; | |
+ clocks = <&clkref>; | |
+ #pwm-cells = <2>; | |
+ power-domains = <&ps_fpwm0>; | |
+ }; | |
\ No newline at end of file | |
diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi | |
index bbe66ea64f09..e41633e0480e 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 <dt-bindings/leds/common.h> | |
+ | |
/ { | |
aliases { | |
serial0 = &serial0; | |
@@ -309,6 +311,21 @@ backlight: gpio-bl { | |
default-on; | |
}; | |
+ pwmleds { | |
+ compatible = "pwm-leds"; | |
+ | |
+ kbd-backlight { | |
+ label = "kbd_backlight"; | |
+ function = LED_FUNCTION_KBD_BACKLIGHT; | |
+ color = <LED_COLOR_ID_WHITE>; | |
+ pwm-names = "kbd_backlight"; | |
+ pwms = <&fpwm0 0 40000>; | |
+ max-brightness = <255>; | |
+ default-state = "keep"; | |
+ }; | |
+ }; | |
+ | |
+ | |
sound { | |
compatible = "apple,j314-macaudio", "apple,macaudio"; | |
model = "MacBook Pro J314/6 integrated audio"; | |
diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts | |
index b51f651d2326..e2598bdba06b 100644 | |
--- a/arch/arm64/boot/dts/apple/t8103-j313.dts | |
+++ b/arch/arm64/boot/dts/apple/t8103-j313.dts | |
@@ -11,10 +11,25 @@ | |
#include "t8103.dtsi" | |
#include "t8103-jxxx.dtsi" | |
+#include <dt-bindings/leds/common.h> | |
/ { | |
compatible = "apple,j313", "apple,t8103", "apple,arm-platform"; | |
model = "Apple MacBook Air (M1, 2020)"; | |
+ | |
+ pwmleds { | |
+ compatible = "pwm-leds"; | |
+ | |
+ kbd-backlight { | |
+ label = "kbd_backlight"; | |
+ function = LED_FUNCTION_KBD_BACKLIGHT; | |
+ color = <LED_COLOR_ID_WHITE>; | |
+ pwm-names = "kbd_backlight"; | |
+ pwms = <&fpwm1 0 40000>; | |
+ max-brightness = <255>; | |
+ default-state = "keep"; | |
+ }; | |
+ }; | |
}; | |
&wifi0 { | |
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi | |
index afb0688cb1c2..cc5668fb68fe 100644 | |
--- a/arch/arm64/boot/dts/apple/t8103.dtsi | |
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi | |
@@ -315,6 +315,14 @@ cpufreq_hw: cpufreq@210e20000 { | |
#freq-domain-cells = <1>; | |
}; | |
+ fpwm1: pwm@235044000 { | |
+ compatible = "apple-fpwm"; | |
+ reg = <0x2 0x35044000 0x0 0x4000>; | |
+ clocks = <&clkref>; | |
+ #pwm-cells = <2>; | |
+ power-domains = <&ps_fpwm1>; | |
+ }; | |
+ | |
i2c0: i2c@235010000 { | |
compatible = "apple,t8103-i2c", "apple,i2c"; | |
reg = <0x2 0x35010000 0x0 0x4000>; | |
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig | |
index 904de8d61828..c2c8180176d6 100644 | |
--- a/drivers/pwm/Kconfig | |
+++ b/drivers/pwm/Kconfig | |
@@ -51,6 +51,16 @@ 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 PWM support" | |
+ depends on ARCH_APPLE || COMPILE_TEST | |
+ help | |
+ Generic PWM framework driver for Apple FPWM. | |
+ Found in Apple Silicon SoCs. | |
+ | |
+ 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 5c08bdb817b4..bf0c04ce704e 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 000000000000..bc3f78147bb4 | |
--- /dev/null | |
+++ b/drivers/pwm/pwm-apple.c | |
@@ -0,0 +1,161 @@ | |
+// SPDX-License-Identifier: GPL-2.0 OR MIT | |
+/* | |
+ * Apple FPWM driver | |
+ * | |
+ * Copyright The Asahi Linux Contributors | |
+ */ | |
+ | |
+#include <linux/clk.h> | |
+#include <linux/io.h> | |
+#include <linux/module.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/pwm.h> | |
+ | |
+#define FPWM_CTRL 0x00 | |
+ | |
+#define CTRL_ENABLE BIT(0) | |
+/* Setting this bit to 1 changes mode from PWM to single-pulse mode. */ | |
+#define CTRL_MODE BIT(2) | |
+/* Setting this bit to 1 to this causes changes in the TICKS_* registers to | |
+ * take effect at the end of the current period. Only for PWM mode. */ | |
+#define CTRL_UPDATE BIT(5) | |
+/* Setting this bit to one generates a pulse in single-pulse mode. | |
+ * HW clears this at the end of the pulse. */ | |
+#define CTRL_TRIGGER BIT(9) | |
+/* Configures single-pulse polarity: 0 means positive. Length is TICKS_ON. | |
+ * 1 means negative. Length is TICKS_OFF. */ | |
+#define CTRL_INVERT BIT(10) | |
+#define CTRL_OUTPUT_ENABLE BIT(14) | |
+ | |
+#define FPWM_STATUS 0x08 | |
+ | |
+/* Set at the end of the OFF period. Write 1 to clear. */ | |
+#define STATUS_END_OF_OFF BIT(0) | |
+/* Set at the end of the ON period. Write 1 to clear. */ | |
+#define STATUS_END_OF_ON BIT(1) | |
+ | |
+#define FPWM_TICKS_OFF 0x18 | |
+#define FPWM_TICKS_ON 0x1c | |
+ | |
+struct apple_fpwm { | |
+ struct pwm_chip chip; | |
+ void __iomem *base; | |
+ struct clk *clk; | |
+}; | |
+ | |
+static inline struct apple_fpwm *to_fpwm(struct pwm_chip *chip) | |
+{ | |
+ return container_of(chip, struct apple_fpwm, chip); | |
+} | |
+ | |
+#define NSEC_PER_SEC 1000000000L | |
+ | |
+static u64 ns_to_ticks(u64 ns, u64 rate) | |
+{ | |
+ return mul_u64_u64_div_u64(ns, rate, NSEC_PER_SEC); | |
+} | |
+ | |
+static u64 ticks_to_ns(u64 ticks, u64 rate) | |
+{ | |
+ return mul_u64_u64_div_u64(ticks, NSEC_PER_SEC, rate); | |
+} | |
+ | |
+static int apple_fpwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | |
+ const struct pwm_state *state) | |
+{ | |
+ struct apple_fpwm *fpwm = to_fpwm(chip); | |
+ u64 rate = clk_get_rate(fpwm->clk); | |
+ u64 duty_ticks = ns_to_ticks(state->duty_cycle, rate); | |
+ u64 period_ticks = ns_to_ticks(state->period, rate); | |
+ u64 off_ticks = period_ticks - duty_ticks; | |
+ | |
+ writel(duty_ticks, fpwm->base + FPWM_TICKS_ON); | |
+ writel(off_ticks, fpwm->base + FPWM_TICKS_OFF); | |
+ writel(CTRL_ENABLE | CTRL_OUTPUT_ENABLE | CTRL_UPDATE, | |
+ fpwm->base + FPWM_CTRL); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void apple_fpwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, | |
+ struct pwm_state *state) | |
+{ | |
+ struct apple_fpwm *fpwm = to_fpwm(chip); | |
+ u64 rate = clk_get_rate(fpwm->clk); | |
+ u32 ctrl = readl(fpwm->base + FPWM_CTRL); | |
+ u32 ticks_off = readl(fpwm->base + FPWM_TICKS_OFF); | |
+ u32 ticks_on = readl(fpwm->base + FPWM_TICKS_ON); | |
+ | |
+ state->period = ticks_to_ns(ticks_on + ticks_off, rate); | |
+ state->duty_cycle = ticks_to_ns(ticks_on, rate); | |
+ state->polarity = PWM_POLARITY_NORMAL; | |
+ state->enabled = (ctrl == (CTRL_ENABLE | CTRL_OUTPUT_ENABLE)); | |
+ state->usage_power = false; | |
+} | |
+ | |
+static const struct pwm_ops apple_fpwm_ops = { | |
+ .apply = apple_fpwm_apply, | |
+ .get_state = apple_fpwm_get_state, | |
+}; | |
+ | |
+static int apple_fpwm_probe(struct platform_device *pdev) | |
+{ | |
+ struct apple_fpwm *fpwm; | |
+ int ret; | |
+ | |
+ fpwm = devm_kzalloc(&pdev->dev, sizeof(*fpwm), GFP_KERNEL); | |
+ if (!fpwm) | |
+ return -ENOMEM; | |
+ | |
+ fpwm->base = devm_platform_ioremap_resource(pdev, 0); | |
+ if (IS_ERR(fpwm->base)) { | |
+ return PTR_ERR(fpwm->base); | |
+ } | |
+ | |
+ fpwm->clk = devm_clk_get(&pdev->dev, NULL); | |
+ if (IS_ERR(fpwm->clk)) | |
+ return PTR_ERR(fpwm->clk); | |
+ | |
+ ret = clk_prepare_enable(fpwm->clk); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ fpwm->chip.dev = &pdev->dev; | |
+ fpwm->chip.ops = &apple_fpwm_ops; | |
+ fpwm->chip.npwm = 1; | |
+ | |
+ ret = devm_pwmchip_add(&pdev->dev, &fpwm->chip); | |
+ if (ret < 0) { | |
+ clk_disable_unprepare(fpwm->clk); | |
+ return ret; | |
+ } | |
+ | |
+ platform_set_drvdata(pdev, fpwm); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int apple_fpwm_remove(struct platform_device *pdev) | |
+{ | |
+ struct apple_fpwm *fpwm = platform_get_drvdata(pdev); | |
+ | |
+ clk_disable_unprepare(fpwm->clk); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static const struct of_device_id apple_fpwm_of_match[] = { | |
+ { .compatible = "apple,fpwm" }, | |
+ {}, | |
+}; | |
+MODULE_DEVICE_TABLE(of, apple_fpwm_of_match); | |
+ | |
+static struct platform_driver apple_fpwm_driver = { | |
+ .probe = apple_fpwm_probe, | |
+ .remove = apple_fpwm_remove, | |
+ .driver = { | |
+ .name = "apple-fpwm", | |
+ .of_match_table = apple_fpwm_of_match, | |
+ }, | |
+}; | |
+module_platform_driver(apple_fpwm_driver); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment