1 /* 2 * Copyright (c) 2016 Google, Inc 3 * Written by Simon Glass <sjg@chromium.org> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <clk.h> 10 #include <div64.h> 11 #include <dm.h> 12 #include <dm/pinctrl.h> 13 #include <pwm.h> 14 #include <regmap.h> 15 #include <syscon.h> 16 #include <asm/io.h> 17 #include <asm/arch/pwm.h> 18 #include <power/regulator.h> 19 20 DECLARE_GLOBAL_DATA_PTR; 21 22 struct rockchip_pwm_data { 23 struct rockchip_pwm_regs regs; 24 unsigned int prescaler; 25 bool supports_polarity; 26 bool supports_lock; 27 bool vop_pwm; 28 u32 enable_conf; 29 u32 enable_conf_mask; 30 }; 31 32 struct rk_pwm_priv { 33 fdt_addr_t base; 34 ulong freq; 35 u32 conf_polarity; 36 bool vop_pwm_en; /* indicate voppwm mirror register state */ 37 const struct rockchip_pwm_data *data; 38 }; 39 40 static int rk_pwm_set_invert(struct udevice *dev, uint channel, bool polarity) 41 { 42 struct rk_pwm_priv *priv = dev_get_priv(dev); 43 44 if (!priv->data->supports_polarity) { 45 debug("%s: Do not support polarity\n", __func__); 46 return 0; 47 } 48 49 debug("%s: polarity=%u\n", __func__, polarity); 50 if (polarity) 51 priv->conf_polarity = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE; 52 else 53 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE; 54 55 return 0; 56 } 57 58 static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns, 59 uint duty_ns) 60 { 61 struct rk_pwm_priv *priv = dev_get_priv(dev); 62 const struct rockchip_pwm_regs *regs = &priv->data->regs; 63 unsigned long period, duty; 64 u32 ctrl; 65 66 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns); 67 68 ctrl = readl(priv->base + regs->ctrl); 69 if (priv->data->vop_pwm) { 70 if (priv->vop_pwm_en) 71 ctrl |= RK_PWM_ENABLE; 72 else 73 ctrl &= ~RK_PWM_ENABLE; 74 } 75 76 /* 77 * Lock the period and duty of previous configuration, then 78 * change the duty and period, that would not be effective. 79 */ 80 if (priv->data->supports_lock) { 81 ctrl |= PWM_LOCK; 82 writel(ctrl, priv->base + regs->ctrl); 83 } 84 85 period = lldiv((uint64_t)(priv->freq / 1000) * period_ns, 86 priv->data->prescaler * 1000000); 87 duty = lldiv((uint64_t)(priv->freq / 1000) * duty_ns, 88 priv->data->prescaler * 1000000); 89 90 writel(period, priv->base + regs->period); 91 writel(duty, priv->base + regs->duty); 92 93 if (priv->data->supports_polarity) { 94 ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK); 95 ctrl |= priv->conf_polarity; 96 } 97 98 /* 99 * Unlock and set polarity at the same time, 100 * the configuration of duty, period and polarity 101 * would be effective together at next period. 102 */ 103 if (priv->data->supports_lock) 104 ctrl &= ~PWM_LOCK; 105 writel(ctrl, priv->base + regs->ctrl); 106 107 debug("%s: period=%lu, duty=%lu\n", __func__, period, duty); 108 109 return 0; 110 } 111 112 static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable) 113 { 114 struct rk_pwm_priv *priv = dev_get_priv(dev); 115 const struct rockchip_pwm_regs *regs = &priv->data->regs; 116 u32 ctrl; 117 118 debug("%s: Enable '%s'\n", __func__, dev->name); 119 120 ctrl = readl(priv->base + regs->ctrl); 121 ctrl &= ~priv->data->enable_conf_mask; 122 123 if (enable) 124 ctrl |= priv->data->enable_conf; 125 else 126 ctrl &= ~priv->data->enable_conf; 127 128 writel(ctrl, priv->base + regs->ctrl); 129 if (priv->data->vop_pwm) 130 priv->vop_pwm_en = enable; 131 132 if (enable) 133 pinctrl_select_state(dev, "active"); 134 135 return 0; 136 } 137 138 static int rk_pwm_ofdata_to_platdata(struct udevice *dev) 139 { 140 struct rk_pwm_priv *priv = dev_get_priv(dev); 141 142 priv->base = dev_read_addr(dev); 143 144 return 0; 145 } 146 147 static int rk_pwm_probe(struct udevice *dev) 148 { 149 struct rk_pwm_priv *priv = dev_get_priv(dev); 150 struct clk clk; 151 int ret = 0; 152 153 ret = clk_get_by_index(dev, 0, &clk); 154 if (ret < 0) { 155 debug("%s get clock fail!\n", __func__); 156 return -EINVAL; 157 } 158 159 priv->freq = clk_get_rate(&clk); 160 priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev); 161 162 if (priv->data->supports_polarity) 163 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE; 164 165 return 0; 166 } 167 168 static const struct pwm_ops rk_pwm_ops = { 169 .set_invert = rk_pwm_set_invert, 170 .set_config = rk_pwm_set_config, 171 .set_enable = rk_pwm_set_enable, 172 }; 173 174 static const struct rockchip_pwm_data pwm_data_v1 = { 175 .regs = { 176 .duty = 0x04, 177 .period = 0x08, 178 .cntr = 0x00, 179 .ctrl = 0x0c, 180 }, 181 .prescaler = 2, 182 .supports_polarity = false, 183 .supports_lock = false, 184 .vop_pwm = false, 185 .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, 186 .enable_conf_mask = BIT(1) | BIT(3), 187 }; 188 189 static const struct rockchip_pwm_data pwm_data_v2 = { 190 .regs = { 191 .duty = 0x08, 192 .period = 0x04, 193 .cntr = 0x00, 194 .ctrl = 0x0c, 195 }, 196 .prescaler = 1, 197 .supports_polarity = true, 198 .supports_lock = false, 199 .vop_pwm = false, 200 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE | 201 PWM_CONTINUOUS, 202 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), 203 }; 204 205 static const struct rockchip_pwm_data pwm_data_vop = { 206 .regs = { 207 .duty = 0x08, 208 .period = 0x04, 209 .cntr = 0x0c, 210 .ctrl = 0x00, 211 }, 212 .prescaler = 1, 213 .supports_polarity = true, 214 .supports_lock = false, 215 .vop_pwm = true, 216 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE | 217 PWM_CONTINUOUS, 218 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), 219 }; 220 221 static const struct rockchip_pwm_data pwm_data_v3 = { 222 .regs = { 223 .duty = 0x08, 224 .period = 0x04, 225 .cntr = 0x00, 226 .ctrl = 0x0c, 227 }, 228 .prescaler = 1, 229 .supports_polarity = true, 230 .supports_lock = true, 231 .vop_pwm = false, 232 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE | 233 PWM_CONTINUOUS, 234 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8), 235 }; 236 237 static const struct udevice_id rk_pwm_ids[] = { 238 { .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1}, 239 { .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2}, 240 { .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3}, 241 { .compatible = "rockchip,vop-pwm", .data = (ulong)&pwm_data_vop}, 242 { .compatible = "rockchip,rk3399-pwm", .data = (ulong)&pwm_data_v2}, 243 { } 244 }; 245 246 U_BOOT_DRIVER(rk_pwm) = { 247 .name = "rk_pwm", 248 .id = UCLASS_PWM, 249 .of_match = rk_pwm_ids, 250 .ops = &rk_pwm_ops, 251 .ofdata_to_platdata = rk_pwm_ofdata_to_platdata, 252 .probe = rk_pwm_probe, 253 .priv_auto_alloc_size = sizeof(struct rk_pwm_priv), 254 }; 255