1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * PWM driver for Rockchip SoCs 4 * 5 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> 6 * Copyright (C) 2014 ROCKCHIP, Inc. 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/io.h> 11 #include <linux/limits.h> 12 #include <linux/math64.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/property.h> 17 #include <linux/pwm.h> 18 #include <linux/time.h> 19 20 #define PWM_CTRL_TIMER_EN (1 << 0) 21 #define PWM_CTRL_OUTPUT_EN (1 << 3) 22 23 #define PWM_ENABLE (1 << 0) 24 #define PWM_CONTINUOUS (1 << 1) 25 #define PWM_DUTY_POSITIVE (1 << 3) 26 #define PWM_DUTY_NEGATIVE (0 << 3) 27 #define PWM_INACTIVE_NEGATIVE (0 << 4) 28 #define PWM_INACTIVE_POSITIVE (1 << 4) 29 #define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) 30 #define PWM_OUTPUT_LEFT (0 << 5) 31 #define PWM_LOCK_EN (1 << 6) 32 #define PWM_LP_DISABLE (0 << 8) 33 34 struct rockchip_pwm_chip { 35 struct clk *clk; 36 struct clk *pclk; 37 const struct rockchip_pwm_data *data; 38 void __iomem *base; 39 }; 40 41 struct rockchip_pwm_regs { 42 unsigned long duty; 43 unsigned long period; 44 unsigned long cntr; 45 unsigned long ctrl; 46 }; 47 48 struct rockchip_pwm_data { 49 struct rockchip_pwm_regs regs; 50 unsigned int prescaler; 51 bool supports_polarity; 52 bool supports_lock; 53 u32 enable_conf; 54 }; 55 56 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip) 57 { 58 return pwmchip_get_drvdata(chip); 59 } 60 61 static int rockchip_pwm_get_state(struct pwm_chip *chip, 62 struct pwm_device *pwm, 63 struct pwm_state *state) 64 { 65 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 66 u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; 67 u32 enable_conf = pc->data->enable_conf; 68 unsigned long clk_rate; 69 u64 tmp; 70 u32 val; 71 int ret; 72 73 ret = clk_enable(pc->pclk); 74 if (ret) 75 return ret; 76 77 ret = clk_enable(pc->clk); 78 if (ret) 79 return ret; 80 81 clk_rate = clk_get_rate(pc->clk); 82 83 tmp = readl_relaxed(pc->base + pc->data->regs.period); 84 tmp *= prescaled_ns; 85 state->period = DIV_U64_ROUND_UP(tmp, clk_rate); 86 87 tmp = readl_relaxed(pc->base + pc->data->regs.duty); 88 tmp *= prescaled_ns; 89 state->duty_cycle = DIV_U64_ROUND_UP(tmp, clk_rate); 90 91 val = readl_relaxed(pc->base + pc->data->regs.ctrl); 92 state->enabled = (val & enable_conf) == enable_conf; 93 94 if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE)) 95 state->polarity = PWM_POLARITY_INVERSED; 96 else 97 state->polarity = PWM_POLARITY_NORMAL; 98 99 clk_disable(pc->clk); 100 clk_disable(pc->pclk); 101 102 return 0; 103 } 104 105 static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 106 const struct pwm_state *state) 107 { 108 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 109 u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; 110 u64 clk_rate, tmp; 111 u32 period_ticks, duty_ticks; 112 u32 ctrl; 113 114 clk_rate = clk_get_rate(pc->clk); 115 116 /* 117 * Since period and duty cycle registers have a width of 32 118 * bits, every possible input period can be obtained using the 119 * default prescaler value for all practical clock rate values. 120 */ 121 tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns); 122 if (tmp > U32_MAX) 123 tmp = U32_MAX; 124 period_ticks = tmp; 125 126 tmp = mul_u64_u64_div_u64(clk_rate, state->duty_cycle, prescaled_ns); 127 if (tmp > U32_MAX) 128 tmp = U32_MAX; 129 duty_ticks = tmp; 130 131 /* 132 * Lock the period and duty of previous configuration, then 133 * change the duty and period, that would not be effective. 134 */ 135 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); 136 if (pc->data->supports_lock) { 137 ctrl |= PWM_LOCK_EN; 138 writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); 139 } 140 141 writel(period_ticks, pc->base + pc->data->regs.period); 142 writel(duty_ticks, pc->base + pc->data->regs.duty); 143 144 if (pc->data->supports_polarity) { 145 ctrl &= ~PWM_POLARITY_MASK; 146 if (state->polarity == PWM_POLARITY_INVERSED) 147 ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; 148 else 149 ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; 150 } 151 152 /* 153 * Unlock and set polarity at the same time, 154 * the configuration of duty, period and polarity 155 * would be effective together at next period. 156 */ 157 if (pc->data->supports_lock) 158 ctrl &= ~PWM_LOCK_EN; 159 160 writel(ctrl, pc->base + pc->data->regs.ctrl); 161 } 162 163 static int rockchip_pwm_enable(struct pwm_chip *chip, 164 struct pwm_device *pwm, 165 bool enable) 166 { 167 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 168 u32 enable_conf = pc->data->enable_conf; 169 int ret; 170 u32 val; 171 172 if (enable) { 173 ret = clk_enable(pc->clk); 174 if (ret) 175 return ret; 176 } 177 178 val = readl_relaxed(pc->base + pc->data->regs.ctrl); 179 180 if (enable) 181 val |= enable_conf; 182 else 183 val &= ~enable_conf; 184 185 writel_relaxed(val, pc->base + pc->data->regs.ctrl); 186 187 if (!enable) 188 clk_disable(pc->clk); 189 190 return 0; 191 } 192 193 static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 194 const struct pwm_state *state) 195 { 196 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 197 struct pwm_state curstate; 198 bool enabled; 199 int ret = 0; 200 201 ret = clk_enable(pc->pclk); 202 if (ret) 203 return ret; 204 205 ret = clk_enable(pc->clk); 206 if (ret) 207 return ret; 208 209 pwm_get_state(pwm, &curstate); 210 enabled = curstate.enabled; 211 212 if (state->polarity != curstate.polarity && enabled && 213 !pc->data->supports_lock) { 214 ret = rockchip_pwm_enable(chip, pwm, false); 215 if (ret) 216 goto out; 217 enabled = false; 218 } 219 220 rockchip_pwm_config(chip, pwm, state); 221 if (state->enabled != enabled) { 222 ret = rockchip_pwm_enable(chip, pwm, state->enabled); 223 if (ret) 224 goto out; 225 } 226 227 out: 228 clk_disable(pc->clk); 229 clk_disable(pc->pclk); 230 231 return ret; 232 } 233 234 static const struct pwm_ops rockchip_pwm_ops = { 235 .get_state = rockchip_pwm_get_state, 236 .apply = rockchip_pwm_apply, 237 }; 238 239 static const struct rockchip_pwm_data pwm_data_v1 = { 240 .regs = { 241 .duty = 0x04, 242 .period = 0x08, 243 .cntr = 0x00, 244 .ctrl = 0x0c, 245 }, 246 .prescaler = 2, 247 .supports_polarity = false, 248 .supports_lock = false, 249 .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, 250 }; 251 252 static const struct rockchip_pwm_data pwm_data_v2 = { 253 .regs = { 254 .duty = 0x08, 255 .period = 0x04, 256 .cntr = 0x00, 257 .ctrl = 0x0c, 258 }, 259 .prescaler = 1, 260 .supports_polarity = true, 261 .supports_lock = false, 262 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | 263 PWM_CONTINUOUS, 264 }; 265 266 static const struct rockchip_pwm_data pwm_data_vop = { 267 .regs = { 268 .duty = 0x08, 269 .period = 0x04, 270 .cntr = 0x0c, 271 .ctrl = 0x00, 272 }, 273 .prescaler = 1, 274 .supports_polarity = true, 275 .supports_lock = false, 276 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | 277 PWM_CONTINUOUS, 278 }; 279 280 static const struct rockchip_pwm_data pwm_data_v3 = { 281 .regs = { 282 .duty = 0x08, 283 .period = 0x04, 284 .cntr = 0x00, 285 .ctrl = 0x0c, 286 }, 287 .prescaler = 1, 288 .supports_polarity = true, 289 .supports_lock = true, 290 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | 291 PWM_CONTINUOUS, 292 }; 293 294 static const struct of_device_id rockchip_pwm_dt_ids[] = { 295 { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, 296 { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, 297 { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, 298 { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3}, 299 { /* sentinel */ } 300 }; 301 MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); 302 303 static int rockchip_pwm_probe(struct platform_device *pdev) 304 { 305 struct pwm_chip *chip; 306 struct rockchip_pwm_chip *pc; 307 u32 enable_conf, ctrl; 308 bool enabled; 309 int ret, count; 310 311 chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc)); 312 if (IS_ERR(chip)) 313 return PTR_ERR(chip); 314 pc = to_rockchip_pwm_chip(chip); 315 316 pc->base = devm_platform_ioremap_resource(pdev, 0); 317 if (IS_ERR(pc->base)) 318 return PTR_ERR(pc->base); 319 320 pc->clk = devm_clk_get(&pdev->dev, "pwm"); 321 if (IS_ERR(pc->clk)) { 322 pc->clk = devm_clk_get(&pdev->dev, NULL); 323 if (IS_ERR(pc->clk)) 324 return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), 325 "Can't get PWM clk\n"); 326 } 327 328 count = of_count_phandle_with_args(pdev->dev.of_node, 329 "clocks", "#clock-cells"); 330 if (count == 2) 331 pc->pclk = devm_clk_get(&pdev->dev, "pclk"); 332 else 333 pc->pclk = pc->clk; 334 335 if (IS_ERR(pc->pclk)) 336 return dev_err_probe(&pdev->dev, PTR_ERR(pc->pclk), "Can't get APB clk\n"); 337 338 ret = clk_prepare_enable(pc->clk); 339 if (ret) 340 return dev_err_probe(&pdev->dev, ret, "Can't prepare enable PWM clk\n"); 341 342 ret = clk_prepare_enable(pc->pclk); 343 if (ret) { 344 dev_err_probe(&pdev->dev, ret, "Can't prepare enable APB clk\n"); 345 goto err_clk; 346 } 347 348 platform_set_drvdata(pdev, chip); 349 350 pc->data = device_get_match_data(&pdev->dev); 351 chip->ops = &rockchip_pwm_ops; 352 353 enable_conf = pc->data->enable_conf; 354 ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); 355 enabled = (ctrl & enable_conf) == enable_conf; 356 357 ret = pwmchip_add(chip); 358 if (ret < 0) { 359 dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n"); 360 goto err_pclk; 361 } 362 363 /* Keep the PWM clk enabled if the PWM appears to be up and running. */ 364 if (!enabled) 365 clk_disable(pc->clk); 366 367 clk_disable(pc->pclk); 368 369 return 0; 370 371 err_pclk: 372 clk_disable_unprepare(pc->pclk); 373 err_clk: 374 clk_disable_unprepare(pc->clk); 375 376 return ret; 377 } 378 379 static void rockchip_pwm_remove(struct platform_device *pdev) 380 { 381 struct pwm_chip *chip = platform_get_drvdata(pdev); 382 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 383 384 pwmchip_remove(chip); 385 386 clk_unprepare(pc->pclk); 387 clk_unprepare(pc->clk); 388 } 389 390 static struct platform_driver rockchip_pwm_driver = { 391 .driver = { 392 .name = "rockchip-pwm", 393 .of_match_table = rockchip_pwm_dt_ids, 394 }, 395 .probe = rockchip_pwm_probe, 396 .remove = rockchip_pwm_remove, 397 }; 398 module_platform_driver(rockchip_pwm_driver); 399 400 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); 401 MODULE_DESCRIPTION("Rockchip SoC PWM driver"); 402 MODULE_LICENSE("GPL v2"); 403