summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch
diff options
context:
space:
mode:
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.patch514
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
-