Message ID | 1586932377-2895-1-git-send-email-yash.shah@sifive.com |
---|---|
State | Superseded |
Headers | show |
Series | pwm: Add PWM driver for SiFive SoC | expand |
Hello Yash Shah, Am 15.04.2020 um 08:32 schrieb Yash Shah: > Adds a PWM driver for PWM chip present in SiFive's HiFive Unleashed SoC > This driver is simple port of Linux pwm sifive driver. So please add more information from exatly which linux version this patch is from, see: https://www.denx.de/wiki/U-Boot/Patches#Attributing_Code_Copyrights_Sign > Signed-off-by: Yash Shah <yash.shah at sifive.com> > --- > drivers/pwm/Kconfig | 6 ++ > drivers/pwm/Makefile | 1 + > drivers/pwm/pwm-sifive.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 188 insertions(+) > create mode 100644 drivers/pwm/pwm-sifive.c You introduce a new devicetree node, please add a documentation in doc/device-tree-bindings/pwm/ > > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 1f36fc7..d6ea9ee 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -40,6 +40,12 @@ config PWM_SANDBOX > useful. The PWM can be enabled but is not connected to any outputs > so this is not very useful. > > +config PWM_SIFIVE > + bool "Enable support for SiFive PWM" > + depends on DM_PWM > + help > + This PWM is found SiFive's FU540 and other SoCs. > + > config PWM_TEGRA > bool "Enable support for the Tegra PWM" > depends on DM_PWM > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile > index a837c35..e2d4185 100644 > --- a/drivers/pwm/Makefile > +++ b/drivers/pwm/Makefile > @@ -12,6 +12,7 @@ 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_SIFIVE) += pwm-sifive.o Please keep this list sorted. > obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o > obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o > obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c > new file mode 100644 > index 0000000..c54297e > --- /dev/null > +++ b/drivers/pwm/pwm-sifive.c > @@ -0,0 +1,181 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2020 SiFive, Inc > + * For SiFive's PWM IP block documentation please refer Chapter 14 of > + * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf > + * > + * Limitations: > + * - When changing both duty cycle and period, we cannot prevent in > + * software that the output might produce a period with mixed > + * settings (new period length and old duty cycle). > + * - The hardware cannot generate a 100% duty cycle. > + * - The hardware generates only inverted output. > + */ > + > +#include <common.h> > +#include <clk.h> > +#include <div64.h> > +#include <dm.h> > +#include <pwm.h> > +#include <regmap.h> > +#include <linux/io.h> > +#include <linux/log2.h> > +#include <linux/bitfield.h> > + > +/* PWMCFG fields */ > +#define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0) > +#define PWM_SIFIVE_PWMCFG_STICKY BIT(8) > +#define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9) > +#define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10) > +#define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12) > +#define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13) > +#define PWM_SIFIVE_PWMCFG_CENTER BIT(16) > +#define PWM_SIFIVE_PWMCFG_GANG BIT(24) > +#define PWM_SIFIVE_PWMCFG_IP BIT(28) > + > +/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */ > +#define PWM_SIFIVE_SIZE_PWMCMP 4 > +#define PWM_SIFIVE_CMPWIDTH 16 > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct pwm_sifive_regs { > + unsigned long cfg; > + unsigned long cnt; > + unsigned long pwms; > + unsigned long cmp0; > +}; > + > +struct pwm_sifive_data { > + struct pwm_sifive_regs regs; > +}; > + > +struct pwm_sifive_priv { > + fdt_addr_t base; > + ulong freq; > + const struct pwm_sifive_data *data; > +}; > + > +static int pwm_sifive_set_invert(struct udevice *dev, uint channel, > + bool polarity) > +{ > + debug("%s: Do not support polarity\n", __func__); > + > + return 0; > +} You don;t need this function. > + > +static int pwm_sifive_set_config(struct udevice *dev, uint channel, > + uint period_ns, uint duty_ns) > +{ > + struct pwm_sifive_priv *priv = dev_get_priv(dev); > + const struct pwm_sifive_regs *regs = &priv->data->regs; > + unsigned long scale_pow; > + unsigned long long num; > + u32 scale, val = 0, frac; > + > + debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); > + > + /* > + * The PWM unit is used with pwmzerocmp=0, so the only way to modify the > + * period length is using pwmscale which provides the number of bits the > + * counter is shifted before being feed to the comparators. A period > + * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks. > + * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period > + */ > + scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000); > + scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf); > + val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale); > + > + /* > + * The problem of output producing mixed setting as mentioned at top, > + * occurs here. To minimize the window for this problem, we are > + * calculating the register values first and then writing them > + * consecutively > + */ > + num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH); > + frac = DIV_ROUND_CLOSEST_ULL(num, period_ns); > + frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); > + > + writel(val, (void __iomem *)priv->base + regs->cfg); Do you really need this cast on each readl/writel call? > + writel(frac, (void __iomem *)priv->base + regs->cmp0 + channel * > + PWM_SIFIVE_SIZE_PWMCMP); > + > + return 0; > +} > + > +static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable) > +{ > + struct pwm_sifive_priv *priv = dev_get_priv(dev); > + const struct pwm_sifive_regs *regs = &priv->data->regs; > + u32 val; > + > + debug("%s: Enable '%s'\n", __func__, dev->name); > + > + if (enable) { > + val = readl((void __iomem *)priv->base + regs->cfg); > + val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS; > + writel(val, (void __iomem *)priv->base + regs->cfg); > + } else { > + writel(0, (void __iomem *)priv->base + regs->cmp0 + channel * > + PWM_SIFIVE_SIZE_PWMCMP); > + } > + > + return 0; > +} > + > +static int pwm_sifive_ofdata_to_platdata(struct udevice *dev) > +{ > + struct pwm_sifive_priv *priv = dev_get_priv(dev); > + > + priv->base = dev_read_addr(dev); > + > + return 0; > +} > + > +static int pwm_sifive_probe(struct udevice *dev) > +{ > + struct pwm_sifive_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 pwm_sifive_data *)dev_get_driver_data(dev); > + > + return 0; > +} > + > +static const struct pwm_ops pwm_sifive_ops = { > + .set_invert = pwm_sifive_set_invert, > + .set_config = pwm_sifive_set_config, > + .set_enable = pwm_sifive_set_enable, > +}; > + > +static const struct pwm_sifive_data pwm_data = { > + .regs = { > + .cfg = 0x00, > + .cnt = 0x08, > + .pwms = 0x10, > + .cmp0 = 0x20, > + }, > +}; > + > +static const struct udevice_id pwm_sifive_ids[] = { > + { .compatible = "sifive,pwm0", .data = (ulong)&pwm_data}, > + { } > +}; > + > +U_BOOT_DRIVER(pwm_sifive) = { > + .name = "pwm_sifive", > + .id = UCLASS_PWM, > + .of_match = pwm_sifive_ids, > + .ops = &pwm_sifive_ops, > + .ofdata_to_platdata = pwm_sifive_ofdata_to_platdata, > + .probe = pwm_sifive_probe, > + .priv_auto_alloc_size = sizeof(struct pwm_sifive_priv), > +}; > Thanks! bye, Heiko
> -----Original Message----- > From: Heiko Schocher <hs at denx.de> > Sent: 15 April 2020 22:34 > To: Yash Shah <yash.shah at sifive.com> > Cc: martyn.welch at collabora.co.uk; u-boot at lists.denx.de > Subject: Re: [PATCH] pwm: Add PWM driver for SiFive SoC > > [External Email] Do not click links or attachments unless you recognize the > sender and know the content is safe > > Hello Yash Shah, > > Am 15.04.2020 um 08:32 schrieb Yash Shah: > > Adds a PWM driver for PWM chip present in SiFive's HiFive Unleashed > > SoC This driver is simple port of Linux pwm sifive driver. > > So please add more information from exatly which linux version this patch is > from, see: > > https://www.denx.de/wiki/U- > Boot/Patches#Attributing_Code_Copyrights_Sign Sure, will refer this doc and send a v2 accordingly. > > > Signed-off-by: Yash Shah <yash.shah at sifive.com> > > --- > > drivers/pwm/Kconfig | 6 ++ > > drivers/pwm/Makefile | 1 + > > drivers/pwm/pwm-sifive.c | 181 > +++++++++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 188 insertions(+) > > create mode 100644 drivers/pwm/pwm-sifive.c > > You introduce a new devicetree node, please add a documentation in > doc/device-tree-bindings/pwm/ Will add the dt documentation. > > > > > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index > > 1f36fc7..d6ea9ee 100644 > > --- a/drivers/pwm/Kconfig > > +++ b/drivers/pwm/Kconfig > > @@ -40,6 +40,12 @@ config PWM_SANDBOX > > useful. The PWM can be enabled but is not connected to any outputs > > so this is not very useful. > > > > +config PWM_SIFIVE > > + bool "Enable support for SiFive PWM" > > + depends on DM_PWM > > + help > > + This PWM is found SiFive's FU540 and other SoCs. > > + > > config PWM_TEGRA > > bool "Enable support for the Tegra PWM" > > depends on DM_PWM > > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index > > a837c35..e2d4185 100644 > > --- a/drivers/pwm/Makefile > > +++ b/drivers/pwm/Makefile > > @@ -12,6 +12,7 @@ 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_SIFIVE) += pwm-sifive.o > > Please keep this list sorted. Sure. My bad. > > > obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o > > obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o > > obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o > > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c new > > file mode 100644 index 0000000..c54297e > > --- /dev/null > > +++ b/drivers/pwm/pwm-sifive.c > > @@ -0,0 +1,181 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * Copyright (C) 2020 SiFive, Inc > > + * For SiFive's PWM IP block documentation please refer Chapter 14 of > > + * Reference Manual : > > +https://static.dev.sifive.com/FU540-C000-v1.0.pdf > > + * > > + * Limitations: > > + * - When changing both duty cycle and period, we cannot prevent in > > + * software that the output might produce a period with mixed > > + * settings (new period length and old duty cycle). > > + * - The hardware cannot generate a 100% duty cycle. > > + * - The hardware generates only inverted output. > > + */ > > + > > +#include <common.h> > > +#include <clk.h> > > +#include <div64.h> > > +#include <dm.h> > > +#include <pwm.h> > > +#include <regmap.h> > > +#include <linux/io.h> > > +#include <linux/log2.h> > > +#include <linux/bitfield.h> > > + > > +/* PWMCFG fields */ > > +#define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0) > > +#define PWM_SIFIVE_PWMCFG_STICKY BIT(8) > > +#define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9) > > +#define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10) > > +#define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12) > > +#define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13) > > +#define PWM_SIFIVE_PWMCFG_CENTER BIT(16) > > +#define PWM_SIFIVE_PWMCFG_GANG BIT(24) > > +#define PWM_SIFIVE_PWMCFG_IP BIT(28) > > + > > +/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX > registers */ > > +#define PWM_SIFIVE_SIZE_PWMCMP 4 > > +#define PWM_SIFIVE_CMPWIDTH 16 > > + > > +DECLARE_GLOBAL_DATA_PTR; > > + > > +struct pwm_sifive_regs { > > + unsigned long cfg; > > + unsigned long cnt; > > + unsigned long pwms; > > + unsigned long cmp0; > > +}; > > + > > +struct pwm_sifive_data { > > + struct pwm_sifive_regs regs; > > +}; > > + > > +struct pwm_sifive_priv { > > + fdt_addr_t base; > > + ulong freq; > > + const struct pwm_sifive_data *data; }; > > + > > +static int pwm_sifive_set_invert(struct udevice *dev, uint channel, > > + bool polarity) { > > + debug("%s: Do not support polarity\n", __func__); > > + > > + return 0; > > +} > > You don;t need this function. > > > + > > +static int pwm_sifive_set_config(struct udevice *dev, uint channel, > > + uint period_ns, uint duty_ns) { > > + struct pwm_sifive_priv *priv = dev_get_priv(dev); > > + const struct pwm_sifive_regs *regs = &priv->data->regs; > > + unsigned long scale_pow; > > + unsigned long long num; > > + u32 scale, val = 0, frac; > > + > > + debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, > > + duty_ns); > > + > > + /* > > + * The PWM unit is used with pwmzerocmp=0, so the only way to > modify the > > + * period length is using pwmscale which provides the number of bits > the > > + * counter is shifted before being feed to the comparators. A period > > + * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks. > > + * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period > > + */ > > + scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000); > > + scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf); > > + val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale); > > + > > + /* > > + * The problem of output producing mixed setting as mentioned at top, > > + * occurs here. To minimize the window for this problem, we are > > + * calculating the register values first and then writing them > > + * consecutively > > + */ > > + num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH); > > + frac = DIV_ROUND_CLOSEST_ULL(num, period_ns); > > + frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); > > + > > + writel(val, (void __iomem *)priv->base + regs->cfg); > > Do you really need this cast on each readl/writel call? Will fix this in v2. Thanks for your comments. - Yash
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 1f36fc7..d6ea9ee 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -40,6 +40,12 @@ config PWM_SANDBOX useful. The PWM can be enabled but is not connected to any outputs so this is not very useful. +config PWM_SIFIVE + bool "Enable support for SiFive PWM" + depends on DM_PWM + help + This PWM is found SiFive's FU540 and other SoCs. + config PWM_TEGRA bool "Enable support for the Tegra PWM" depends on DM_PWM diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..e2d4185 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,6 +12,7 @@ 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_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c new file mode 100644 index 0000000..c54297e --- /dev/null +++ b/drivers/pwm/pwm-sifive.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 SiFive, Inc + * For SiFive's PWM IP block documentation please refer Chapter 14 of + * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf + * + * Limitations: + * - When changing both duty cycle and period, we cannot prevent in + * software that the output might produce a period with mixed + * settings (new period length and old duty cycle). + * - The hardware cannot generate a 100% duty cycle. + * - The hardware generates only inverted output. + */ + +#include <common.h> +#include <clk.h> +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <regmap.h> +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/bitfield.h> + +/* PWMCFG fields */ +#define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0) +#define PWM_SIFIVE_PWMCFG_STICKY BIT(8) +#define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9) +#define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10) +#define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12) +#define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13) +#define PWM_SIFIVE_PWMCFG_CENTER BIT(16) +#define PWM_SIFIVE_PWMCFG_GANG BIT(24) +#define PWM_SIFIVE_PWMCFG_IP BIT(28) + +/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */ +#define PWM_SIFIVE_SIZE_PWMCMP 4 +#define PWM_SIFIVE_CMPWIDTH 16 + +DECLARE_GLOBAL_DATA_PTR; + +struct pwm_sifive_regs { + unsigned long cfg; + unsigned long cnt; + unsigned long pwms; + unsigned long cmp0; +}; + +struct pwm_sifive_data { + struct pwm_sifive_regs regs; +}; + +struct pwm_sifive_priv { + fdt_addr_t base; + ulong freq; + const struct pwm_sifive_data *data; +}; + +static int pwm_sifive_set_invert(struct udevice *dev, uint channel, + bool polarity) +{ + debug("%s: Do not support polarity\n", __func__); + + return 0; +} + +static int pwm_sifive_set_config(struct udevice *dev, uint channel, + uint period_ns, uint duty_ns) +{ + struct pwm_sifive_priv *priv = dev_get_priv(dev); + const struct pwm_sifive_regs *regs = &priv->data->regs; + unsigned long scale_pow; + unsigned long long num; + u32 scale, val = 0, frac; + + debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); + + /* + * The PWM unit is used with pwmzerocmp=0, so the only way to modify the + * period length is using pwmscale which provides the number of bits the + * counter is shifted before being feed to the comparators. A period + * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks. + * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period + */ + scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000); + scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf); + val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale); + + /* + * The problem of output producing mixed setting as mentioned at top, + * occurs here. To minimize the window for this problem, we are + * calculating the register values first and then writing them + * consecutively + */ + num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH); + frac = DIV_ROUND_CLOSEST_ULL(num, period_ns); + frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); + + writel(val, (void __iomem *)priv->base + regs->cfg); + writel(frac, (void __iomem *)priv->base + regs->cmp0 + channel * + PWM_SIFIVE_SIZE_PWMCMP); + + return 0; +} + +static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct pwm_sifive_priv *priv = dev_get_priv(dev); + const struct pwm_sifive_regs *regs = &priv->data->regs; + u32 val; + + debug("%s: Enable '%s'\n", __func__, dev->name); + + if (enable) { + val = readl((void __iomem *)priv->base + regs->cfg); + val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS; + writel(val, (void __iomem *)priv->base + regs->cfg); + } else { + writel(0, (void __iomem *)priv->base + regs->cmp0 + channel * + PWM_SIFIVE_SIZE_PWMCMP); + } + + return 0; +} + +static int pwm_sifive_ofdata_to_platdata(struct udevice *dev) +{ + struct pwm_sifive_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + + return 0; +} + +static int pwm_sifive_probe(struct udevice *dev) +{ + struct pwm_sifive_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 pwm_sifive_data *)dev_get_driver_data(dev); + + return 0; +} + +static const struct pwm_ops pwm_sifive_ops = { + .set_invert = pwm_sifive_set_invert, + .set_config = pwm_sifive_set_config, + .set_enable = pwm_sifive_set_enable, +}; + +static const struct pwm_sifive_data pwm_data = { + .regs = { + .cfg = 0x00, + .cnt = 0x08, + .pwms = 0x10, + .cmp0 = 0x20, + }, +}; + +static const struct udevice_id pwm_sifive_ids[] = { + { .compatible = "sifive,pwm0", .data = (ulong)&pwm_data}, + { } +}; + +U_BOOT_DRIVER(pwm_sifive) = { + .name = "pwm_sifive", + .id = UCLASS_PWM, + .of_match = pwm_sifive_ids, + .ops = &pwm_sifive_ops, + .ofdata_to_platdata = pwm_sifive_ofdata_to_platdata, + .probe = pwm_sifive_probe, + .priv_auto_alloc_size = sizeof(struct pwm_sifive_priv), +};
Adds a PWM driver for PWM chip present in SiFive's HiFive Unleashed SoC This driver is simple port of Linux pwm sifive driver. Signed-off-by: Yash Shah <yash.shah at sifive.com> --- drivers/pwm/Kconfig | 6 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sifive.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 drivers/pwm/pwm-sifive.c