aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@linaro.org>2013-03-18 21:29:04 +0800
committerHaojian Zhuang <haojian.zhuang@linaro.org>2013-04-04 17:07:53 +0800
commitadf6720b858c5153cecbe4adab4e7a194fd30055 (patch)
tree7a999c831b39ca6504a78f6e2bde49aaae1cb0cb
parent3a15d02839bb63cda65f43b8adcf3411a34b1278 (diff)
pwm: add hi3620 support
Support pwm driver of Hisilicon Hi3620 SoC. Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
-rw-r--r--drivers/pwm/Kconfig9
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-hi3620.c186
3 files changed, 196 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e513cd99817..8ffb7c56e18 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -46,6 +46,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.
+config PWM_HI3620
+ tristate "Hi3620 PWM support"
+ depends on ARCH_HS
+ help
+ Generic PWM framework driver for Hisilicon Hi3620 SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-hi3620.
+
config PWM_IMX
tristate "i.MX pwm support"
depends on ARCH_MXC
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 62a2963cfe5..076bdeb3965 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_PWM) += core.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_HI3620) += pwm-hi3620.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
diff --git a/drivers/pwm/pwm-hi3620.c b/drivers/pwm/pwm-hi3620.c
new file mode 100644
index 00000000000..1fbb6855cc0
--- /dev/null
+++ b/drivers/pwm/pwm-hi3620.c
@@ -0,0 +1,186 @@
+/*
+ * Hisilicon Hi3620 PWM driver
+ *
+ * Copyright (C) 2013 Hisilicon Ltd.
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+
+#define REG_OUT_EN 0x00
+#define REG_OUT_DIV 0x08
+#define REG_OUT_WIDE 0x10
+#define REG_OUT_WARN 0x18
+
+#define DELTA_NS 5
+
+struct hi3620_pwm_info {
+ struct pwm_chip chip;
+ struct clk *clk;
+ void __iomem *mmio_base;
+ unsigned int clk_rate;
+ int ratio;
+};
+
+static inline struct hi3620_pwm_info *to_hi3620_pwm_info(struct pwm_chip *chip)
+{
+ return container_of(chip, struct hi3620_pwm_info, chip);
+}
+
+static int hi3620_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+ int ratio, data;
+ unsigned long long int c;
+
+ if (!info->ratio) {
+ c = (unsigned long long int)period_ns * info->clk_rate;
+ do_div(c, NSEC_PER_SEC);
+ ratio = (int)c;
+ info->ratio = 1 << __fls(ratio);
+ writel_relaxed(info->ratio, info->mmio_base + REG_OUT_DIV);
+ readl_relaxed(info->mmio_base + REG_OUT_DIV);
+ }
+ c = (unsigned long long int)(duty_ns + DELTA_NS)* info->clk_rate;
+ do_div(c, NSEC_PER_SEC);
+ data = (int)c;
+ writel_relaxed(data, info->mmio_base + REG_OUT_WIDE);
+ readl_relaxed(info->mmio_base + REG_OUT_WIDE);
+ return 0;
+}
+
+static int hi3620_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+
+ writel_relaxed(1, info->mmio_base + REG_OUT_EN);
+ readl_relaxed(info->mmio_base + REG_OUT_EN);
+ return 0;
+}
+
+static void hi3620_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+
+ writel_relaxed(0, info->mmio_base + REG_OUT_EN);
+ readl_relaxed(info->mmio_base + REG_OUT_EN);
+}
+
+static const struct pwm_ops hi3620_pwm_ops = {
+ .config = hi3620_pwm_config,
+ .enable = hi3620_pwm_enable,
+ .disable = hi3620_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int hi3620_pwm_probe(struct platform_device *pdev)
+{
+ struct pinctrl *pinctrl;
+ struct resource *res;
+ struct hi3620_pwm_info *info;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(&pdev->dev, "unable to select pin group\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ info->mmio_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->mmio_base)
+ return -EADDRNOTAVAIL;
+
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(info->clk);
+ }
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+ info->clk_rate = clk_get_rate(info->clk);
+ if (!info->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+ info->chip.dev = &pdev->dev;
+ info->chip.ops = &hi3620_pwm_ops;
+ info->chip.base = -1;
+ info->chip.npwm = 1;
+
+ ret = pwmchip_add(&info->chip);
+ if (ret)
+ goto err;
+
+ platform_set_drvdata(pdev, info);
+ return 0;
+err:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int hi3620_pwm_remove(struct platform_device *pdev)
+{
+ struct hi3620_pwm_info *info;
+
+ info = platform_get_drvdata(pdev);
+ pwmchip_remove(&info->chip);
+ clk_disable_unprepare(info->clk);
+ return 0;
+}
+
+static struct of_device_id hi3620_pwm_of_match[] = {
+ { .compatible = "hisilicon,hi3620-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hi3620_pwm_of_match);
+
+static struct platform_driver hi3620_pwm_driver = {
+ .driver = {
+ .name = "hi3620-pwm",
+ .of_match_table = of_match_ptr(hi3620_pwm_of_match),
+ },
+ .probe = hi3620_pwm_probe,
+ .remove = hi3620_pwm_remove,
+};
+module_platform_driver(hi3620_pwm_driver);
+
+MODULE_LICENSE("GPLv2");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org");
+MODULE_ALIAS("platform:hi3620-pwm");