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 23 #include <asm/div64.h> 24 25 #define HAS_SECONDARY_PWM 0x10 26 27 static const struct platform_device_id pwm_id_table[] = { 28 /* PWM has_secondary_pwm? */ 29 { "pxa25x-pwm", 0 }, 30 { "pxa27x-pwm", HAS_SECONDARY_PWM }, 31 { "pxa168-pwm", 0 }, 32 { "pxa910-pwm", 0 }, 33 { }, 34 }; 35 MODULE_DEVICE_TABLE(platform, pwm_id_table); 36 37 /* PWM registers and bits definitions */ 38 #define PWMCR (0x00) 39 #define PWMDCR (0x04) 40 #define PWMPCR (0x08) 41 42 #define PWMCR_SD (1 << 6) 43 #define PWMDCR_FD (1 << 10) 44 45 struct pxa_pwm_chip { 46 struct pwm_chip chip; 47 struct device *dev; 48 49 struct clk *clk; 50 void __iomem *mmio_base; 51 }; 52 53 static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip) 54 { 55 return container_of(chip, struct pxa_pwm_chip, chip); 56 } 57 58 /* 59 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE 60 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE 61 */ 62 static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 63 int duty_ns, int period_ns) 64 { 65 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 66 unsigned long long c; 67 unsigned long period_cycles, prescale, pv, dc; 68 unsigned long offset; 69 int rc; 70 71 offset = pwm->hwpwm ? 0x10 : 0; 72 73 c = clk_get_rate(pc->clk); 74 c = c * period_ns; 75 do_div(c, 1000000000); 76 period_cycles = c; 77 78 if (period_cycles < 1) 79 period_cycles = 1; 80 prescale = (period_cycles - 1) / 1024; 81 pv = period_cycles / (prescale + 1) - 1; 82 83 if (prescale > 63) 84 return -EINVAL; 85 86 if (duty_ns == period_ns) 87 dc = PWMDCR_FD; 88 else 89 dc = (pv + 1) * duty_ns / period_ns; 90 91 /* NOTE: the clock to PWM has to be enabled first 92 * before writing to the registers 93 */ 94 rc = clk_prepare_enable(pc->clk); 95 if (rc < 0) 96 return rc; 97 98 writel(prescale, pc->mmio_base + offset + PWMCR); 99 writel(dc, pc->mmio_base + offset + PWMDCR); 100 writel(pv, pc->mmio_base + offset + PWMPCR); 101 102 clk_disable_unprepare(pc->clk); 103 return 0; 104 } 105 106 static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 107 { 108 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 109 110 return clk_prepare_enable(pc->clk); 111 } 112 113 static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 114 { 115 struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); 116 117 clk_disable_unprepare(pc->clk); 118 } 119 120 static struct pwm_ops pxa_pwm_ops = { 121 .config = pxa_pwm_config, 122 .enable = pxa_pwm_enable, 123 .disable = pxa_pwm_disable, 124 .owner = THIS_MODULE, 125 }; 126 127 static int pwm_probe(struct platform_device *pdev) 128 { 129 const struct platform_device_id *id = platform_get_device_id(pdev); 130 struct pxa_pwm_chip *pwm; 131 struct resource *r; 132 int ret = 0; 133 134 pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); 135 if (pwm == NULL) { 136 dev_err(&pdev->dev, "failed to allocate memory\n"); 137 return -ENOMEM; 138 } 139 140 pwm->clk = devm_clk_get(&pdev->dev, NULL); 141 if (IS_ERR(pwm->clk)) 142 return PTR_ERR(pwm->clk); 143 144 pwm->chip.dev = &pdev->dev; 145 pwm->chip.ops = &pxa_pwm_ops; 146 pwm->chip.base = -1; 147 pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; 148 149 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 150 pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r); 151 if (IS_ERR(pwm->mmio_base)) 152 return PTR_ERR(pwm->mmio_base); 153 154 ret = pwmchip_add(&pwm->chip); 155 if (ret < 0) { 156 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 157 return ret; 158 } 159 160 platform_set_drvdata(pdev, pwm); 161 return 0; 162 } 163 164 static int pwm_remove(struct platform_device *pdev) 165 { 166 struct pxa_pwm_chip *chip; 167 168 chip = platform_get_drvdata(pdev); 169 if (chip == NULL) 170 return -ENODEV; 171 172 return pwmchip_remove(&chip->chip); 173 } 174 175 static struct platform_driver pwm_driver = { 176 .driver = { 177 .name = "pxa25x-pwm", 178 .owner = THIS_MODULE, 179 }, 180 .probe = pwm_probe, 181 .remove = pwm_remove, 182 .id_table = pwm_id_table, 183 }; 184 185 module_platform_driver(pwm_driver); 186 187 MODULE_LICENSE("GPL v2"); 188