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