1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2017-2025 Loongson Technology Corporation Limited. 4 * 5 * Loongson PWM driver 6 * 7 * For Loongson's PWM IP block documentation please refer Chapter 11 of 8 * Reference Manual: https://loongson.github.io/LoongArch-Documentation/Loongson-7A1000-usermanual-EN.pdf 9 * 10 * Author: Juxin Gao <gaojuxin@loongson.cn> 11 * Further cleanup and restructuring by: 12 * Binbin Zhou <zhoubinbin@loongson.cn> 13 * 14 * Limitations: 15 * - If both DUTY and PERIOD are set to 0, the output is a constant low signal. 16 * - When disabled the output is driven to 0 independent of the configured 17 * polarity. 18 * - If the register is reconfigured while PWM is running, it does not complete 19 * the currently running period. 20 * - Disabling the PWM stops the output immediately (without waiting for current 21 * period to complete first). 22 */ 23 24 #include <linux/acpi.h> 25 #include <linux/clk.h> 26 #include <linux/device.h> 27 #include <linux/init.h> 28 #include <linux/io.h> 29 #include <linux/kernel.h> 30 #include <linux/module.h> 31 #include <linux/platform_device.h> 32 #include <linux/pwm.h> 33 #include <linux/units.h> 34 35 /* Loongson PWM registers */ 36 #define LOONGSON_PWM_REG_DUTY 0x4 /* Low Pulse Buffer Register */ 37 #define LOONGSON_PWM_REG_PERIOD 0x8 /* Pulse Period Buffer Register */ 38 #define LOONGSON_PWM_REG_CTRL 0xc /* Control Register */ 39 40 /* Control register bits */ 41 #define LOONGSON_PWM_CTRL_REG_EN BIT(0) /* Counter Enable Bit */ 42 #define LOONGSON_PWM_CTRL_REG_OE BIT(3) /* Pulse Output Enable Control Bit, Valid Low */ 43 #define LOONGSON_PWM_CTRL_REG_SINGLE BIT(4) /* Single Pulse Control Bit */ 44 #define LOONGSON_PWM_CTRL_REG_INTE BIT(5) /* Interrupt Enable Bit */ 45 #define LOONGSON_PWM_CTRL_REG_INT BIT(6) /* Interrupt Bit */ 46 #define LOONGSON_PWM_CTRL_REG_RST BIT(7) /* Counter Reset Bit */ 47 #define LOONGSON_PWM_CTRL_REG_CAPTE BIT(8) /* Measurement Pulse Enable Bit */ 48 #define LOONGSON_PWM_CTRL_REG_INVERT BIT(9) /* Output flip-flop Enable Bit */ 49 #define LOONGSON_PWM_CTRL_REG_DZONE BIT(10) /* Anti-dead Zone Enable Bit */ 50 51 /* default input clk frequency for the ACPI case */ 52 #define LOONGSON_PWM_FREQ_DEFAULT 50000 /* Hz */ 53 54 struct pwm_loongson_ddata { 55 struct clk *clk; 56 void __iomem *base; 57 u64 clk_rate; 58 }; 59 60 static inline __pure struct pwm_loongson_ddata *to_pwm_loongson_ddata(struct pwm_chip *chip) 61 { 62 return pwmchip_get_drvdata(chip); 63 } 64 65 static inline u32 pwm_loongson_readl(struct pwm_loongson_ddata *ddata, u32 offset) 66 { 67 return readl(ddata->base + offset); 68 } 69 70 static inline void pwm_loongson_writel(struct pwm_loongson_ddata *ddata, 71 u32 val, u32 offset) 72 { 73 writel(val, ddata->base + offset); 74 } 75 76 static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, 77 enum pwm_polarity polarity) 78 { 79 u16 val; 80 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 81 82 val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); 83 84 if (polarity == PWM_POLARITY_INVERSED) 85 /* Duty cycle defines LOW period of PWM */ 86 val |= LOONGSON_PWM_CTRL_REG_INVERT; 87 else 88 /* Duty cycle defines HIGH period of PWM */ 89 val &= ~LOONGSON_PWM_CTRL_REG_INVERT; 90 91 pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); 92 93 return 0; 94 } 95 96 static void pwm_loongson_disable(struct pwm_chip *chip, struct pwm_device *pwm) 97 { 98 u32 val; 99 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 100 101 val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); 102 val &= ~LOONGSON_PWM_CTRL_REG_EN; 103 pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); 104 } 105 106 static int pwm_loongson_enable(struct pwm_chip *chip, struct pwm_device *pwm) 107 { 108 u32 val; 109 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 110 111 val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); 112 val |= LOONGSON_PWM_CTRL_REG_EN; 113 pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); 114 115 return 0; 116 } 117 118 static int pwm_loongson_config(struct pwm_chip *chip, struct pwm_device *pwm, 119 u64 duty_ns, u64 period_ns) 120 { 121 u64 duty, period; 122 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 123 124 /* duty = duty_ns * ddata->clk_rate / NSEC_PER_SEC */ 125 duty = mul_u64_u64_div_u64(duty_ns, ddata->clk_rate, NSEC_PER_SEC); 126 if (duty > U32_MAX) 127 duty = U32_MAX; 128 129 /* period = period_ns * ddata->clk_rate / NSEC_PER_SEC */ 130 period = mul_u64_u64_div_u64(period_ns, ddata->clk_rate, NSEC_PER_SEC); 131 if (period > U32_MAX) 132 period = U32_MAX; 133 134 pwm_loongson_writel(ddata, duty, LOONGSON_PWM_REG_DUTY); 135 pwm_loongson_writel(ddata, period, LOONGSON_PWM_REG_PERIOD); 136 137 return 0; 138 } 139 140 static int pwm_loongson_apply(struct pwm_chip *chip, struct pwm_device *pwm, 141 const struct pwm_state *state) 142 { 143 int ret; 144 bool enabled = pwm->state.enabled; 145 146 if (!state->enabled) { 147 if (enabled) 148 pwm_loongson_disable(chip, pwm); 149 return 0; 150 } 151 152 ret = pwm_loongson_set_polarity(chip, pwm, state->polarity); 153 if (ret) 154 return ret; 155 156 ret = pwm_loongson_config(chip, pwm, state->duty_cycle, state->period); 157 if (ret) 158 return ret; 159 160 if (!enabled && state->enabled) 161 ret = pwm_loongson_enable(chip, pwm); 162 163 return ret; 164 } 165 166 static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 167 struct pwm_state *state) 168 { 169 u32 duty, period, ctrl; 170 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 171 172 duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY); 173 period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD); 174 ctrl = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); 175 176 /* duty & period have a max of 2^32, so we can't overflow */ 177 state->duty_cycle = DIV64_U64_ROUND_UP((u64)duty * NSEC_PER_SEC, ddata->clk_rate); 178 state->period = DIV64_U64_ROUND_UP((u64)period * NSEC_PER_SEC, ddata->clk_rate); 179 state->polarity = (ctrl & LOONGSON_PWM_CTRL_REG_INVERT) ? PWM_POLARITY_INVERSED : 180 PWM_POLARITY_NORMAL; 181 state->enabled = (ctrl & LOONGSON_PWM_CTRL_REG_EN) ? true : false; 182 183 return 0; 184 } 185 186 static const struct pwm_ops pwm_loongson_ops = { 187 .apply = pwm_loongson_apply, 188 .get_state = pwm_loongson_get_state, 189 }; 190 191 static int pwm_loongson_probe(struct platform_device *pdev) 192 { 193 int ret; 194 struct pwm_chip *chip; 195 struct pwm_loongson_ddata *ddata; 196 struct device *dev = &pdev->dev; 197 198 chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata)); 199 if (IS_ERR(chip)) 200 return PTR_ERR(chip); 201 ddata = to_pwm_loongson_ddata(chip); 202 203 ddata->base = devm_platform_ioremap_resource(pdev, 0); 204 if (IS_ERR(ddata->base)) 205 return PTR_ERR(ddata->base); 206 207 ddata->clk = devm_clk_get_optional_enabled(dev, NULL); 208 if (IS_ERR(ddata->clk)) 209 return dev_err_probe(dev, PTR_ERR(ddata->clk), 210 "Failed to get pwm clock\n"); 211 if (ddata->clk) { 212 ret = devm_clk_rate_exclusive_get(dev, ddata->clk); 213 if (ret) 214 return dev_err_probe(dev, ret, 215 "Failed to get exclusive rate\n"); 216 217 ddata->clk_rate = clk_get_rate(ddata->clk); 218 if (!ddata->clk_rate) 219 return dev_err_probe(dev, -EINVAL, 220 "Failed to get frequency\n"); 221 } else { 222 ddata->clk_rate = LOONGSON_PWM_FREQ_DEFAULT; 223 } 224 225 /* This check is done to prevent an overflow in .apply */ 226 if (ddata->clk_rate > NSEC_PER_SEC) 227 return dev_err_probe(dev, -EINVAL, "PWM clock out of range\n"); 228 229 chip->ops = &pwm_loongson_ops; 230 chip->atomic = true; 231 dev_set_drvdata(dev, chip); 232 233 ret = devm_pwmchip_add(dev, chip); 234 if (ret < 0) 235 return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); 236 237 return 0; 238 } 239 240 static int pwm_loongson_suspend(struct device *dev) 241 { 242 struct pwm_chip *chip = dev_get_drvdata(dev); 243 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 244 struct pwm_device *pwm = &chip->pwms[0]; 245 246 if (pwm->state.enabled) 247 return -EBUSY; 248 249 clk_disable_unprepare(ddata->clk); 250 251 return 0; 252 } 253 254 static int pwm_loongson_resume(struct device *dev) 255 { 256 struct pwm_chip *chip = dev_get_drvdata(dev); 257 struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); 258 259 return clk_prepare_enable(ddata->clk); 260 } 261 262 static DEFINE_SIMPLE_DEV_PM_OPS(pwm_loongson_pm_ops, pwm_loongson_suspend, 263 pwm_loongson_resume); 264 265 static const struct of_device_id pwm_loongson_of_ids[] = { 266 { .compatible = "loongson,ls7a-pwm" }, 267 { /* sentinel */ }, 268 }; 269 MODULE_DEVICE_TABLE(of, pwm_loongson_of_ids); 270 271 static const struct acpi_device_id pwm_loongson_acpi_ids[] = { 272 { "LOON0006" }, 273 { } 274 }; 275 MODULE_DEVICE_TABLE(acpi, pwm_loongson_acpi_ids); 276 277 static struct platform_driver pwm_loongson_driver = { 278 .probe = pwm_loongson_probe, 279 .driver = { 280 .name = "loongson-pwm", 281 .pm = pm_ptr(&pwm_loongson_pm_ops), 282 .of_match_table = pwm_loongson_of_ids, 283 .acpi_match_table = pwm_loongson_acpi_ids, 284 }, 285 }; 286 module_platform_driver(pwm_loongson_driver); 287 288 MODULE_DESCRIPTION("Loongson PWM driver"); 289 MODULE_AUTHOR("Loongson Technology Corporation Limited."); 290 MODULE_LICENSE("GPL"); 291