xref: /OK3568_Linux_fs/u-boot/drivers/pwm/exynos_pwm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright 2016 Google Inc.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * SPDX-License-Identifier:     GPL-2.0+
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <dm.h>
9*4882a593Smuzhiyun #include <pwm.h>
10*4882a593Smuzhiyun #include <asm/io.h>
11*4882a593Smuzhiyun #include <asm/arch/clk.h>
12*4882a593Smuzhiyun #include <asm/arch/clock.h>
13*4882a593Smuzhiyun #include <asm/arch/pwm.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun struct exynos_pwm_priv {
18*4882a593Smuzhiyun 	struct s5p_timer *regs;
19*4882a593Smuzhiyun };
20*4882a593Smuzhiyun 
exynos_pwm_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)21*4882a593Smuzhiyun static int exynos_pwm_set_config(struct udevice *dev, uint channel,
22*4882a593Smuzhiyun 				uint period_ns, uint duty_ns)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
25*4882a593Smuzhiyun 	struct s5p_timer *regs = priv->regs;
26*4882a593Smuzhiyun 	unsigned int offset, prescaler;
27*4882a593Smuzhiyun 	uint div = 4, rate, rate_ns;
28*4882a593Smuzhiyun 	u32 val;
29*4882a593Smuzhiyun 	u32 tcnt, tcmp, tcon;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	if (channel >= 5)
32*4882a593Smuzhiyun 		return -EINVAL;
33*4882a593Smuzhiyun 	debug("%s: Configure '%s' channel %u, period_ns %u, duty_ns %u\n",
34*4882a593Smuzhiyun 	      __func__, dev->name, channel, period_ns, duty_ns);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	val = readl(&regs->tcfg0);
37*4882a593Smuzhiyun 	prescaler = (channel < 2 ? val : (val >> 8)) & 0xff;
38*4882a593Smuzhiyun 	div = (readl(&regs->tcfg1) >> MUX_DIV_SHIFT(channel)) & 0xf;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	rate = get_pwm_clk() / ((prescaler + 1) * (1 << div));
41*4882a593Smuzhiyun 	debug("%s: pwm_clk %lu, rate %u\n", __func__, get_pwm_clk(), rate);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	if (channel < 4) {
44*4882a593Smuzhiyun 		rate_ns = 1000000000 / rate;
45*4882a593Smuzhiyun 		tcnt = period_ns / rate_ns;
46*4882a593Smuzhiyun 		tcmp = duty_ns / rate_ns;
47*4882a593Smuzhiyun 		debug("%s: tcnt %u, tcmp %u\n", __func__, tcnt, tcmp);
48*4882a593Smuzhiyun 		offset = channel * 3;
49*4882a593Smuzhiyun 		writel(tcnt, &regs->tcntb0 + offset);
50*4882a593Smuzhiyun 		writel(tcmp, &regs->tcmpb0 + offset);
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	tcon = readl(&regs->tcon);
54*4882a593Smuzhiyun 	tcon |= TCON_UPDATE(channel);
55*4882a593Smuzhiyun 	if (channel < 4)
56*4882a593Smuzhiyun 		tcon |= TCON_AUTO_RELOAD(channel);
57*4882a593Smuzhiyun 	else
58*4882a593Smuzhiyun 		tcon |= TCON4_AUTO_RELOAD;
59*4882a593Smuzhiyun 	writel(tcon, &regs->tcon);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	tcon &= ~TCON_UPDATE(channel);
62*4882a593Smuzhiyun 	writel(tcon, &regs->tcon);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	return 0;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
exynos_pwm_set_enable(struct udevice * dev,uint channel,bool enable)67*4882a593Smuzhiyun static int exynos_pwm_set_enable(struct udevice *dev, uint channel,
68*4882a593Smuzhiyun 				 bool enable)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
71*4882a593Smuzhiyun 	struct s5p_timer *regs = priv->regs;
72*4882a593Smuzhiyun 	u32 mask;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	if (channel >= 4)
75*4882a593Smuzhiyun 		return -EINVAL;
76*4882a593Smuzhiyun 	debug("%s: Enable '%s' channel %u\n", __func__, dev->name, channel);
77*4882a593Smuzhiyun 	mask = TCON_START(channel);
78*4882a593Smuzhiyun 	clrsetbits_le32(&regs->tcon, mask, enable ? mask : 0);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	return 0;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
exynos_pwm_probe(struct udevice * dev)83*4882a593Smuzhiyun static int exynos_pwm_probe(struct udevice *dev)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
86*4882a593Smuzhiyun 	struct s5p_timer *regs = priv->regs;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	writel(PRESCALER_0 | PRESCALER_1 << 8, &regs->tcfg0);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	return 0;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
exynos_pwm_ofdata_to_platdata(struct udevice * dev)93*4882a593Smuzhiyun static int exynos_pwm_ofdata_to_platdata(struct udevice *dev)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	struct exynos_pwm_priv *priv = dev_get_priv(dev);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	priv->regs = (struct s5p_timer *)devfdt_get_addr(dev);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	return 0;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun static const struct pwm_ops exynos_pwm_ops = {
103*4882a593Smuzhiyun 	.set_config	= exynos_pwm_set_config,
104*4882a593Smuzhiyun 	.set_enable	= exynos_pwm_set_enable,
105*4882a593Smuzhiyun };
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun static const struct udevice_id exynos_channels[] = {
108*4882a593Smuzhiyun 	{ .compatible = "samsung,exynos4210-pwm" },
109*4882a593Smuzhiyun 	{ }
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun U_BOOT_DRIVER(exynos_pwm) = {
113*4882a593Smuzhiyun 	.name	= "exynos_pwm",
114*4882a593Smuzhiyun 	.id	= UCLASS_PWM,
115*4882a593Smuzhiyun 	.of_match = exynos_channels,
116*4882a593Smuzhiyun 	.ops	= &exynos_pwm_ops,
117*4882a593Smuzhiyun 	.probe	= exynos_pwm_probe,
118*4882a593Smuzhiyun 	.ofdata_to_platdata	= exynos_pwm_ofdata_to_platdata,
119*4882a593Smuzhiyun 	.priv_auto_alloc_size	= sizeof(struct exynos_pwm_priv),
120*4882a593Smuzhiyun };
121