From 7429b9623b9d636d6fc686db3af29cc79d901bc6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 30 Jan 2016 16:37:46 -0700 Subject: [PATCH] tegra: pwm: Add a driver for the tegra PWM This PWM supports four channels. The driver always uses the 32KHz clock, and adjusts the duty cycle accordingly. Signed-off-by: Simon Glass Signed-off-by: Tom Warren --- arch/arm/include/asm/arch-tegra/pwm.h | 2 + drivers/pwm/Makefile | 3 + drivers/pwm/tegra_pwm.c | 85 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 drivers/pwm/tegra_pwm.c diff --git a/arch/arm/include/asm/arch-tegra/pwm.h b/arch/arm/include/asm/arch-tegra/pwm.h index 92dced448a..eb2e465772 100644 --- a/arch/arm/include/asm/arch-tegra/pwm.h +++ b/arch/arm/include/asm/arch-tegra/pwm.h @@ -27,6 +27,7 @@ struct pwm_ctlr { #define PWM_DIVIDER_SHIFT 0 #define PWM_DIVIDER_MASK (0x1FFF << PWM_DIVIDER_SHIFT) +#ifndef CONFIG_PWM /** * Program the PWM with the given parameters. * @@ -56,5 +57,6 @@ int pwm_request(const void *blob, int node, const char *prop_name); * @return 0 if ok, -1 if the device tree node was not found or invalid. */ int pwm_init(const void *blob); +#endif #endif /* __ASM_ARCH_TEGRA_PWM_H */ diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index b6d8c16604..fd414b1893 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -13,3 +13,6 @@ obj-$(CONFIG_DM_PWM) += pwm-uclass.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o +ifdef CONFIG_DM_PWM +obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o +endif diff --git a/drivers/pwm/tegra_pwm.c b/drivers/pwm/tegra_pwm.c new file mode 100644 index 0000000000..10e1fdc9b5 --- /dev/null +++ b/drivers/pwm/tegra_pwm.c @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct tegra_pwm_priv { + struct pwm_ctlr *regs; +}; + +static int tegra_pwm_set_config(struct udevice *dev, uint channel, + uint period_ns, uint duty_ns) +{ + struct tegra_pwm_priv *priv = dev_get_priv(dev); + struct pwm_ctlr *regs = priv->regs; + uint pulse_width; + u32 reg; + + if (channel >= 4) + return -EINVAL; + debug("%s: Configure '%s' channel %u\n", __func__, dev->name, channel); + /* We ignore the period here and just use 32KHz */ + clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, 32768); + + pulse_width = duty_ns * 255 / period_ns; + + reg = pulse_width << PWM_WIDTH_SHIFT; + reg |= 1 << PWM_DIVIDER_SHIFT; + writel(reg, ®s[channel].control); + debug("%s: pulse_width=%u\n", __func__, pulse_width); + + return 0; +} + +static int tegra_pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct tegra_pwm_priv *priv = dev_get_priv(dev); + struct pwm_ctlr *regs = priv->regs; + + if (channel >= 4) + return -EINVAL; + debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel); + clrsetbits_le32(®s[channel].control, PWM_ENABLE_MASK, + enable ? PWM_ENABLE_MASK : 0); + + return 0; +} + +static int tegra_pwm_ofdata_to_platdata(struct udevice *dev) +{ + struct tegra_pwm_priv *priv = dev_get_priv(dev); + + priv->regs = (struct pwm_ctlr *)dev_get_addr(dev); + + return 0; +} + +static const struct pwm_ops tegra_pwm_ops = { + .set_config = tegra_pwm_set_config, + .set_enable = tegra_pwm_set_enable, +}; + +static const struct udevice_id tegra_pwm_ids[] = { + { .compatible = "nvidia,tegra124-pwm" }, + { .compatible = "nvidia,tegra20-pwm" }, + { } +}; + +U_BOOT_DRIVER(tegra_pwm) = { + .name = "tegra_pwm", + .id = UCLASS_PWM, + .of_match = tegra_pwm_ids, + .ops = &tegra_pwm_ops, + .ofdata_to_platdata = tegra_pwm_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct tegra_pwm_priv), +}; -- 2.39.5