xref: /rk3399_rockchip-uboot/drivers/pwm/exynos_pwm.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
15c2dd4cdSSimon Glass /*
25c2dd4cdSSimon Glass  * Copyright 2016 Google Inc.
35c2dd4cdSSimon Glass  *
45c2dd4cdSSimon Glass  * SPDX-License-Identifier:     GPL-2.0+
55c2dd4cdSSimon Glass  */
65c2dd4cdSSimon Glass 
75c2dd4cdSSimon Glass #include <common.h>
85c2dd4cdSSimon Glass #include <dm.h>
95c2dd4cdSSimon Glass #include <pwm.h>
105c2dd4cdSSimon Glass #include <asm/io.h>
115c2dd4cdSSimon Glass #include <asm/arch/clk.h>
125c2dd4cdSSimon Glass #include <asm/arch/clock.h>
135c2dd4cdSSimon Glass #include <asm/arch/pwm.h>
145c2dd4cdSSimon Glass 
155c2dd4cdSSimon Glass DECLARE_GLOBAL_DATA_PTR;
165c2dd4cdSSimon Glass 
175c2dd4cdSSimon Glass struct exynos_pwm_priv {
185c2dd4cdSSimon Glass 	struct s5p_timer *regs;
195c2dd4cdSSimon Glass };
205c2dd4cdSSimon Glass 
exynos_pwm_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)215c2dd4cdSSimon Glass static int exynos_pwm_set_config(struct udevice *dev, uint channel,
225c2dd4cdSSimon Glass 				uint period_ns, uint duty_ns)
235c2dd4cdSSimon Glass {
245c2dd4cdSSimon Glass 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
255c2dd4cdSSimon Glass 	struct s5p_timer *regs = priv->regs;
265c2dd4cdSSimon Glass 	unsigned int offset, prescaler;
275c2dd4cdSSimon Glass 	uint div = 4, rate, rate_ns;
285c2dd4cdSSimon Glass 	u32 val;
295c2dd4cdSSimon Glass 	u32 tcnt, tcmp, tcon;
305c2dd4cdSSimon Glass 
315c2dd4cdSSimon Glass 	if (channel >= 5)
325c2dd4cdSSimon Glass 		return -EINVAL;
335c2dd4cdSSimon Glass 	debug("%s: Configure '%s' channel %u, period_ns %u, duty_ns %u\n",
345c2dd4cdSSimon Glass 	      __func__, dev->name, channel, period_ns, duty_ns);
355c2dd4cdSSimon Glass 
365c2dd4cdSSimon Glass 	val = readl(&regs->tcfg0);
375c2dd4cdSSimon Glass 	prescaler = (channel < 2 ? val : (val >> 8)) & 0xff;
385c2dd4cdSSimon Glass 	div = (readl(&regs->tcfg1) >> MUX_DIV_SHIFT(channel)) & 0xf;
395c2dd4cdSSimon Glass 
405c2dd4cdSSimon Glass 	rate = get_pwm_clk() / ((prescaler + 1) * (1 << div));
415c2dd4cdSSimon Glass 	debug("%s: pwm_clk %lu, rate %u\n", __func__, get_pwm_clk(), rate);
425c2dd4cdSSimon Glass 
435c2dd4cdSSimon Glass 	if (channel < 4) {
445c2dd4cdSSimon Glass 		rate_ns = 1000000000 / rate;
455c2dd4cdSSimon Glass 		tcnt = period_ns / rate_ns;
465c2dd4cdSSimon Glass 		tcmp = duty_ns / rate_ns;
475c2dd4cdSSimon Glass 		debug("%s: tcnt %u, tcmp %u\n", __func__, tcnt, tcmp);
485c2dd4cdSSimon Glass 		offset = channel * 3;
495c2dd4cdSSimon Glass 		writel(tcnt, &regs->tcntb0 + offset);
505c2dd4cdSSimon Glass 		writel(tcmp, &regs->tcmpb0 + offset);
515c2dd4cdSSimon Glass 	}
525c2dd4cdSSimon Glass 
535c2dd4cdSSimon Glass 	tcon = readl(&regs->tcon);
545c2dd4cdSSimon Glass 	tcon |= TCON_UPDATE(channel);
555c2dd4cdSSimon Glass 	if (channel < 4)
565c2dd4cdSSimon Glass 		tcon |= TCON_AUTO_RELOAD(channel);
575c2dd4cdSSimon Glass 	else
585c2dd4cdSSimon Glass 		tcon |= TCON4_AUTO_RELOAD;
595c2dd4cdSSimon Glass 	writel(tcon, &regs->tcon);
605c2dd4cdSSimon Glass 
615c2dd4cdSSimon Glass 	tcon &= ~TCON_UPDATE(channel);
625c2dd4cdSSimon Glass 	writel(tcon, &regs->tcon);
635c2dd4cdSSimon Glass 
645c2dd4cdSSimon Glass 	return 0;
655c2dd4cdSSimon Glass }
665c2dd4cdSSimon Glass 
exynos_pwm_set_enable(struct udevice * dev,uint channel,bool enable)675c2dd4cdSSimon Glass static int exynos_pwm_set_enable(struct udevice *dev, uint channel,
685c2dd4cdSSimon Glass 				 bool enable)
695c2dd4cdSSimon Glass {
705c2dd4cdSSimon Glass 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
715c2dd4cdSSimon Glass 	struct s5p_timer *regs = priv->regs;
725c2dd4cdSSimon Glass 	u32 mask;
735c2dd4cdSSimon Glass 
745c2dd4cdSSimon Glass 	if (channel >= 4)
755c2dd4cdSSimon Glass 		return -EINVAL;
765c2dd4cdSSimon Glass 	debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel);
775c2dd4cdSSimon Glass 	mask = TCON_START(channel);
785c2dd4cdSSimon Glass 	clrsetbits_le32(&regs->tcon, mask, enable ? mask : 0);
795c2dd4cdSSimon Glass 
805c2dd4cdSSimon Glass 	return 0;
815c2dd4cdSSimon Glass }
825c2dd4cdSSimon Glass 
exynos_pwm_probe(struct udevice * dev)835c2dd4cdSSimon Glass static int exynos_pwm_probe(struct udevice *dev)
845c2dd4cdSSimon Glass {
855c2dd4cdSSimon Glass 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
865c2dd4cdSSimon Glass 	struct s5p_timer *regs = priv->regs;
875c2dd4cdSSimon Glass 
885c2dd4cdSSimon Glass 	writel(PRESCALER_0 | PRESCALER_1 << 8, &regs->tcfg0);
895c2dd4cdSSimon Glass 
905c2dd4cdSSimon Glass 	return 0;
915c2dd4cdSSimon Glass }
925c2dd4cdSSimon Glass 
exynos_pwm_ofdata_to_platdata(struct udevice * dev)935c2dd4cdSSimon Glass static int exynos_pwm_ofdata_to_platdata(struct udevice *dev)
945c2dd4cdSSimon Glass {
955c2dd4cdSSimon Glass 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
965c2dd4cdSSimon Glass 
97*a821c4afSSimon Glass 	priv->regs = (struct s5p_timer *)devfdt_get_addr(dev);
985c2dd4cdSSimon Glass 
995c2dd4cdSSimon Glass 	return 0;
1005c2dd4cdSSimon Glass }
1015c2dd4cdSSimon Glass 
1025c2dd4cdSSimon Glass static const struct pwm_ops exynos_pwm_ops = {
1035c2dd4cdSSimon Glass 	.set_config	= exynos_pwm_set_config,
1045c2dd4cdSSimon Glass 	.set_enable	= exynos_pwm_set_enable,
1055c2dd4cdSSimon Glass };
1065c2dd4cdSSimon Glass 
1075c2dd4cdSSimon Glass static const struct udevice_id exynos_channels[] = {
1085c2dd4cdSSimon Glass 	{ .compatible = "samsung,exynos4210-pwm" },
1095c2dd4cdSSimon Glass 	{ }
1105c2dd4cdSSimon Glass };
1115c2dd4cdSSimon Glass 
1125c2dd4cdSSimon Glass U_BOOT_DRIVER(exynos_pwm) = {
1135c2dd4cdSSimon Glass 	.name	= "exynos_pwm",
1145c2dd4cdSSimon Glass 	.id	= UCLASS_PWM,
1155c2dd4cdSSimon Glass 	.of_match = exynos_channels,
1165c2dd4cdSSimon Glass 	.ops	= &exynos_pwm_ops,
1175c2dd4cdSSimon Glass 	.probe	= exynos_pwm_probe,
1185c2dd4cdSSimon Glass 	.ofdata_to_platdata	= exynos_pwm_ofdata_to_platdata,
1195c2dd4cdSSimon Glass 	.priv_auto_alloc_size	= sizeof(struct exynos_pwm_priv),
1205c2dd4cdSSimon Glass };
121