0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include <linux/bitfield.h>
0023 #include <linux/clk.h>
0024 #include <linux/io.h>
0025 #include <linux/kernel.h>
0026 #include <linux/module.h>
0027 #include <linux/platform_device.h>
0028 #include <linux/pwm.h>
0029
0030 #define SP7021_PWM_MODE0 0x000
0031 #define SP7021_PWM_MODE0_PWMEN(ch) BIT(ch)
0032 #define SP7021_PWM_MODE0_BYPASS(ch) BIT(8 + (ch))
0033 #define SP7021_PWM_MODE1 0x004
0034 #define SP7021_PWM_MODE1_CNT_EN(ch) BIT(ch)
0035 #define SP7021_PWM_FREQ(ch) (0x008 + 4 * (ch))
0036 #define SP7021_PWM_FREQ_MAX GENMASK(15, 0)
0037 #define SP7021_PWM_DUTY(ch) (0x018 + 4 * (ch))
0038 #define SP7021_PWM_DUTY_DD_SEL(ch) FIELD_PREP(GENMASK(9, 8), ch)
0039 #define SP7021_PWM_DUTY_MAX GENMASK(7, 0)
0040 #define SP7021_PWM_DUTY_MASK SP7021_PWM_DUTY_MAX
0041 #define SP7021_PWM_FREQ_SCALER 256
0042 #define SP7021_PWM_NUM 4
0043
0044 struct sunplus_pwm {
0045 struct pwm_chip chip;
0046 void __iomem *base;
0047 struct clk *clk;
0048 };
0049
0050 static inline struct sunplus_pwm *to_sunplus_pwm(struct pwm_chip *chip)
0051 {
0052 return container_of(chip, struct sunplus_pwm, chip);
0053 }
0054
0055 static int sunplus_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0056 const struct pwm_state *state)
0057 {
0058 struct sunplus_pwm *priv = to_sunplus_pwm(chip);
0059 u32 dd_freq, duty, mode0, mode1;
0060 u64 clk_rate;
0061
0062 if (state->polarity != pwm->state.polarity)
0063 return -EINVAL;
0064
0065 if (!state->enabled) {
0066
0067 mode0 = readl(priv->base + SP7021_PWM_MODE0);
0068 mode0 &= ~SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
0069 writel(mode0, priv->base + SP7021_PWM_MODE0);
0070
0071 mode1 = readl(priv->base + SP7021_PWM_MODE1);
0072 mode1 &= ~SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
0073 writel(mode1, priv->base + SP7021_PWM_MODE1);
0074 return 0;
0075 }
0076
0077 clk_rate = clk_get_rate(priv->clk);
0078
0079
0080
0081
0082
0083
0084 if (clk_rate > (u64)SP7021_PWM_FREQ_SCALER * NSEC_PER_SEC)
0085 return -EINVAL;
0086
0087
0088
0089
0090
0091 dd_freq = mul_u64_u64_div_u64(clk_rate, state->period, (u64)SP7021_PWM_FREQ_SCALER
0092 * NSEC_PER_SEC);
0093
0094 if (dd_freq == 0)
0095 return -EINVAL;
0096
0097 if (dd_freq > SP7021_PWM_FREQ_MAX)
0098 dd_freq = SP7021_PWM_FREQ_MAX;
0099
0100 writel(dd_freq, priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
0101
0102
0103 mode0 = readl(priv->base + SP7021_PWM_MODE0);
0104 mode0 |= SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
0105 mode1 = readl(priv->base + SP7021_PWM_MODE1);
0106 mode1 |= SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
0107 if (state->duty_cycle == state->period) {
0108
0109 mode0 |= SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
0110 duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | SP7021_PWM_DUTY_MAX;
0111 } else {
0112 mode0 &= ~SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
0113
0114
0115
0116 duty = mul_u64_u64_div_u64(state->duty_cycle, clk_rate,
0117 (u64)dd_freq * NSEC_PER_SEC);
0118 duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | duty;
0119 }
0120 writel(duty, priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
0121 writel(mode1, priv->base + SP7021_PWM_MODE1);
0122 writel(mode0, priv->base + SP7021_PWM_MODE0);
0123
0124 return 0;
0125 }
0126
0127 static void sunplus_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
0128 struct pwm_state *state)
0129 {
0130 struct sunplus_pwm *priv = to_sunplus_pwm(chip);
0131 u32 mode0, dd_freq, duty;
0132 u64 clk_rate;
0133
0134 mode0 = readl(priv->base + SP7021_PWM_MODE0);
0135
0136 if (mode0 & BIT(pwm->hwpwm)) {
0137 clk_rate = clk_get_rate(priv->clk);
0138 dd_freq = readl(priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
0139 duty = readl(priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
0140 duty = FIELD_GET(SP7021_PWM_DUTY_MASK, duty);
0141
0142
0143
0144
0145 state->period = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)SP7021_PWM_FREQ_SCALER
0146 * NSEC_PER_SEC, clk_rate);
0147
0148
0149
0150 state->duty_cycle = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)duty * NSEC_PER_SEC,
0151 clk_rate);
0152 state->enabled = true;
0153 } else {
0154 state->enabled = false;
0155 }
0156
0157 state->polarity = PWM_POLARITY_NORMAL;
0158 }
0159
0160 static const struct pwm_ops sunplus_pwm_ops = {
0161 .apply = sunplus_pwm_apply,
0162 .get_state = sunplus_pwm_get_state,
0163 .owner = THIS_MODULE,
0164 };
0165
0166 static void sunplus_pwm_clk_release(void *data)
0167 {
0168 struct clk *clk = data;
0169
0170 clk_disable_unprepare(clk);
0171 }
0172
0173 static int sunplus_pwm_probe(struct platform_device *pdev)
0174 {
0175 struct device *dev = &pdev->dev;
0176 struct sunplus_pwm *priv;
0177 int ret;
0178
0179 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0180 if (!priv)
0181 return -ENOMEM;
0182
0183 priv->base = devm_platform_ioremap_resource(pdev, 0);
0184 if (IS_ERR(priv->base))
0185 return PTR_ERR(priv->base);
0186
0187 priv->clk = devm_clk_get(dev, NULL);
0188 if (IS_ERR(priv->clk))
0189 return dev_err_probe(dev, PTR_ERR(priv->clk),
0190 "get pwm clock failed\n");
0191
0192 ret = clk_prepare_enable(priv->clk);
0193 if (ret < 0) {
0194 dev_err(dev, "failed to enable clock: %d\n", ret);
0195 return ret;
0196 }
0197
0198 ret = devm_add_action_or_reset(dev, sunplus_pwm_clk_release, priv->clk);
0199 if (ret < 0) {
0200 dev_err(dev, "failed to release clock: %d\n", ret);
0201 return ret;
0202 }
0203
0204 priv->chip.dev = dev;
0205 priv->chip.ops = &sunplus_pwm_ops;
0206 priv->chip.npwm = SP7021_PWM_NUM;
0207
0208 ret = devm_pwmchip_add(dev, &priv->chip);
0209 if (ret < 0)
0210 return dev_err_probe(dev, ret, "Cannot register sunplus PWM\n");
0211
0212 return 0;
0213 }
0214
0215 static const struct of_device_id sunplus_pwm_of_match[] = {
0216 { .compatible = "sunplus,sp7021-pwm", },
0217 {}
0218 };
0219 MODULE_DEVICE_TABLE(of, sunplus_pwm_of_match);
0220
0221 static struct platform_driver sunplus_pwm_driver = {
0222 .probe = sunplus_pwm_probe,
0223 .driver = {
0224 .name = "sunplus-pwm",
0225 .of_match_table = sunplus_pwm_of_match,
0226 },
0227 };
0228 module_platform_driver(sunplus_pwm_driver);
0229
0230 MODULE_DESCRIPTION("Sunplus SoC PWM Driver");
0231 MODULE_AUTHOR("Hammer Hsieh <hammerh0314@gmail.com>");
0232 MODULE_LICENSE("GPL");