1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2017-2018 Vasily Khoruzhick <anarsoul@gmail.com> 4 */ 5 6 #include <common.h> 7 #include <div64.h> 8 #include <dm.h> 9 #include <pwm.h> 10 #include <regmap.h> 11 #include <syscon.h> 12 #include <asm/io.h> 13 #include <asm/arch/pwm.h> 14 #include <asm/arch/gpio.h> 15 #include <power/regulator.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 #define OSC_24MHZ 24000000 20 21 struct sunxi_pwm_priv { 22 struct sunxi_pwm *regs; 23 bool invert; 24 u32 prescaler; 25 }; 26 27 static const u32 prescaler_table[] = { 28 120, /* 0000 */ 29 180, /* 0001 */ 30 240, /* 0010 */ 31 360, /* 0011 */ 32 480, /* 0100 */ 33 0, /* 0101 */ 34 0, /* 0110 */ 35 0, /* 0111 */ 36 12000, /* 1000 */ 37 24000, /* 1001 */ 38 36000, /* 1010 */ 39 48000, /* 1011 */ 40 72000, /* 1100 */ 41 0, /* 1101 */ 42 0, /* 1110 */ 43 1, /* 1111 */ 44 }; 45 46 static int sunxi_pwm_config_pinmux(void) 47 { 48 #ifdef CONFIG_MACH_SUN50I 49 sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM); 50 #endif 51 return 0; 52 } 53 54 static int sunxi_pwm_set_invert(struct udevice *dev, uint channel, 55 bool polarity) 56 { 57 struct sunxi_pwm_priv *priv = dev_get_priv(dev); 58 59 debug("%s: polarity=%u\n", __func__, polarity); 60 priv->invert = polarity; 61 62 return 0; 63 } 64 65 static int sunxi_pwm_set_config(struct udevice *dev, uint channel, 66 uint period_ns, uint duty_ns) 67 { 68 struct sunxi_pwm_priv *priv = dev_get_priv(dev); 69 struct sunxi_pwm *regs = priv->regs; 70 int best_prescaler = 0; 71 u32 v, best_period = 0, duty; 72 u64 best_scaled_freq = 0; 73 const u32 nsecs_per_sec = 1000000000U; 74 75 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); 76 77 for (int prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK; 78 prescaler++) { 79 u32 period = 0; 80 u64 scaled_freq = 0; 81 if (!prescaler_table[prescaler]) 82 continue; 83 scaled_freq = lldiv(OSC_24MHZ, prescaler_table[prescaler]); 84 period = lldiv(scaled_freq * period_ns, nsecs_per_sec); 85 if ((period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) && 86 best_period < period) { 87 best_period = period; 88 best_scaled_freq = scaled_freq; 89 best_prescaler = prescaler; 90 } 91 } 92 93 if (best_period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) { 94 debug("%s: failed to find prescaler value\n", __func__); 95 return -EINVAL; 96 } 97 98 duty = lldiv(best_scaled_freq * duty_ns, nsecs_per_sec); 99 100 if (priv->prescaler != best_prescaler) { 101 /* Mask clock to update prescaler */ 102 v = readl(®s->ctrl); 103 v &= ~SUNXI_PWM_CTRL_CLK_GATE; 104 writel(v, ®s->ctrl); 105 v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK; 106 v |= (best_prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK); 107 writel(v, ®s->ctrl); 108 v |= SUNXI_PWM_CTRL_CLK_GATE; 109 writel(v, ®s->ctrl); 110 priv->prescaler = best_prescaler; 111 } 112 113 writel(SUNXI_PWM_CH0_PERIOD_PRD(best_period) | 114 SUNXI_PWM_CH0_PERIOD_DUTY(duty), ®s->ch0_period); 115 116 debug("%s: prescaler: %d, period: %d, duty: %d\n", 117 __func__, priv->prescaler, 118 best_period, duty); 119 120 return 0; 121 } 122 123 static int sunxi_pwm_set_enable(struct udevice *dev, uint channel, bool enable) 124 { 125 struct sunxi_pwm_priv *priv = dev_get_priv(dev); 126 struct sunxi_pwm *regs = priv->regs; 127 u32 v; 128 129 debug("%s: Enable '%s'\n", __func__, dev->name); 130 131 v = readl(®s->ctrl); 132 if (!enable) { 133 v &= ~SUNXI_PWM_CTRL_ENABLE0; 134 writel(v, ®s->ctrl); 135 return 0; 136 } 137 138 sunxi_pwm_config_pinmux(); 139 140 if (priv->invert) 141 v &= ~SUNXI_PWM_CTRL_CH0_ACT_STA; 142 else 143 v |= SUNXI_PWM_CTRL_CH0_ACT_STA; 144 v |= SUNXI_PWM_CTRL_ENABLE0; 145 writel(v, ®s->ctrl); 146 147 return 0; 148 } 149 150 static int sunxi_pwm_ofdata_to_platdata(struct udevice *dev) 151 { 152 struct sunxi_pwm_priv *priv = dev_get_priv(dev); 153 154 priv->regs = (struct sunxi_pwm *)devfdt_get_addr(dev); 155 156 return 0; 157 } 158 159 static int sunxi_pwm_probe(struct udevice *dev) 160 { 161 return 0; 162 } 163 164 static const struct pwm_ops sunxi_pwm_ops = { 165 .set_invert = sunxi_pwm_set_invert, 166 .set_config = sunxi_pwm_set_config, 167 .set_enable = sunxi_pwm_set_enable, 168 }; 169 170 static const struct udevice_id sunxi_pwm_ids[] = { 171 { .compatible = "allwinner,sun5i-a13-pwm" }, 172 { .compatible = "allwinner,sun50i-a64-pwm" }, 173 { } 174 }; 175 176 U_BOOT_DRIVER(sunxi_pwm) = { 177 .name = "sunxi_pwm", 178 .id = UCLASS_PWM, 179 .of_match = sunxi_pwm_ids, 180 .ops = &sunxi_pwm_ops, 181 .ofdata_to_platdata = sunxi_pwm_ofdata_to_platdata, 182 .probe = sunxi_pwm_probe, 183 .priv_auto_alloc_size = sizeof(struct sunxi_pwm_priv), 184 }; 185