1 /* 2 * PWM driver for Rockchip SoCs 3 * 4 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> 5 * Copyright (C) 2014 ROCKCHIP, Inc. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 */ 11 12 #include <linux/clk.h> 13 #include <linux/io.h> 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/of_device.h> 17 #include <linux/platform_device.h> 18 #include <linux/pwm.h> 19 #include <linux/time.h> 20 21 #define PWM_CTRL_TIMER_EN (1 << 0) 22 #define PWM_CTRL_OUTPUT_EN (1 << 3) 23 24 #define PWM_ENABLE (1 << 0) 25 #define PWM_CONTINUOUS (1 << 1) 26 #define PWM_DUTY_POSITIVE (1 << 3) 27 #define PWM_INACTIVE_NEGATIVE (0 << 4) 28 #define PWM_OUTPUT_LEFT (0 << 5) 29 #define PWM_LP_DISABLE (0 << 8) 30 31 struct rockchip_pwm_chip { 32 struct pwm_chip chip; 33 struct clk *clk; 34 const struct rockchip_pwm_data *data; 35 void __iomem *base; 36 }; 37 38 struct rockchip_pwm_regs { 39 unsigned long duty; 40 unsigned long period; 41 unsigned long cntr; 42 unsigned long ctrl; 43 }; 44 45 struct rockchip_pwm_data { 46 struct rockchip_pwm_regs regs; 47 unsigned int prescaler; 48 49 void (*set_enable)(struct pwm_chip *chip, bool enable); 50 }; 51 52 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) 53 { 54 return container_of(c, struct rockchip_pwm_chip, chip); 55 } 56 57 static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) 58 { 59 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 60 u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; 61 u32 val; 62 63 val = readl_relaxed(pc->base + pc->data->regs.ctrl); 64 65 if (enable) 66 val |= enable_conf; 67 else 68 val &= ~enable_conf; 69 70 writel_relaxed(val, pc->base + pc->data->regs.ctrl); 71 } 72 73 static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) 74 { 75 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 76 u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | 77 PWM_CONTINUOUS | PWM_DUTY_POSITIVE | 78 PWM_INACTIVE_NEGATIVE; 79 u32 val; 80 81 val = readl_relaxed(pc->base + pc->data->regs.ctrl); 82 83 if (enable) 84 val |= enable_conf; 85 else 86 val &= ~enable_conf; 87 88 writel_relaxed(val, pc->base + pc->data->regs.ctrl); 89 } 90 91 static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 92 int duty_ns, int period_ns) 93 { 94 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 95 unsigned long period, duty; 96 u64 clk_rate, div; 97 int ret; 98 99 clk_rate = clk_get_rate(pc->clk); 100 101 /* 102 * Since period and duty cycle registers have a width of 32 103 * bits, every possible input period can be obtained using the 104 * default prescaler value for all practical clock rate values. 105 */ 106 div = clk_rate * period_ns; 107 do_div(div, pc->data->prescaler * NSEC_PER_SEC); 108 period = div; 109 110 div = clk_rate * duty_ns; 111 do_div(div, pc->data->prescaler * NSEC_PER_SEC); 112 duty = div; 113 114 ret = clk_enable(pc->clk); 115 if (ret) 116 return ret; 117 118 writel(period, pc->base + pc->data->regs.period); 119 writel(duty, pc->base + pc->data->regs.duty); 120 writel(0, pc->base + pc->data->regs.cntr); 121 122 clk_disable(pc->clk); 123 124 return 0; 125 } 126 127 static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 128 { 129 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 130 int ret; 131 132 ret = clk_enable(pc->clk); 133 if (ret) 134 return ret; 135 136 pc->data->set_enable(chip, true); 137 138 return 0; 139 } 140 141 static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 142 { 143 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 144 145 pc->data->set_enable(chip, false); 146 147 clk_disable(pc->clk); 148 } 149 150 static const struct pwm_ops rockchip_pwm_ops = { 151 .config = rockchip_pwm_config, 152 .enable = rockchip_pwm_enable, 153 .disable = rockchip_pwm_disable, 154 .owner = THIS_MODULE, 155 }; 156 157 static const struct rockchip_pwm_data pwm_data_v1 = { 158 .regs = { 159 .duty = 0x04, 160 .period = 0x08, 161 .cntr = 0x00, 162 .ctrl = 0x0c, 163 }, 164 .prescaler = 2, 165 .set_enable = rockchip_pwm_set_enable_v1, 166 }; 167 168 static const struct rockchip_pwm_data pwm_data_v2 = { 169 .regs = { 170 .duty = 0x08, 171 .period = 0x04, 172 .cntr = 0x00, 173 .ctrl = 0x0c, 174 }, 175 .prescaler = 1, 176 .set_enable = rockchip_pwm_set_enable_v2, 177 }; 178 179 static const struct rockchip_pwm_data pwm_data_vop = { 180 .regs = { 181 .duty = 0x08, 182 .period = 0x04, 183 .cntr = 0x0c, 184 .ctrl = 0x00, 185 }, 186 .prescaler = 1, 187 .set_enable = rockchip_pwm_set_enable_v2, 188 }; 189 190 static const struct of_device_id rockchip_pwm_dt_ids[] = { 191 { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, 192 { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, 193 { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, 194 { /* sentinel */ } 195 }; 196 MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); 197 198 static int rockchip_pwm_probe(struct platform_device *pdev) 199 { 200 const struct of_device_id *id; 201 struct rockchip_pwm_chip *pc; 202 struct resource *r; 203 int ret; 204 205 id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); 206 if (!id) 207 return -EINVAL; 208 209 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); 210 if (!pc) 211 return -ENOMEM; 212 213 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 214 pc->base = devm_ioremap_resource(&pdev->dev, r); 215 if (IS_ERR(pc->base)) 216 return PTR_ERR(pc->base); 217 218 pc->clk = devm_clk_get(&pdev->dev, NULL); 219 if (IS_ERR(pc->clk)) 220 return PTR_ERR(pc->clk); 221 222 ret = clk_prepare(pc->clk); 223 if (ret) 224 return ret; 225 226 platform_set_drvdata(pdev, pc); 227 228 pc->data = id->data; 229 pc->chip.dev = &pdev->dev; 230 pc->chip.ops = &rockchip_pwm_ops; 231 pc->chip.base = -1; 232 pc->chip.npwm = 1; 233 234 ret = pwmchip_add(&pc->chip); 235 if (ret < 0) { 236 clk_unprepare(pc->clk); 237 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 238 } 239 240 return ret; 241 } 242 243 static int rockchip_pwm_remove(struct platform_device *pdev) 244 { 245 struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); 246 247 clk_unprepare(pc->clk); 248 249 return pwmchip_remove(&pc->chip); 250 } 251 252 static struct platform_driver rockchip_pwm_driver = { 253 .driver = { 254 .name = "rockchip-pwm", 255 .of_match_table = rockchip_pwm_dt_ids, 256 }, 257 .probe = rockchip_pwm_probe, 258 .remove = rockchip_pwm_remove, 259 }; 260 module_platform_driver(rockchip_pwm_driver); 261 262 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); 263 MODULE_DESCRIPTION("Rockchip SoC PWM driver"); 264 MODULE_LICENSE("GPL v2"); 265