mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-01 06:48:52 +09:00
![Tom Rini](/assets/img/avatar_default.png)
We have a large number of places where while we historically referenced gd in the code we no longer do, as well as cases where the code added that line "just in case" during development and never dropped it. Signed-off-by: Tom Rini <trini@konsulko.com>
119 lines
2.8 KiB
C
119 lines
2.8 KiB
C
/*
|
|
* 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>
|
|
|
|
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(®s->tcfg0);
|
|
prescaler = (channel < 2 ? val : (val >> 8)) & 0xff;
|
|
div = (readl(®s->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, ®s->tcntb0 + offset);
|
|
writel(tcmp, ®s->tcmpb0 + offset);
|
|
}
|
|
|
|
tcon = readl(®s->tcon);
|
|
tcon |= TCON_UPDATE(channel);
|
|
if (channel < 4)
|
|
tcon |= TCON_AUTO_RELOAD(channel);
|
|
else
|
|
tcon |= TCON4_AUTO_RELOAD;
|
|
writel(tcon, ®s->tcon);
|
|
|
|
tcon &= ~TCON_UPDATE(channel);
|
|
writel(tcon, ®s->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(®s->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, ®s->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 *)devfdt_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),
|
|
};
|