1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Sophgo SG2042 PWM Controller Driver 4 * 5 * Copyright (C) 2024 Sophgo Technology Inc. 6 * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> 7 * 8 * Limitations: 9 * - After reset, the output of the PWM channel is always high. 10 * The value of HLPERIOD/PERIOD is 0. 11 * - When HLPERIOD or PERIOD is reconfigured, PWM will start to 12 * output waveforms with the new configuration after completing 13 * the running period. 14 * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will 15 * be stopped and the output is pulled to high. 16 * See the datasheet [1] for more details. 17 * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM 18 */ 19 20 #include <linux/clk.h> 21 #include <linux/err.h> 22 #include <linux/io.h> 23 #include <linux/math64.h> 24 #include <linux/module.h> 25 #include <linux/platform_device.h> 26 #include <linux/pwm.h> 27 #include <linux/reset.h> 28 29 /* 30 * Offset RegisterName 31 * 0x0000 HLPERIOD0 32 * 0x0004 PERIOD0 33 * 0x0008 HLPERIOD1 34 * 0x000C PERIOD1 35 * 0x0010 HLPERIOD2 36 * 0x0014 PERIOD2 37 * 0x0018 HLPERIOD3 38 * 0x001C PERIOD3 39 * Four groups and every group is composed of HLPERIOD & PERIOD 40 */ 41 #define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0) 42 #define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4) 43 44 #define SG2042_PWM_CHANNELNUM 4 45 46 /** 47 * struct sg2042_pwm_ddata - private driver data 48 * @base: base address of mapped PWM registers 49 * @clk_rate_hz: rate of base clock in HZ 50 */ 51 struct sg2042_pwm_ddata { 52 void __iomem *base; 53 unsigned long clk_rate_hz; 54 }; 55 56 /* 57 * period_ticks: PERIOD 58 * hlperiod_ticks: HLPERIOD 59 */ 60 static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan, 61 u32 period_ticks, u32 hlperiod_ticks) 62 { 63 void __iomem *base = ddata->base; 64 65 writel(period_ticks, base + SG2042_PWM_PERIOD(chan)); 66 writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan)); 67 } 68 69 static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, 70 const struct pwm_state *state) 71 { 72 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); 73 u32 hlperiod_ticks; 74 u32 period_ticks; 75 76 if (state->polarity == PWM_POLARITY_INVERSED) 77 return -EINVAL; 78 79 if (!state->enabled) { 80 pwm_sg2042_config(ddata, pwm->hwpwm, 0, 0); 81 return 0; 82 } 83 84 /* 85 * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk 86 * Duration of One Cycle (period) = PERIOD x Period_of_input_clk 87 */ 88 period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX); 89 hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX); 90 91 dev_dbg(pwmchip_parent(chip), "chan[%u]: PERIOD=%u, HLPERIOD=%u\n", 92 pwm->hwpwm, period_ticks, hlperiod_ticks); 93 94 pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks); 95 96 return 0; 97 } 98 99 static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 100 struct pwm_state *state) 101 { 102 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); 103 unsigned int chan = pwm->hwpwm; 104 u32 hlperiod_ticks; 105 u32 period_ticks; 106 107 period_ticks = readl(ddata->base + SG2042_PWM_PERIOD(chan)); 108 hlperiod_ticks = readl(ddata->base + SG2042_PWM_HLPERIOD(chan)); 109 110 if (!period_ticks) { 111 state->enabled = false; 112 return 0; 113 } 114 115 if (hlperiod_ticks > period_ticks) 116 hlperiod_ticks = period_ticks; 117 118 state->enabled = true; 119 state->period = DIV_ROUND_UP_ULL((u64)period_ticks * NSEC_PER_SEC, ddata->clk_rate_hz); 120 state->duty_cycle = DIV_ROUND_UP_ULL((u64)hlperiod_ticks * NSEC_PER_SEC, ddata->clk_rate_hz); 121 state->polarity = PWM_POLARITY_NORMAL; 122 123 return 0; 124 } 125 126 static const struct pwm_ops pwm_sg2042_ops = { 127 .apply = pwm_sg2042_apply, 128 .get_state = pwm_sg2042_get_state, 129 }; 130 131 static const struct of_device_id sg2042_pwm_ids[] = { 132 { .compatible = "sophgo,sg2042-pwm" }, 133 { } 134 }; 135 MODULE_DEVICE_TABLE(of, sg2042_pwm_ids); 136 137 static int pwm_sg2042_probe(struct platform_device *pdev) 138 { 139 struct device *dev = &pdev->dev; 140 struct sg2042_pwm_ddata *ddata; 141 struct reset_control *rst; 142 struct pwm_chip *chip; 143 struct clk *clk; 144 int ret; 145 146 chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata)); 147 if (IS_ERR(chip)) 148 return PTR_ERR(chip); 149 ddata = pwmchip_get_drvdata(chip); 150 151 ddata->base = devm_platform_ioremap_resource(pdev, 0); 152 if (IS_ERR(ddata->base)) 153 return PTR_ERR(ddata->base); 154 155 clk = devm_clk_get_enabled(dev, "apb"); 156 if (IS_ERR(clk)) 157 return dev_err_probe(dev, PTR_ERR(clk), "Failed to get base clk\n"); 158 159 ret = devm_clk_rate_exclusive_get(dev, clk); 160 if (ret) 161 return dev_err_probe(dev, ret, "Failed to get exclusive rate\n"); 162 163 ddata->clk_rate_hz = clk_get_rate(clk); 164 /* period = PERIOD * NSEC_PER_SEC / clk_rate_hz */ 165 if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC) 166 return dev_err_probe(dev, -EINVAL, 167 "Invalid clock rate: %lu\n", ddata->clk_rate_hz); 168 169 rst = devm_reset_control_get_optional_shared_deasserted(dev, NULL); 170 if (IS_ERR(rst)) 171 return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n"); 172 173 chip->ops = &pwm_sg2042_ops; 174 chip->atomic = true; 175 176 ret = devm_pwmchip_add(dev, chip); 177 if (ret < 0) 178 return dev_err_probe(dev, ret, "Failed to register PWM chip\n"); 179 180 return 0; 181 } 182 183 static struct platform_driver pwm_sg2042_driver = { 184 .driver = { 185 .name = "sg2042-pwm", 186 .of_match_table = sg2042_pwm_ids, 187 }, 188 .probe = pwm_sg2042_probe, 189 }; 190 module_platform_driver(pwm_sg2042_driver); 191 192 MODULE_AUTHOR("Chen Wang"); 193 MODULE_DESCRIPTION("Sophgo SG2042 PWM driver"); 194 MODULE_LICENSE("GPL"); 195