1 /* 2 * Mediatek Pulse Width Modulator driver 3 * 4 * Copyright (C) 2015 John Crispin <blogic@openwrt.org> 5 * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com> 6 * 7 * This file is licensed under the terms of the GNU General Public 8 * License version 2. This program is licensed "as is" without any 9 * warranty of any kind, whether express or implied. 10 */ 11 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/ioport.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/clk.h> 18 #include <linux/of.h> 19 #include <linux/of_device.h> 20 #include <linux/platform_device.h> 21 #include <linux/pwm.h> 22 #include <linux/slab.h> 23 #include <linux/types.h> 24 25 /* PWM registers and bits definitions */ 26 #define PWMCON 0x00 27 #define PWMHDUR 0x04 28 #define PWMLDUR 0x08 29 #define PWMGDUR 0x0c 30 #define PWMWAVENUM 0x28 31 #define PWMDWIDTH 0x2c 32 #define PWM45DWIDTH_FIXUP 0x30 33 #define PWMTHRES 0x30 34 #define PWM45THRES_FIXUP 0x34 35 36 #define PWM_CLK_DIV_MAX 7 37 38 enum { 39 MTK_CLK_MAIN = 0, 40 MTK_CLK_TOP, 41 MTK_CLK_PWM1, 42 MTK_CLK_PWM2, 43 MTK_CLK_PWM3, 44 MTK_CLK_PWM4, 45 MTK_CLK_PWM5, 46 MTK_CLK_PWM6, 47 MTK_CLK_PWM7, 48 MTK_CLK_PWM8, 49 MTK_CLK_MAX, 50 }; 51 52 static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = { 53 "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7", 54 "pwm8" 55 }; 56 57 struct mtk_pwm_platform_data { 58 unsigned int num_pwms; 59 bool pwm45_fixup; 60 bool has_clks; 61 }; 62 63 /** 64 * struct mtk_pwm_chip - struct representing PWM chip 65 * @chip: linux PWM chip representation 66 * @regs: base address of PWM chip 67 * @clks: list of clocks 68 */ 69 struct mtk_pwm_chip { 70 struct pwm_chip chip; 71 void __iomem *regs; 72 struct clk *clks[MTK_CLK_MAX]; 73 const struct mtk_pwm_platform_data *soc; 74 }; 75 76 static const unsigned int mtk_pwm_reg_offset[] = { 77 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220 78 }; 79 80 static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) 81 { 82 return container_of(chip, struct mtk_pwm_chip, chip); 83 } 84 85 static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm) 86 { 87 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 88 int ret; 89 90 if (!pc->soc->has_clks) 91 return 0; 92 93 ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]); 94 if (ret < 0) 95 return ret; 96 97 ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]); 98 if (ret < 0) 99 goto disable_clk_top; 100 101 ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); 102 if (ret < 0) 103 goto disable_clk_main; 104 105 return 0; 106 107 disable_clk_main: 108 clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); 109 disable_clk_top: 110 clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); 111 112 return ret; 113 } 114 115 static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) 116 { 117 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 118 119 if (!pc->soc->has_clks) 120 return; 121 122 clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); 123 clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); 124 clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); 125 } 126 127 static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, 128 unsigned int offset) 129 { 130 return readl(chip->regs + mtk_pwm_reg_offset[num] + offset); 131 } 132 133 static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, 134 unsigned int num, unsigned int offset, 135 u32 value) 136 { 137 writel(value, chip->regs + mtk_pwm_reg_offset[num] + offset); 138 } 139 140 static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 141 int duty_ns, int period_ns) 142 { 143 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 144 struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; 145 u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, 146 reg_thres = PWMTHRES; 147 u64 resolution; 148 int ret; 149 150 ret = mtk_pwm_clk_enable(chip, pwm); 151 if (ret < 0) 152 return ret; 153 154 /* Using resolution in picosecond gets accuracy higher */ 155 resolution = (u64)NSEC_PER_SEC * 1000; 156 do_div(resolution, clk_get_rate(clk)); 157 158 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); 159 while (cnt_period > 8191) { 160 resolution *= 2; 161 clkdiv++; 162 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, 163 resolution); 164 } 165 166 if (clkdiv > PWM_CLK_DIV_MAX) { 167 mtk_pwm_clk_disable(chip, pwm); 168 dev_err(chip->dev, "period %d not supported\n", period_ns); 169 return -EINVAL; 170 } 171 172 if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 173 /* 174 * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 175 * from the other PWMs on MT7623. 176 */ 177 reg_width = PWM45DWIDTH_FIXUP; 178 reg_thres = PWM45THRES_FIXUP; 179 } 180 181 cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); 182 mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); 183 mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period); 184 mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); 185 186 mtk_pwm_clk_disable(chip, pwm); 187 188 return 0; 189 } 190 191 static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 192 { 193 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 194 u32 value; 195 int ret; 196 197 ret = mtk_pwm_clk_enable(chip, pwm); 198 if (ret < 0) 199 return ret; 200 201 value = readl(pc->regs); 202 value |= BIT(pwm->hwpwm); 203 writel(value, pc->regs); 204 205 return 0; 206 } 207 208 static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 209 { 210 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 211 u32 value; 212 213 value = readl(pc->regs); 214 value &= ~BIT(pwm->hwpwm); 215 writel(value, pc->regs); 216 217 mtk_pwm_clk_disable(chip, pwm); 218 } 219 220 static const struct pwm_ops mtk_pwm_ops = { 221 .config = mtk_pwm_config, 222 .enable = mtk_pwm_enable, 223 .disable = mtk_pwm_disable, 224 .owner = THIS_MODULE, 225 }; 226 227 static int mtk_pwm_probe(struct platform_device *pdev) 228 { 229 const struct mtk_pwm_platform_data *data; 230 struct mtk_pwm_chip *pc; 231 struct resource *res; 232 unsigned int i; 233 int ret; 234 235 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); 236 if (!pc) 237 return -ENOMEM; 238 239 data = of_device_get_match_data(&pdev->dev); 240 if (data == NULL) 241 return -EINVAL; 242 pc->soc = data; 243 244 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 245 pc->regs = devm_ioremap_resource(&pdev->dev, res); 246 if (IS_ERR(pc->regs)) 247 return PTR_ERR(pc->regs); 248 249 for (i = 0; i < data->num_pwms + 2 && pc->soc->has_clks; i++) { 250 pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]); 251 if (IS_ERR(pc->clks[i])) { 252 dev_err(&pdev->dev, "clock: %s fail: %ld\n", 253 mtk_pwm_clk_name[i], PTR_ERR(pc->clks[i])); 254 return PTR_ERR(pc->clks[i]); 255 } 256 } 257 258 platform_set_drvdata(pdev, pc); 259 260 pc->chip.dev = &pdev->dev; 261 pc->chip.ops = &mtk_pwm_ops; 262 pc->chip.base = -1; 263 pc->chip.npwm = data->num_pwms; 264 265 ret = pwmchip_add(&pc->chip); 266 if (ret < 0) { 267 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 268 return ret; 269 } 270 271 return 0; 272 } 273 274 static int mtk_pwm_remove(struct platform_device *pdev) 275 { 276 struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); 277 278 return pwmchip_remove(&pc->chip); 279 } 280 281 static const struct mtk_pwm_platform_data mt2712_pwm_data = { 282 .num_pwms = 8, 283 .pwm45_fixup = false, 284 .has_clks = true, 285 }; 286 287 static const struct mtk_pwm_platform_data mt7622_pwm_data = { 288 .num_pwms = 6, 289 .pwm45_fixup = false, 290 .has_clks = true, 291 }; 292 293 static const struct mtk_pwm_platform_data mt7623_pwm_data = { 294 .num_pwms = 5, 295 .pwm45_fixup = true, 296 .has_clks = true, 297 }; 298 299 static const struct mtk_pwm_platform_data mt7628_pwm_data = { 300 .num_pwms = 4, 301 .pwm45_fixup = true, 302 .has_clks = false, 303 }; 304 305 static const struct of_device_id mtk_pwm_of_match[] = { 306 { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data }, 307 { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data }, 308 { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, 309 { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, 310 { }, 311 }; 312 MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); 313 314 static struct platform_driver mtk_pwm_driver = { 315 .driver = { 316 .name = "mtk-pwm", 317 .of_match_table = mtk_pwm_of_match, 318 }, 319 .probe = mtk_pwm_probe, 320 .remove = mtk_pwm_remove, 321 }; 322 module_platform_driver(mtk_pwm_driver); 323 324 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); 325 MODULE_LICENSE("GPL"); 326