diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch | 514 |
1 files changed, 0 insertions, 514 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch deleted file mode 100644 index 0fdd40d77..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch +++ /dev/null @@ -1,514 +0,0 @@ -From b20b1ce81f4451d9e906b84e7edcc22d4e70e7df Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> -Date: Mon, 11 Feb 2019 17:02:35 -0800 -Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP - -This commit adds Aspeed PWM driver which uses timer pulse output -feature in Aspeed SoCs. The timer IP is derived from Faraday -Technologies FTTMR010 IP but has some customized register -structure changes only for Aspeed SoCs. - -Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> ---- - arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- - drivers/pwm/Kconfig | 9 + - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-fttmr010.c | 441 +++++++++++++++++++++++++++++++++++++++ - 4 files changed, 452 insertions(+), 1 deletion(-) - create mode 100644 drivers/pwm/pwm-fttmr010.c - -diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 27ed188296a1..45202bc3d60c 100644 ---- a/arch/arm/boot/dts/aspeed-g5.dtsi -+++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -341,7 +341,7 @@ - - timer: timer@1e782000 { - /* This timer is a Faraday FTTMR010 derivative */ -- compatible = "aspeed,ast2400-timer"; -+ compatible = "aspeed,ast2500-timer"; - reg = <0x1e782000 0x90>; - interrupts = <16 17 18 35 36 37 38 39>; - clocks = <&syscon ASPEED_CLK_APB>; -diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig -index e3a2518503ed..21def8cd5af6 100644 ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -171,6 +171,15 @@ config PWM_FSL_FTM - To compile this driver as a module, choose M here: the module - will be called pwm-fsl-ftm. - -+config PWM_FTTMR010 -+ tristate "Faraday Technology FTTMR010 timer PWM support" -+ help -+ Generic PWM framework driver for Faraday Technology FTTMR010 Timer -+ PWM output -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pwm-fttmr010 -+ - config PWM_HIBVT - tristate "HiSilicon BVT PWM support" - depends on ARCH_HISI || COMPILE_TEST -diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile -index 26326adf71d7..3a9e9840b5ae 100644 ---- a/drivers/pwm/Makefile -+++ b/drivers/pwm/Makefile -@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o - obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o - obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o - obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o -+obj-$(CONFIG_PWM_FTTMR010) += pwm-fttmr010.o - obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o - obj-$(CONFIG_PWM_IMG) += pwm-img.o - obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o -diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c -new file mode 100644 -index 000000000000..283ded6906f1 ---- /dev/null -+++ b/drivers/pwm/pwm-fttmr010.c -@@ -0,0 +1,441 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2019 Intel Corporation -+ -+#include <linux/clk.h> -+#include <linux/io.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/pwm.h> -+ -+#define TIMER_CR 0x30 -+ -+#define TIMER5_ASPEED_COUNT 0x50 -+#define TIMER5_ASPEED_LOAD 0x54 -+#define TIMER5_ASPEED_MATCH1 0x58 -+#define TIMER5_ASPEED_MATCH2 0x5c -+#define TIMER6_ASPEED_COUNT 0x60 -+#define TIMER6_ASPEED_LOAD 0x64 -+#define TIMER6_ASPEED_MATCH1 0x68 -+#define TIMER6_ASPEED_MATCH2 0x6c -+#define TIMER7_ASPEED_COUNT 0x70 -+#define TIMER7_ASPEED_LOAD 0x74 -+#define TIMER7_ASPEED_MATCH1 0x78 -+#define TIMER7_ASPEED_MATCH2 0x7c -+#define TIMER8_ASPEED_COUNT 0x80 -+#define TIMER8_ASPEED_LOAD 0x84 -+#define TIMER8_ASPEED_MATCH1 0x88 -+#define TIMER8_ASPEED_MATCH2 0x8c -+ -+#define TIMER_5_CR_ASPEED_ENABLE BIT(16) -+#define TIMER_5_CR_ASPEED_CLOCK BIT(17) -+#define TIMER_5_CR_ASPEED_INT BIT(18) -+#define TIMER_5_CR_ASPEED_PULSE_OUT BIT(19) -+#define TIMER_6_CR_ASPEED_ENABLE BIT(20) -+#define TIMER_6_CR_ASPEED_CLOCK BIT(21) -+#define TIMER_6_CR_ASPEED_INT BIT(22) -+#define TIMER_6_CR_ASPEED_PULSE_OUT BIT(23) -+#define TIMER_7_CR_ASPEED_ENABLE BIT(24) -+#define TIMER_7_CR_ASPEED_CLOCK BIT(25) -+#define TIMER_7_CR_ASPEED_INT BIT(26) -+#define TIMER_7_CR_ASPEED_PULSE_OUT BIT(27) -+#define TIMER_8_CR_ASPEED_ENABLE BIT(28) -+#define TIMER_8_CR_ASPEED_CLOCK BIT(29) -+#define TIMER_8_CR_ASPEED_INT BIT(30) -+#define TIMER_8_CR_ASPEED_PULSE_OUT BIT(31) -+ -+/** -+ * struct pwm_fttmr010_variant - variant data depends on SoC -+ * @bits: timer counter resolution -+ * @chan_min: lowest timer channel which has pwm pulse output -+ * @chan_max: highest timer channel which has pwm pulse output -+ * @output_mask: pwm pulse output mask which is defined in device tree -+ */ -+struct pwm_fttmr010_variant { -+ u8 bits; -+ u8 chan_min; -+ u8 chan_max; -+ u8 output_mask; -+}; -+ -+/** -+ * struct pwm_fttmr010_chan - private data of FTTMR010 PWM channel -+ * @period_ns: current period in nanoseconds programmed to the hardware -+ * @duty_ns: current duty time in nanoseconds programmed to the hardware -+ */ -+struct pwm_fttmr010_chan { -+ u32 period_ns; -+ u32 duty_ns; -+}; -+ -+/** -+ * struct pwm_fttmr010 - private data of FTTMR010 PWM -+ * @chip: generic PWM chip -+ * @variant: local copy of hardware variant data -+ * @disabled_mask: disabled status for all channels - one bit per channel -+ * @base: base address of mapped PWM registers -+ * @clk: clock used to drive the timers -+ */ -+struct pwm_fttmr010 { -+ struct pwm_chip chip; -+ struct pwm_fttmr010_variant variant; -+ u8 disabled_mask; -+ void __iomem *base; -+ struct clk *clk; -+ u32 clk_tick_ns; -+}; -+ -+static inline -+struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip) -+{ -+ return container_of(chip, struct pwm_fttmr010, chip); -+} -+ -+static int pwm_fttmr010_request(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); -+ struct pwm_fttmr010_chan *chan; -+ -+ if (!(priv->variant.output_mask & BIT(pwm->hwpwm))) { -+ dev_warn(chip->dev, -+ "tried to request PWM channel %d without output\n", -+ pwm->hwpwm); -+ return -EINVAL; -+ } -+ -+ chan = devm_kzalloc(chip->dev, sizeof(*chan), GFP_KERNEL); -+ if (!chan) -+ return -ENOMEM; -+ -+ pwm_set_chip_data(pwm, chan); -+ -+ return 0; -+} -+ -+static void pwm_fttmr010_free(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ devm_kfree(chip->dev, pwm_get_chip_data(pwm)); -+ pwm_set_chip_data(pwm, NULL); -+} -+ -+static int pwm_fttmr010_enable(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); -+ u32 cr; -+ -+ cr = readl(priv->base + TIMER_CR); -+ -+ switch (pwm->hwpwm) { -+ case 5: -+ cr |= (TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); -+ break; -+ case 6: -+ cr |= (TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); -+ break; -+ case 7: -+ cr |= (TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); -+ break; -+ case 8: -+ cr |= (TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); -+ break; -+ default: -+ return -ERANGE; -+ } -+ -+ writel(cr, priv->base + TIMER_CR); -+ priv->disabled_mask &= ~BIT(pwm->hwpwm); -+ -+ return 0; -+} -+ -+static void pwm_fttmr010_disable(struct pwm_chip *chip, struct pwm_device *pwm) -+{ -+ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); -+ u32 cr; -+ -+ cr = readl(priv->base + TIMER_CR); -+ -+ switch (pwm->hwpwm) { -+ case 5: -+ cr &= ~(TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); -+ break; -+ case 6: -+ cr &= ~(TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); -+ break; -+ case 7: -+ cr &= ~(TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); -+ break; -+ case 8: -+ cr &= ~(TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); -+ break; -+ default: -+ return; -+ } -+ -+ writel(cr, priv->base + TIMER_CR); -+ priv->disabled_mask |= BIT(pwm->hwpwm); -+} -+ -+static int pwm_fttmr010_config(struct pwm_chip *chip, struct pwm_device *pwm, -+ int duty_ns, int period_ns) -+{ -+ u32 tload, tmatch, creg_offset, lreg_offset, mreg_offset; -+ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); -+ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); -+ -+ /* -+ * We currently avoid using 64bit arithmetic by using the -+ * fact that anything faster than 1Hz is easily representable -+ * by 32bits. -+ */ -+ if (period_ns > NSEC_PER_SEC) -+ return -ERANGE; -+ -+ /* No need to update */ -+ if (chan->period_ns == period_ns || chan->duty_ns == duty_ns) -+ return 0; -+ -+ tload = period_ns / priv->clk_tick_ns; -+ -+ /* Period is too short */ -+ if (tload <= 1) -+ return -ERANGE; -+ -+ tmatch = duty_ns / priv->clk_tick_ns; -+ -+ /* 0% duty is not available */ -+ if (!tmatch) -+ ++tmatch; -+ -+ tmatch = tload - tmatch; -+ -+ /* Decrement to get tick numbers, instead of tick counts */ -+ --tload; -+ --tmatch; -+ -+ if (tload == 0 || tmatch == 0) -+ return -ERANGE; -+ -+ dev_dbg(priv->chip.dev, "clk_tick_ns:%u, tload:%u, tmatch:%u\n", -+ priv->clk_tick_ns, tload, tmatch); -+ -+ switch (pwm->hwpwm) { -+ case 5: -+ creg_offset = TIMER5_ASPEED_COUNT; -+ lreg_offset = TIMER5_ASPEED_LOAD; -+ mreg_offset = TIMER5_ASPEED_MATCH1; -+ break; -+ case 6: -+ creg_offset = TIMER6_ASPEED_COUNT; -+ lreg_offset = TIMER6_ASPEED_LOAD; -+ mreg_offset = TIMER6_ASPEED_MATCH1; -+ break; -+ case 7: -+ creg_offset = TIMER7_ASPEED_COUNT; -+ lreg_offset = TIMER7_ASPEED_LOAD; -+ mreg_offset = TIMER7_ASPEED_MATCH1; -+ break; -+ case 8: -+ creg_offset = TIMER8_ASPEED_COUNT; -+ lreg_offset = TIMER8_ASPEED_LOAD; -+ mreg_offset = TIMER8_ASPEED_MATCH1; -+ break; -+ default: -+ return -ERANGE; -+ } -+ -+ writel(tload, priv->base + creg_offset); -+ writel(tload, priv->base + lreg_offset); -+ writel(tmatch, priv->base + mreg_offset); -+ -+ chan->period_ns = period_ns; -+ chan->duty_ns = duty_ns; -+ -+ return 0; -+} -+ -+static const struct pwm_ops pwm_fttmr010_ops = { -+ .request = pwm_fttmr010_request, -+ .free = pwm_fttmr010_free, -+ .enable = pwm_fttmr010_enable, -+ .disable = pwm_fttmr010_disable, -+ .config = pwm_fttmr010_config, -+ .owner = THIS_MODULE, -+}; -+ -+#ifdef CONFIG_OF -+static const struct pwm_fttmr010_variant aspeed_variant = { -+ .bits = 32, -+ .chan_min = 5, -+ .chan_max = 8, -+}; -+ -+static const struct of_device_id pwm_fttmr010_matches[] = { -+ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant }, -+ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches); -+ -+static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) -+{ -+ struct device_node *np = priv->chip.dev->of_node; -+ const struct of_device_id *match; -+ struct property *prop; -+ const __be32 *cur; -+ u32 val; -+ -+ match = of_match_node(pwm_fttmr010_matches, np); -+ if (!match) -+ return -ENODEV; -+ -+ memcpy(&priv->variant, match->data, sizeof(priv->variant)); -+ -+ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) { -+ if (val < priv->variant.chan_min || -+ val > priv->variant.chan_max) { -+ dev_err(priv->chip.dev, -+ "invalid channel index in fttmr010,pwm-outputs property\n"); -+ continue; -+ } -+ priv->variant.output_mask |= BIT(val); -+ } -+ -+ return 0; -+} -+#else -+static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) -+{ -+ return -ENODEV; -+} -+#endif -+ -+static int pwm_fttmr010_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct pwm_fttmr010 *priv; -+ struct resource *res; -+ ulong clk_rate; -+ int ret; -+ -+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->chip.dev = &pdev->dev; -+ priv->chip.ops = &pwm_fttmr010_ops; -+ priv->chip.base = -1; -+ -+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { -+ ret = pwm_fttmr010_parse_dt(priv); -+ if (ret) -+ return ret; -+ -+ priv->chip.of_xlate = of_pwm_xlate_with_flags; -+ priv->chip.of_pwm_n_cells = 3; -+ } else { -+ if (!pdev->dev.platform_data) { -+ dev_err(&pdev->dev, "no platform data specified\n"); -+ return -EINVAL; -+ } -+ -+ memcpy(&priv->variant, pdev->dev.platform_data, -+ sizeof(priv->variant)); -+ } -+ -+ priv->chip.npwm = priv->variant.chan_max + 1; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(priv->base)) -+ return PTR_ERR(priv->base); -+ -+ priv->clk = devm_clk_get(&pdev->dev, "PCLK"); -+ if (IS_ERR(priv->clk)) { -+ dev_err(dev, "failed to get timer base clk\n"); -+ return PTR_ERR(priv->clk); -+ } -+ -+ ret = clk_prepare_enable(priv->clk); -+ if (ret < 0) { -+ dev_err(dev, "failed to enable base clock\n"); -+ return ret; -+ } -+ -+ clk_rate = clk_get_rate(priv->clk); -+ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate; -+ -+ platform_set_drvdata(pdev, priv); -+ -+ ret = pwmchip_add(&priv->chip); -+ if (ret < 0) { -+ dev_err(dev, "failed to register PWM chip\n"); -+ clk_disable_unprepare(priv->clk); -+ return ret; -+ } -+ -+ dev_dbg(dev, "clk at %lu\n", clk_rate); -+ -+ return 0; -+} -+ -+static int pwm_fttmr010_remove(struct platform_device *pdev) -+{ -+ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev); -+ int ret; -+ -+ ret = pwmchip_remove(&priv->chip); -+ if (ret < 0) -+ return ret; -+ -+ clk_disable_unprepare(priv->clk); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM_SLEEP -+static int pwm_fttmr010_resume(struct device *dev) -+{ -+ struct pwm_fttmr010 *priv = dev_get_drvdata(dev); -+ struct pwm_chip *chip = &priv->chip; -+ unsigned int i; -+ -+ for (i = priv->variant.chan_min; i < priv->variant.chan_max; i++) { -+ struct pwm_device *pwm = &chip->pwms[i]; -+ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); -+ -+ if (!chan) -+ continue; -+ -+ if (chan->period_ns) { -+ pwm_fttmr010_config(chip, pwm, chan->duty_ns, -+ chan->period_ns); -+ } -+ -+ if (priv->disabled_mask & BIT(i)) -+ pwm_fttmr010_disable(chip, pwm); -+ else -+ pwm_fttmr010_enable(chip, pwm); -+ } -+ -+ return 0; -+} -+#endif -+ -+static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume); -+ -+static struct platform_driver pwm_fttmr010_driver = { -+ .driver = { -+ .name = "fttmr010-timer-pwm", -+ .pm = &pwm_fttmr010_pm_ops, -+ .of_match_table = of_match_ptr(pwm_fttmr010_matches), -+ }, -+ .probe = pwm_fttmr010_probe, -+ .remove = pwm_fttmr010_remove, -+}; -+module_platform_driver(pwm_fttmr010_driver); -+ -+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); -+MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs"); -+MODULE_LICENSE("GPL v2"); --- -2.7.4 - |