diff --git a/arch/arm/include/asm/arch-tegra/pwm.h b/arch/arm/include/asm/arch-tegra/pwm.h index 92dced4..eb2e465 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 b6d8c16..fd414b1 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 0000000..10e1fdc --- /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), +};