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