GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,57 @@
config DM_PWM
bool "Enable support for pulse-width modulation devices (PWM)"
depends on DM
help
A pulse-width modulator emits a pulse of varying width and provides
control over the duty cycle (high and low time) of the signal. This
is often used to control a voltage level. The more time the PWM
spends in the 'high' state, the higher the voltage. The PWM's
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_IMX
bool "Enable support for i.MX27 and later PWM"
help
This PWM is found i.MX27 and later i.MX SoCs.
config PWM_ROCKCHIP
bool "Enable support for the Rockchip PWM"
depends on DM_PWM
help
This PWM is found on RK3288 and other Rockchip SoCs. It supports a
programmable period and duty cycle. A 32-bit counter is used.
Various options provided in the hardware (such as capture mode and
continuous/single-shot) are not supported by the driver.
config PWM_SANDBOX
bool "Enable support for the sandbox PWM"
help
This is a sandbox PWM used for testing. It provides 3 channels and
records the settings passed into it, but otherwise does nothing
useful. The PWM can be enabled but is not connected to any outputs
so this is not very useful.
config PWM_TEGRA
bool "Enable support for the Tegra PWM"
depends on DM_PWM
help
This PWM is found on Tegra 20 and other Nvidia SoCs. It supports
four channels with a programmable period and duty cycle. Only a
32KHz clock is supported by the driver but the duty cycle is
configurable.
config PWM_SUNXI
bool "Enable support for the Allwinner Sunxi PWM"
depends on DM_PWM
help
This PWM is found on H3, A64 and other Allwinner SoCs. It supports a
programmable period and duty cycle. A 16-bit counter is used.
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2001
# Erik Theisen, Wave 7 Optics, etheisen@mindspring.com.
#ccflags-y += -DDEBUG
obj-$(CONFIG_DM_PWM) += pwm-uclass.o
obj-$(CONFIG_PWM_EXYNOS) += exynos_pwm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o
obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o
obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2016 Google Inc.
*/
#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(&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 *)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),
};
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* (C) Copyright 2014
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Basic support for the pwm module on imx6.
*
* Based on linux:drivers/pwm/pwm-imx.c
* from
* Sascha Hauer <s.hauer@pengutronix.de>
*/
#include <common.h>
#include <div64.h>
#include <asm/arch/imx-regs.h>
/* pwm_id from 0..7 */
struct pwm_regs *pwm_id_to_reg(int pwm_id)
{
switch (pwm_id) {
case 0:
return (struct pwm_regs *)PWM1_BASE_ADDR;
case 1:
return (struct pwm_regs *)PWM2_BASE_ADDR;
#ifdef CONFIG_MX6
case 2:
return (struct pwm_regs *)PWM3_BASE_ADDR;
case 3:
return (struct pwm_regs *)PWM4_BASE_ADDR;
#endif
#ifdef CONFIG_MX6SX
case 4:
return (struct pwm_regs *)PWM5_BASE_ADDR;
case 5:
return (struct pwm_regs *)PWM6_BASE_ADDR;
case 6:
return (struct pwm_regs *)PWM7_BASE_ADDR;
case 7:
return (struct pwm_regs *)PWM8_BASE_ADDR;
#endif
default:
printf("unknown pwm_id: %d\n", pwm_id);
break;
}
return NULL;
}
int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c,
unsigned long *duty_c, unsigned long *prescale)
{
unsigned long long c;
/*
* we have not yet a clock framework for imx6, so add the clock
* value here as a define. Replace it when we have the clock
* framework.
*/
c = CONFIG_IMX6_PWM_PER_CLK;
c = c * period_ns;
do_div(c, 1000000000);
*period_c = c;
*prescale = *period_c / 0x10000 + 1;
*period_c /= *prescale;
c = *period_c * (unsigned long long)duty_ns;
do_div(c, period_ns);
*duty_c = c;
/*
* according to imx pwm RM, the real period value should be
* PERIOD value in PWMPR plus 2.
*/
if (*period_c > 2)
*period_c -= 2;
else
*period_c = 0;
return 0;
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* (C) Copyright 2014
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Basic support for the pwm module on imx6.
*/
#ifndef _pwm_imx_util_h_
#define _pwm_imx_util_h_
struct pwm_regs *pwm_id_to_reg(int pwm_id);
int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c,
unsigned long *duty_c, unsigned long *prescale);
#endif
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2014
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Basic support for the pwm module on imx6.
*/
#include <common.h>
#include <div64.h>
#include <dm.h>
#include <pwm.h>
#include <asm/arch/imx-regs.h>
#include <asm/io.h>
#include "pwm-imx-util.h"
int pwm_init(int pwm_id, int div, int invert)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
if (!pwm)
return -1;
writel(0, &pwm->ir);
return 0;
}
int pwm_config_internal(struct pwm_regs *pwm, unsigned long period_cycles,
unsigned long duty_cycles, unsigned long prescale)
{
u32 cr;
writel(0, &pwm->ir);
cr = PWMCR_PRESCALER(prescale) |
PWMCR_DOZEEN | PWMCR_WAITEN |
PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH;
writel(cr, &pwm->cr);
/* set duty cycles */
writel(duty_cycles, &pwm->sar);
/* set period cycles */
writel(period_cycles, &pwm->pr);
return 0;
}
int pwm_config(int pwm_id, int duty_ns, int period_ns)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
unsigned long period_cycles, duty_cycles, prescale;
if (!pwm)
return -1;
pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
&prescale);
return pwm_config_internal(pwm, period_cycles, duty_cycles, prescale);
}
int pwm_enable(int pwm_id)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
if (!pwm)
return -1;
setbits_le32(&pwm->cr, PWMCR_EN);
return 0;
}
void pwm_disable(int pwm_id)
{
struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
if (!pwm)
return;
clrbits_le32(&pwm->cr, PWMCR_EN);
}
#if defined(CONFIG_DM_PWM)
struct imx_pwm_priv {
struct pwm_regs *regs;
bool invert;
};
static int imx_pwm_set_invert(struct udevice *dev, uint channel,
bool polarity)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
debug("%s: polarity=%u\n", __func__, polarity);
priv->invert = polarity;
return 0;
}
static int imx_pwm_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
struct pwm_regs *regs = priv->regs;
unsigned long period_cycles, duty_cycles, prescale;
debug("%s: Config '%s' channel: %d\n", __func__, dev->name, channel);
pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
&prescale);
return pwm_config_internal(regs, period_cycles, duty_cycles, prescale);
};
static int imx_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
struct pwm_regs *regs = priv->regs;
debug("%s: Enable '%s' state: %d\n", __func__, dev->name, enable);
if (enable)
setbits_le32(&regs->cr, PWMCR_EN);
else
clrbits_le32(&regs->cr, PWMCR_EN);
return 0;
};
static int imx_pwm_ofdata_to_platdata(struct udevice *dev)
{
struct imx_pwm_priv *priv = dev_get_priv(dev);
priv->regs = (struct pwm_regs *)devfdt_get_addr(dev);
return 0;
}
static int imx_pwm_probe(struct udevice *dev)
{
return 0;
}
static const struct pwm_ops imx_pwm_ops = {
.set_invert = imx_pwm_set_invert,
.set_config = imx_pwm_set_config,
.set_enable = imx_pwm_set_enable,
};
static const struct udevice_id imx_pwm_ids[] = {
{ .compatible = "fsl,imx27-pwm" },
{ }
};
U_BOOT_DRIVER(imx_pwm) = {
.name = "imx_pwm",
.id = UCLASS_PWM,
.of_match = imx_pwm_ids,
.ops = &imx_pwm_ops,
.ofdata_to_platdata = imx_pwm_ofdata_to_platdata,
.probe = imx_pwm_probe,
.priv_auto_alloc_size = sizeof(struct imx_pwm_priv),
};
#endif
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <pwm.h>
int pwm_set_invert(struct udevice *dev, uint channel, bool polarity)
{
struct pwm_ops *ops = pwm_get_ops(dev);
if (!ops->set_invert)
return -ENOSYS;
return ops->set_invert(dev, channel, polarity);
}
int pwm_set_config(struct udevice *dev, uint channel, uint period_ns,
uint duty_ns)
{
struct pwm_ops *ops = pwm_get_ops(dev);
if (!ops->set_config)
return -ENOSYS;
return ops->set_config(dev, channel, period_ns, duty_ns);
}
int pwm_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct pwm_ops *ops = pwm_get_ops(dev);
if (!ops->set_enable)
return -ENOSYS;
return ops->set_enable(dev, channel, enable);
}
UCLASS_DRIVER(pwm) = {
.id = UCLASS_PWM,
.name = "pwm",
};
@@ -0,0 +1,217 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <clk.h>
#include <div64.h>
#include <dm.h>
#include <pwm.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm/arch-rockchip/pwm.h>
#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
struct rockchip_pwm_data {
struct rockchip_pwm_regs regs;
unsigned int prescaler;
bool supports_polarity;
bool supports_lock;
u32 enable_conf;
u32 enable_conf_mask;
};
struct rk_pwm_priv {
fdt_addr_t base;
ulong freq;
u32 conf_polarity;
const struct rockchip_pwm_data *data;
};
static int rk_pwm_set_invert(struct udevice *dev, uint channel, bool polarity)
{
struct rk_pwm_priv *priv = dev_get_priv(dev);
if (!priv->data->supports_polarity) {
debug("%s: Do not support polarity\n", __func__);
return 0;
}
debug("%s: polarity=%u\n", __func__, polarity);
if (polarity)
priv->conf_polarity = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
else
priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
return 0;
}
static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns,
uint duty_ns)
{
struct rk_pwm_priv *priv = dev_get_priv(dev);
const struct rockchip_pwm_regs *regs = &priv->data->regs;
unsigned long period, duty;
u32 ctrl;
debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
ctrl = readl(priv->base + regs->ctrl);
/*
* Lock the period and duty of previous configuration, then
* change the duty and period, that would not be effective.
*/
if (priv->data->supports_lock) {
ctrl |= PWM_LOCK;
writel(ctrl, priv->base + regs->ctrl);
}
period = lldiv((uint64_t)priv->freq * period_ns,
priv->data->prescaler * 1000000000);
duty = lldiv((uint64_t)priv->freq * duty_ns,
priv->data->prescaler * 1000000000);
writel(period, priv->base + regs->period);
writel(duty, priv->base + regs->duty);
if (priv->data->supports_polarity) {
ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);
ctrl |= priv->conf_polarity;
}
/*
* Unlock and set polarity at the same time,
* the configuration of duty, period and polarity
* would be effective together at next period.
*/
if (priv->data->supports_lock)
ctrl &= ~PWM_LOCK;
writel(ctrl, priv->base + regs->ctrl);
debug("%s: period=%lu, duty=%lu\n", __func__, period, duty);
return 0;
}
static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct rk_pwm_priv *priv = dev_get_priv(dev);
const struct rockchip_pwm_regs *regs = &priv->data->regs;
u32 ctrl;
debug("%s: Enable '%s'\n", __func__, dev->name);
ctrl = readl(priv->base + regs->ctrl);
ctrl &= ~priv->data->enable_conf_mask;
if (enable)
ctrl |= priv->data->enable_conf;
else
ctrl &= ~priv->data->enable_conf;
writel(ctrl, priv->base + regs->ctrl);
return 0;
}
static int rk_pwm_ofdata_to_platdata(struct udevice *dev)
{
struct rk_pwm_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr(dev);
return 0;
}
static int rk_pwm_probe(struct udevice *dev)
{
struct rk_pwm_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret = 0;
ret = clk_get_by_index(dev, 0, &clk);
if (ret < 0) {
debug("%s get clock fail!\n", __func__);
return -EINVAL;
}
priv->freq = clk_get_rate(&clk);
priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev);
if (priv->data->supports_polarity)
priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE;
return 0;
}
static const struct pwm_ops rk_pwm_ops = {
.set_invert = rk_pwm_set_invert,
.set_config = rk_pwm_set_config,
.set_enable = rk_pwm_set_enable,
};
static const struct rockchip_pwm_data pwm_data_v1 = {
.regs = {
.duty = 0x04,
.period = 0x08,
.cntr = 0x00,
.ctrl = 0x0c,
},
.prescaler = 2,
.supports_polarity = false,
.supports_lock = false,
.enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
.enable_conf_mask = BIT(1) | BIT(3),
};
static const struct rockchip_pwm_data pwm_data_v2 = {
.regs = {
.duty = 0x08,
.period = 0x04,
.cntr = 0x00,
.ctrl = 0x0c,
},
.prescaler = 1,
.supports_polarity = true,
.supports_lock = false,
.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
PWM_CONTINUOUS,
.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
};
static const struct rockchip_pwm_data pwm_data_v3 = {
.regs = {
.duty = 0x08,
.period = 0x04,
.cntr = 0x00,
.ctrl = 0x0c,
},
.prescaler = 1,
.supports_polarity = true,
.supports_lock = true,
.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
PWM_CONTINUOUS,
.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
};
static const struct udevice_id rk_pwm_ids[] = {
{ .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1},
{ .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2},
{ .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3},
{ }
};
U_BOOT_DRIVER(rk_pwm) = {
.name = "rk_pwm",
.id = UCLASS_PWM,
.of_match = rk_pwm_ids,
.ops = &rk_pwm_ops,
.ofdata_to_platdata = rk_pwm_ofdata_to_platdata,
.probe = rk_pwm_probe,
.priv_auto_alloc_size = sizeof(struct rk_pwm_priv),
};
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <pwm.h>
#include <asm/test.h>
enum {
NUM_CHANNELS = 3,
};
/**
* struct sandbox_pwm_chan - a sandbox PWM channel
*
* @period_ns: Period of the PWM in nanoseconds
* @duty_ns: Current duty cycle of the PWM in nanoseconds
* @enable: true if the PWM is enabled
* @polarity: true if the PWM polarity is active high
*/
struct sandbox_pwm_chan {
uint period_ns;
uint duty_ns;
bool enable;
bool polarity;
};
struct sandbox_pwm_priv {
struct sandbox_pwm_chan chan[NUM_CHANNELS];
};
int sandbox_pwm_get_config(struct udevice *dev, uint channel, uint *period_nsp,
uint *duty_nsp, bool *enablep, bool *polarityp)
{
struct sandbox_pwm_priv *priv = dev_get_priv(dev);
struct sandbox_pwm_chan *chan;
if (channel >= NUM_CHANNELS)
return -ENOSPC;
chan = &priv->chan[channel];
*period_nsp = chan->period_ns;
*duty_nsp = chan->duty_ns;
*enablep = chan->enable;
*polarityp = chan->polarity;
return 0;
}
static int sandbox_pwm_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct sandbox_pwm_priv *priv = dev_get_priv(dev);
struct sandbox_pwm_chan *chan;
if (channel >= NUM_CHANNELS)
return -ENOSPC;
chan = &priv->chan[channel];
chan->period_ns = period_ns;
chan->duty_ns = duty_ns;
return 0;
}
static int sandbox_pwm_set_enable(struct udevice *dev, uint channel,
bool enable)
{
struct sandbox_pwm_priv *priv = dev_get_priv(dev);
struct sandbox_pwm_chan *chan;
if (channel >= NUM_CHANNELS)
return -ENOSPC;
chan = &priv->chan[channel];
chan->enable = enable;
return 0;
}
static int sandbox_pwm_set_invert(struct udevice *dev, uint channel,
bool polarity)
{
struct sandbox_pwm_priv *priv = dev_get_priv(dev);
struct sandbox_pwm_chan *chan;
if (channel >= NUM_CHANNELS)
return -ENOSPC;
chan = &priv->chan[channel];
chan->polarity = polarity;
return 0;
}
static const struct pwm_ops sandbox_pwm_ops = {
.set_config = sandbox_pwm_set_config,
.set_enable = sandbox_pwm_set_enable,
.set_invert = sandbox_pwm_set_invert,
};
static const struct udevice_id sandbox_pwm_ids[] = {
{ .compatible = "sandbox,pwm" },
{ }
};
U_BOOT_DRIVER(warm_pwm_sandbox) = {
.name = "pwm_sandbox",
.id = UCLASS_PWM,
.of_match = sandbox_pwm_ids,
.ops = &sandbox_pwm_ops,
.priv_auto_alloc_size = sizeof(struct sandbox_pwm_priv),
};
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017-2018 Vasily Khoruzhick <anarsoul@gmail.com>
*/
#include <common.h>
#include <div64.h>
#include <dm.h>
#include <pwm.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm/arch/pwm.h>
#include <asm/arch/gpio.h>
#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
#define OSC_24MHZ 24000000
struct sunxi_pwm_priv {
struct sunxi_pwm *regs;
bool invert;
u32 prescaler;
};
static const u32 prescaler_table[] = {
120, /* 0000 */
180, /* 0001 */
240, /* 0010 */
360, /* 0011 */
480, /* 0100 */
0, /* 0101 */
0, /* 0110 */
0, /* 0111 */
12000, /* 1000 */
24000, /* 1001 */
36000, /* 1010 */
48000, /* 1011 */
72000, /* 1100 */
0, /* 1101 */
0, /* 1110 */
1, /* 1111 */
};
static int sunxi_pwm_config_pinmux(void)
{
#ifdef CONFIG_MACH_SUN50I
sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM);
#endif
return 0;
}
static int sunxi_pwm_set_invert(struct udevice *dev, uint channel,
bool polarity)
{
struct sunxi_pwm_priv *priv = dev_get_priv(dev);
debug("%s: polarity=%u\n", __func__, polarity);
priv->invert = polarity;
return 0;
}
static int sunxi_pwm_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct sunxi_pwm_priv *priv = dev_get_priv(dev);
struct sunxi_pwm *regs = priv->regs;
int best_prescaler = 0;
u32 v, best_period = 0, duty;
u64 best_scaled_freq = 0;
const u32 nsecs_per_sec = 1000000000U;
debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
for (int prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
prescaler++) {
u32 period = 0;
u64 scaled_freq = 0;
if (!prescaler_table[prescaler])
continue;
scaled_freq = lldiv(OSC_24MHZ, prescaler_table[prescaler]);
period = lldiv(scaled_freq * period_ns, nsecs_per_sec);
if ((period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) &&
best_period < period) {
best_period = period;
best_scaled_freq = scaled_freq;
best_prescaler = prescaler;
}
}
if (best_period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
debug("%s: failed to find prescaler value\n", __func__);
return -EINVAL;
}
duty = lldiv(best_scaled_freq * duty_ns, nsecs_per_sec);
if (priv->prescaler != best_prescaler) {
/* Mask clock to update prescaler */
v = readl(&regs->ctrl);
v &= ~SUNXI_PWM_CTRL_CLK_GATE;
writel(v, &regs->ctrl);
v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK;
v |= (best_prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
writel(v, &regs->ctrl);
v |= SUNXI_PWM_CTRL_CLK_GATE;
writel(v, &regs->ctrl);
priv->prescaler = best_prescaler;
}
writel(SUNXI_PWM_CH0_PERIOD_PRD(best_period) |
SUNXI_PWM_CH0_PERIOD_DUTY(duty), &regs->ch0_period);
debug("%s: prescaler: %d, period: %d, duty: %d\n",
__func__, priv->prescaler,
best_period, duty);
return 0;
}
static int sunxi_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct sunxi_pwm_priv *priv = dev_get_priv(dev);
struct sunxi_pwm *regs = priv->regs;
u32 v;
debug("%s: Enable '%s'\n", __func__, dev->name);
v = readl(&regs->ctrl);
if (!enable) {
v &= ~SUNXI_PWM_CTRL_ENABLE0;
writel(v, &regs->ctrl);
return 0;
}
sunxi_pwm_config_pinmux();
if (priv->invert)
v &= ~SUNXI_PWM_CTRL_CH0_ACT_STA;
else
v |= SUNXI_PWM_CTRL_CH0_ACT_STA;
v |= SUNXI_PWM_CTRL_ENABLE0;
writel(v, &regs->ctrl);
return 0;
}
static int sunxi_pwm_ofdata_to_platdata(struct udevice *dev)
{
struct sunxi_pwm_priv *priv = dev_get_priv(dev);
priv->regs = (struct sunxi_pwm *)devfdt_get_addr(dev);
return 0;
}
static int sunxi_pwm_probe(struct udevice *dev)
{
return 0;
}
static const struct pwm_ops sunxi_pwm_ops = {
.set_invert = sunxi_pwm_set_invert,
.set_config = sunxi_pwm_set_config,
.set_enable = sunxi_pwm_set_enable,
};
static const struct udevice_id sunxi_pwm_ids[] = {
{ .compatible = "allwinner,sun5i-a13-pwm" },
{ .compatible = "allwinner,sun50i-a64-pwm" },
{ }
};
U_BOOT_DRIVER(sunxi_pwm) = {
.name = "sunxi_pwm",
.id = UCLASS_PWM,
.of_match = sunxi_pwm_ids,
.ops = &sunxi_pwm_ops,
.ofdata_to_platdata = sunxi_pwm_ofdata_to_platdata,
.probe = sunxi_pwm_probe,
.priv_auto_alloc_size = sizeof(struct sunxi_pwm_priv),
};
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2016 Google Inc.
*/
#include <common.h>
#include <dm.h>
#include <pwm.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/pwm.h>
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, &regs[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(&regs[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_read_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),
};