1 /* 2 * drivers/pwm/pwm-pxa.c 3 * 4 * simple driver for PWM (Pulse Width Modulator) controller 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * 2008-02-13 initial version 11 * eric miao <eric.miao@marvell.com> 12 */ 13 14 #include <linux/module.h> 15 #include <linux/kernel.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 #include <linux/err.h> 19 #include <linux/clk.h> 20 #include <linux/io.h> 21 #include <linux/pwm.h> 22 #include <linux/of_device.h> 23 24 #include <asm/div64.h> 25 26 #define HAS_SECONDARY_PWM 0x10 27 28 static const struct platform_device_id pwm_id_table[] = { 29 /* PWM has_secondary_pwm? */ 30 { "pxa25x-pwm", 0 }, 31 { "pxa27x-pwm", HAS_SECONDARY_PWM }, 32 { "pxa168-pwm", 0 }, 33 { "pxa910-pwm", 0 }, 34 { }, 35 }; 36 MODULE_DEVICE_TABLE(platform, pwm_id_table); 37 38 /* PWM registers and bits definitions */ 39 #define PWMCR (0x00) 40 #define PWMDCR (0x04) 41 #define PWMPCR (0x08) 42 43 #define PWMCR_SD (1 << 6) 44 #define PWMDCR_FD (1 << 10) 45 46 struct pxa_pwm_chip { 47 struct pwm_chip chip; 48 struct device *dev; 49 50 struct clk *clk; 51 void __iomem *mmio_base; 52 }; 53 54 static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip) 55 { 56 return container_of(chip, struct pxa_pwm_chip, chip); 57 } 58 59 /* 60 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE 61 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE 62 */ 63 static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 64 int duty_ns, int period_ns) 65 { 66 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 67 unsigned long long c; 68 unsigned long period_cycles, prescale, pv, dc; 69 unsigned long offset; 70 int rc; 71 72 offset = pwm->hwpwm ? 0x10 : 0; 73 74 c = clk_get_rate(pc->clk); 75 c = c * period_ns; 76 do_div(c, 1000000000); 77 period_cycles = c; 78 79 if (period_cycles < 1) 80 period_cycles = 1; 81 prescale = (period_cycles - 1) / 1024; 82 pv = period_cycles / (prescale + 1) - 1; 83 84 if (prescale > 63) 85 return -EINVAL; 86 87 if (duty_ns == period_ns) 88 dc = PWMDCR_FD; 89 else 90 dc = (pv + 1) * duty_ns / period_ns; 91 92 /* NOTE: the clock to PWM has to be enabled first 93 * before writing to the registers 94 */ 95 rc = clk_prepare_enable(pc->clk); 96 if (rc < 0) 97 return rc; 98 99 writel(prescale, pc->mmio_base + offset + PWMCR); 100 writel(dc, pc->mmio_base + offset + PWMDCR); 101 writel(pv, pc->mmio_base + offset + PWMPCR); 102 103 clk_disable_unprepare(pc->clk); 104 return 0; 105 } 106 107 static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 108 { 109 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 110 111 return clk_prepare_enable(pc->clk); 112 } 113 114 static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 115 { 116 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 117 118 clk_disable_unprepare(pc->clk); 119 } 120 121 static struct pwm_ops pxa_pwm_ops = { 122 .config = pxa_pwm_config, 123 .enable = pxa_pwm_enable, 124 .disable = pxa_pwm_disable, 125 .owner = THIS_MODULE, 126 }; 127 128 #ifdef CONFIG_OF 129 /* 130 * Device tree users must create one device instance for each PWM channel. 131 * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver 132 * code that this is a single channel pxa25x-pwm. Currently all devices are 133 * supported identically. 134 */ 135 static const struct of_device_id pwm_of_match[] = { 136 { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]}, 137 { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]}, 138 { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]}, 139 { .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0]}, 140 { } 141 }; 142 MODULE_DEVICE_TABLE(of, pwm_of_match); 143 #else 144 #define pwm_of_match NULL 145 #endif 146 147 static const struct platform_device_id *pxa_pwm_get_id_dt(struct device *dev) 148 { 149 const struct of_device_id *id = of_match_device(pwm_of_match, dev); 150 151 return id ? id->data : NULL; 152 } 153 154 static struct pwm_device * 155 pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) 156 { 157 struct pwm_device *pwm; 158 159 pwm = pwm_request_from_chip(pc, 0, NULL); 160 if (IS_ERR(pwm)) 161 return pwm; 162 163 pwm_set_period(pwm, args->args[0]); 164 165 return pwm; 166 } 167 168 static int pwm_probe(struct platform_device *pdev) 169 { 170 const struct platform_device_id *id = platform_get_device_id(pdev); 171 struct pxa_pwm_chip *pwm; 172 struct resource *r; 173 int ret = 0; 174 175 if (IS_ENABLED(CONFIG_OF) && id == NULL) 176 id = pxa_pwm_get_id_dt(&pdev->dev); 177 178 if (id == NULL) 179 return -EINVAL; 180 181 pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); 182 if (pwm == NULL) 183 return -ENOMEM; 184 185 pwm->clk = devm_clk_get(&pdev->dev, NULL); 186 if (IS_ERR(pwm->clk)) 187 return PTR_ERR(pwm->clk); 188 189 pwm->chip.dev = &pdev->dev; 190 pwm->chip.ops = &pxa_pwm_ops; 191 pwm->chip.base = -1; 192 pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; 193 194 if (IS_ENABLED(CONFIG_OF)) { 195 pwm->chip.of_xlate = pxa_pwm_of_xlate; 196 pwm->chip.of_pwm_n_cells = 1; 197 } 198 199 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 200 pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r); 201 if (IS_ERR(pwm->mmio_base)) 202 return PTR_ERR(pwm->mmio_base); 203 204 ret = pwmchip_add(&pwm->chip); 205 if (ret < 0) { 206 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 207 return ret; 208 } 209 210 platform_set_drvdata(pdev, pwm); 211 return 0; 212 } 213 214 static int pwm_remove(struct platform_device *pdev) 215 { 216 struct pxa_pwm_chip *chip; 217 218 chip = platform_get_drvdata(pdev); 219 if (chip == NULL) 220 return -ENODEV; 221 222 return pwmchip_remove(&chip->chip); 223 } 224 225 static struct platform_driver pwm_driver = { 226 .driver = { 227 .name = "pxa25x-pwm", 228 .of_match_table = pwm_of_match, 229 }, 230 .probe = pwm_probe, 231 .remove = pwm_remove, 232 .id_table = pwm_id_table, 233 }; 234 235 module_platform_driver(pwm_driver); 236 237 MODULE_LICENSE("GPL v2"); 238