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/platform_device.h> 20 #include <linux/pwm.h> 21 #include <linux/slab.h> 22 #include <linux/types.h> 23 24 /* PWM registers and bits definitions */ 25 #define PWMCON 0x00 26 #define PWMHDUR 0x04 27 #define PWMLDUR 0x08 28 #define PWMGDUR 0x0c 29 #define PWMWAVENUM 0x28 30 #define PWMDWIDTH 0x2c 31 #define PWMTHRES 0x30 32 33 #define PWM_CLK_DIV_MAX 7 34 35 enum { 36 MTK_CLK_MAIN = 0, 37 MTK_CLK_TOP, 38 MTK_CLK_PWM1, 39 MTK_CLK_PWM2, 40 MTK_CLK_PWM3, 41 MTK_CLK_PWM4, 42 MTK_CLK_PWM5, 43 MTK_CLK_MAX, 44 }; 45 46 static const char * const mtk_pwm_clk_name[] = { 47 "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5" 48 }; 49 50 /** 51 * struct mtk_pwm_chip - struct representing PWM chip 52 * @chip: linux PWM chip representation 53 * @regs: base address of PWM chip 54 * @clks: list of clocks 55 */ 56 struct mtk_pwm_chip { 57 struct pwm_chip chip; 58 void __iomem *regs; 59 struct clk *clks[MTK_CLK_MAX]; 60 }; 61 62 static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) 63 { 64 return container_of(chip, struct mtk_pwm_chip, chip); 65 } 66 67 static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm) 68 { 69 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 70 int ret; 71 72 ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]); 73 if (ret < 0) 74 return ret; 75 76 ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]); 77 if (ret < 0) 78 goto disable_clk_top; 79 80 ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); 81 if (ret < 0) 82 goto disable_clk_main; 83 84 return 0; 85 86 disable_clk_main: 87 clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); 88 disable_clk_top: 89 clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); 90 91 return ret; 92 } 93 94 static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) 95 { 96 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 97 98 clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); 99 clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); 100 clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); 101 } 102 103 static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, 104 unsigned int offset) 105 { 106 return readl(chip->regs + 0x10 + (num * 0x40) + offset); 107 } 108 109 static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, 110 unsigned int num, unsigned int offset, 111 u32 value) 112 { 113 writel(value, chip->regs + 0x10 + (num * 0x40) + offset); 114 } 115 116 static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 117 int duty_ns, int period_ns) 118 { 119 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 120 struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; 121 u32 resolution, clkdiv = 0; 122 int ret; 123 124 ret = mtk_pwm_clk_enable(chip, pwm); 125 if (ret < 0) 126 return ret; 127 128 resolution = NSEC_PER_SEC / clk_get_rate(clk); 129 130 while (period_ns / resolution > 8191) { 131 resolution *= 2; 132 clkdiv++; 133 } 134 135 if (clkdiv > PWM_CLK_DIV_MAX) { 136 mtk_pwm_clk_disable(chip, pwm); 137 dev_err(chip->dev, "period %d not supported\n", period_ns); 138 return -EINVAL; 139 } 140 141 mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); 142 mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); 143 mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); 144 145 mtk_pwm_clk_disable(chip, pwm); 146 147 return 0; 148 } 149 150 static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 151 { 152 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 153 u32 value; 154 int ret; 155 156 ret = mtk_pwm_clk_enable(chip, pwm); 157 if (ret < 0) 158 return ret; 159 160 value = readl(pc->regs); 161 value |= BIT(pwm->hwpwm); 162 writel(value, pc->regs); 163 164 return 0; 165 } 166 167 static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 168 { 169 struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); 170 u32 value; 171 172 value = readl(pc->regs); 173 value &= ~BIT(pwm->hwpwm); 174 writel(value, pc->regs); 175 176 mtk_pwm_clk_disable(chip, pwm); 177 } 178 179 static const struct pwm_ops mtk_pwm_ops = { 180 .config = mtk_pwm_config, 181 .enable = mtk_pwm_enable, 182 .disable = mtk_pwm_disable, 183 .owner = THIS_MODULE, 184 }; 185 186 static int mtk_pwm_probe(struct platform_device *pdev) 187 { 188 struct mtk_pwm_chip *pc; 189 struct resource *res; 190 unsigned int i; 191 int ret; 192 193 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); 194 if (!pc) 195 return -ENOMEM; 196 197 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 198 pc->regs = devm_ioremap_resource(&pdev->dev, res); 199 if (IS_ERR(pc->regs)) 200 return PTR_ERR(pc->regs); 201 202 for (i = 0; i < MTK_CLK_MAX; i++) { 203 pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]); 204 if (IS_ERR(pc->clks[i])) 205 return PTR_ERR(pc->clks[i]); 206 } 207 208 platform_set_drvdata(pdev, pc); 209 210 pc->chip.dev = &pdev->dev; 211 pc->chip.ops = &mtk_pwm_ops; 212 pc->chip.base = -1; 213 pc->chip.npwm = 5; 214 215 ret = pwmchip_add(&pc->chip); 216 if (ret < 0) { 217 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 218 return ret; 219 } 220 221 return 0; 222 } 223 224 static int mtk_pwm_remove(struct platform_device *pdev) 225 { 226 struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); 227 228 return pwmchip_remove(&pc->chip); 229 } 230 231 static const struct of_device_id mtk_pwm_of_match[] = { 232 { .compatible = "mediatek,mt7623-pwm" }, 233 { } 234 }; 235 MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); 236 237 static struct platform_driver mtk_pwm_driver = { 238 .driver = { 239 .name = "mtk-pwm", 240 .of_match_table = mtk_pwm_of_match, 241 }, 242 .probe = mtk_pwm_probe, 243 .remove = mtk_pwm_remove, 244 }; 245 module_platform_driver(mtk_pwm_driver); 246 247 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); 248 MODULE_ALIAS("platform:mtk-pwm"); 249 MODULE_LICENSE("GPL"); 250