Commit 5c2dd4cd authored by Simon Glass's avatar Simon Glass Committed by Minkyu Kang

exynos: pwm: Add a driver for the exynos5 PWM

This driver supports the standard PWM API. There are 5 PWMs. Four are used
normally and the last is normally used as a timer.
Signed-off-by: default avatarSimon Glass <sjg@chromium.org>
Signed-off-by: default avatarMinkyu Kang <mk7.kang@samsung.com>
parent 0c84358c
......@@ -12,6 +12,9 @@
#include <asm/io.h>
#include <asm/arch/pwm.h>
#include <asm/arch/clk.h>
/* Use the old PWM interface for now */
#undef CONFIG_DM_PWM
#include <pwm.h>
DECLARE_GLOBAL_DATA_PTR;
......
......@@ -9,6 +9,15 @@ config DM_PWM
frequency/period can be controlled along with the proportion of that
time that the signal is high.
config PWM_EXYNOS
bool "Enable support for the Exynos PWM"
depends on DM_PWM
help
This PWM is found on Samsung Exynos 5250 and other Samsung SoCs. It
supports a programmable period and duty cycle. A 32-bit counter is
used. It provides 5 channels which can be independently
programmed. Channel 4 (the last) is normally used as a timer.
config PWM_ROCKCHIP
bool "Enable support for the Rockchip PWM"
depends on DM_PWM
......
......@@ -15,4 +15,5 @@ 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
obj-$(CONFIG_PWM_EXYNOS) += exynos_pwm.o
endif
/*
* Copyright 2016 Google Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <pwm.h>
#include <asm/io.h>
#include <asm/arch/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/pwm.h>
DECLARE_GLOBAL_DATA_PTR;
struct exynos_pwm_priv {
struct s5p_timer *regs;
};
static int exynos_pwm_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct exynos_pwm_priv *priv = dev_get_priv(dev);
struct s5p_timer *regs = priv->regs;
unsigned int offset, prescaler;
uint div = 4, rate, rate_ns;
u32 val;
u32 tcnt, tcmp, tcon;
if (channel >= 5)
return -EINVAL;
debug("%s: Configure '%s' channel %u, period_ns %u, duty_ns %u\n",
__func__, dev->name, channel, period_ns, duty_ns);
val = readl(&regs->tcfg0);
prescaler = (channel < 2 ? val : (val >> 8)) & 0xff;
div = (readl(&regs->tcfg1) >> MUX_DIV_SHIFT(channel)) & 0xf;
rate = get_pwm_clk() / ((prescaler + 1) * (1 << div));
debug("%s: pwm_clk %lu, rate %u\n", __func__, get_pwm_clk(), rate);
if (channel < 4) {
rate_ns = 1000000000 / rate;
tcnt = period_ns / rate_ns;
tcmp = duty_ns / rate_ns;
debug("%s: tcnt %u, tcmp %u\n", __func__, tcnt, tcmp);
offset = channel * 3;
writel(tcnt, &regs->tcntb0 + offset);
writel(tcmp, &regs->tcmpb0 + offset);
}
tcon = readl(&regs->tcon);
tcon |= TCON_UPDATE(channel);
if (channel < 4)
tcon |= TCON_AUTO_RELOAD(channel);
else
tcon |= TCON4_AUTO_RELOAD;
writel(tcon, &regs->tcon);
tcon &= ~TCON_UPDATE(channel);
writel(tcon, &regs->tcon);
return 0;
}
static int exynos_pwm_set_enable(struct udevice *dev, uint channel,
bool enable)
{
struct exynos_pwm_priv *priv = dev_get_priv(dev);
struct s5p_timer *regs = priv->regs;
u32 mask;
if (channel >= 4)
return -EINVAL;
debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel);
mask = TCON_START(channel);
clrsetbits_le32(&regs->tcon, mask, enable ? mask : 0);
return 0;
}
static int exynos_pwm_probe(struct udevice *dev)
{
struct exynos_pwm_priv *priv = dev_get_priv(dev);
struct s5p_timer *regs = priv->regs;
writel(PRESCALER_0 | PRESCALER_1 << 8, &regs->tcfg0);
return 0;
}
static int exynos_pwm_ofdata_to_platdata(struct udevice *dev)
{
struct exynos_pwm_priv *priv = dev_get_priv(dev);
priv->regs = (struct s5p_timer *)dev_get_addr(dev);
return 0;
}
static const struct pwm_ops exynos_pwm_ops = {
.set_config = exynos_pwm_set_config,
.set_enable = exynos_pwm_set_enable,
};
static const struct udevice_id exynos_channels[] = {
{ .compatible = "samsung,exynos4210-pwm" },
{ }
};
U_BOOT_DRIVER(exynos_pwm) = {
.name = "exynos_pwm",
.id = UCLASS_PWM,
.of_match = exynos_channels,
.ops = &exynos_pwm_ops,
.probe = exynos_pwm_probe,
.ofdata_to_platdata = exynos_pwm_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct exynos_pwm_priv),
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment