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