0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/clk.h>
0013 #include <linux/err.h>
0014 #include <linux/io.h>
0015 #include <linux/ioport.h>
0016 #include <linux/kernel.h>
0017 #include <linux/math64.h>
0018 #include <linux/module.h>
0019 #include <linux/of.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/pwm.h>
0022 #include <linux/slab.h>
0023 #include <linux/types.h>
0024
0025 #define NUM_PWM 4
0026
0027
0028 #define PWMCR 0x00
0029 #define PWMCR_PWM_ENABLE 0x1
0030 #define PWMCR_PRESCALE_SHIFT 2
0031 #define PWMCR_MIN_PRESCALE 0x00
0032 #define PWMCR_MAX_PRESCALE 0x3FFF
0033
0034 #define PWMDCR 0x04
0035 #define PWMDCR_MIN_DUTY 0x0001
0036 #define PWMDCR_MAX_DUTY 0xFFFF
0037
0038 #define PWMPCR 0x08
0039 #define PWMPCR_MIN_PERIOD 0x0001
0040 #define PWMPCR_MAX_PERIOD 0xFFFF
0041
0042
0043 #define PWMMCR 0x3C
0044 #define PWMMCR_PWM_ENABLE 0x1
0045
0046
0047
0048
0049
0050
0051
0052
0053 struct spear_pwm_chip {
0054 void __iomem *mmio_base;
0055 struct clk *clk;
0056 struct pwm_chip chip;
0057 };
0058
0059 static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip)
0060 {
0061 return container_of(chip, struct spear_pwm_chip, chip);
0062 }
0063
0064 static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num,
0065 unsigned long offset)
0066 {
0067 return readl_relaxed(chip->mmio_base + (num << 4) + offset);
0068 }
0069
0070 static inline void spear_pwm_writel(struct spear_pwm_chip *chip,
0071 unsigned int num, unsigned long offset,
0072 unsigned long val)
0073 {
0074 writel_relaxed(val, chip->mmio_base + (num << 4) + offset);
0075 }
0076
0077 static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
0078 u64 duty_ns, u64 period_ns)
0079 {
0080 struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0081 u64 val, div, clk_rate;
0082 unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc;
0083 int ret;
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095 clk_rate = clk_get_rate(pc->clk);
0096 while (1) {
0097 div = 1000000000;
0098 div *= 1 + prescale;
0099 val = clk_rate * period_ns;
0100 pv = div64_u64(val, div);
0101 val = clk_rate * duty_ns;
0102 dc = div64_u64(val, div);
0103
0104
0105 if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY)
0106 return -EINVAL;
0107
0108
0109
0110
0111
0112 if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) {
0113 if (++prescale > PWMCR_MAX_PRESCALE)
0114 return -EINVAL;
0115 continue;
0116 }
0117 break;
0118 }
0119
0120
0121
0122
0123
0124 ret = clk_enable(pc->clk);
0125 if (ret)
0126 return ret;
0127
0128 spear_pwm_writel(pc, pwm->hwpwm, PWMCR,
0129 prescale << PWMCR_PRESCALE_SHIFT);
0130 spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc);
0131 spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv);
0132 clk_disable(pc->clk);
0133
0134 return 0;
0135 }
0136
0137 static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
0138 {
0139 struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0140 int rc = 0;
0141 u32 val;
0142
0143 rc = clk_enable(pc->clk);
0144 if (rc)
0145 return rc;
0146
0147 val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
0148 val |= PWMCR_PWM_ENABLE;
0149 spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
0150
0151 return 0;
0152 }
0153
0154 static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
0155 {
0156 struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
0157 u32 val;
0158
0159 val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
0160 val &= ~PWMCR_PWM_ENABLE;
0161 spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
0162
0163 clk_disable(pc->clk);
0164 }
0165
0166 static int spear_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0167 const struct pwm_state *state)
0168 {
0169 int err;
0170
0171 if (state->polarity != PWM_POLARITY_NORMAL)
0172 return -EINVAL;
0173
0174 if (!state->enabled) {
0175 if (pwm->state.enabled)
0176 spear_pwm_disable(chip, pwm);
0177 return 0;
0178 }
0179
0180 err = spear_pwm_config(chip, pwm, state->duty_cycle, state->period);
0181 if (err)
0182 return err;
0183
0184 if (!pwm->state.enabled)
0185 return spear_pwm_enable(chip, pwm);
0186
0187 return 0;
0188 }
0189
0190 static const struct pwm_ops spear_pwm_ops = {
0191 .apply = spear_pwm_apply,
0192 .owner = THIS_MODULE,
0193 };
0194
0195 static int spear_pwm_probe(struct platform_device *pdev)
0196 {
0197 struct device_node *np = pdev->dev.of_node;
0198 struct spear_pwm_chip *pc;
0199 int ret;
0200 u32 val;
0201
0202 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
0203 if (!pc)
0204 return -ENOMEM;
0205
0206 pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
0207 if (IS_ERR(pc->mmio_base))
0208 return PTR_ERR(pc->mmio_base);
0209
0210 pc->clk = devm_clk_get(&pdev->dev, NULL);
0211 if (IS_ERR(pc->clk))
0212 return PTR_ERR(pc->clk);
0213
0214 platform_set_drvdata(pdev, pc);
0215
0216 pc->chip.dev = &pdev->dev;
0217 pc->chip.ops = &spear_pwm_ops;
0218 pc->chip.npwm = NUM_PWM;
0219
0220 ret = clk_prepare(pc->clk);
0221 if (ret)
0222 return ret;
0223
0224 if (of_device_is_compatible(np, "st,spear1340-pwm")) {
0225 ret = clk_enable(pc->clk);
0226 if (ret) {
0227 clk_unprepare(pc->clk);
0228 return ret;
0229 }
0230
0231
0232
0233
0234 val = readl_relaxed(pc->mmio_base + PWMMCR);
0235 val |= PWMMCR_PWM_ENABLE;
0236 writel_relaxed(val, pc->mmio_base + PWMMCR);
0237
0238 clk_disable(pc->clk);
0239 }
0240
0241 ret = pwmchip_add(&pc->chip);
0242 if (ret < 0) {
0243 clk_unprepare(pc->clk);
0244 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
0245 }
0246
0247 return ret;
0248 }
0249
0250 static int spear_pwm_remove(struct platform_device *pdev)
0251 {
0252 struct spear_pwm_chip *pc = platform_get_drvdata(pdev);
0253
0254 pwmchip_remove(&pc->chip);
0255
0256
0257 clk_unprepare(pc->clk);
0258
0259 return 0;
0260 }
0261
0262 static const struct of_device_id spear_pwm_of_match[] = {
0263 { .compatible = "st,spear320-pwm" },
0264 { .compatible = "st,spear1340-pwm" },
0265 { }
0266 };
0267
0268 MODULE_DEVICE_TABLE(of, spear_pwm_of_match);
0269
0270 static struct platform_driver spear_pwm_driver = {
0271 .driver = {
0272 .name = "spear-pwm",
0273 .of_match_table = spear_pwm_of_match,
0274 },
0275 .probe = spear_pwm_probe,
0276 .remove = spear_pwm_remove,
0277 };
0278
0279 module_platform_driver(spear_pwm_driver);
0280
0281 MODULE_LICENSE("GPL");
0282 MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
0283 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>");
0284 MODULE_ALIAS("platform:spear-pwm");