1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH 4 * 5 * Reference Manual : https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf 6 * 7 * Limitations: 8 * - Supports frequencies between 0.5Hz and 2048Hz with following steps: 9 * - 0.5 Hz steps from 0.5 Hz to 32 Hz 10 * - 2 Hz steps from 2 Hz to 128 Hz 11 * - 8 Hz steps from 8 Hz to 512 Hz 12 * - 32 Hz steps from 32 Hz to 2048 Hz 13 * - Cannot generate a 0 % duty cycle. 14 * - Always produces low output if disabled. 15 * - Configuration isn't atomic. When changing polarity, duty cycle or period 16 * the data is taken immediately, counters not being affected, resulting in a 17 * behavior of the output pin that is neither the old nor the new state, 18 * rather something in between. 19 */ 20 #define DEFAULT_SYMBOL_NAMESPACE "PWM_MC33XS2410" 21 22 #include <linux/auxiliary_bus.h> 23 #include <linux/bitfield.h> 24 #include <linux/delay.h> 25 #include <linux/err.h> 26 #include <linux/math64.h> 27 #include <linux/mc33xs2410.h> 28 #include <linux/minmax.h> 29 #include <linux/module.h> 30 #include <linux/of.h> 31 #include <linux/pwm.h> 32 33 #include <linux/spi/spi.h> 34 35 #define MC33XS2410_GLB_CTRL 0x00 36 #define MC33XS2410_GLB_CTRL_MODE GENMASK(7, 6) 37 #define MC33XS2410_GLB_CTRL_MODE_NORMAL FIELD_PREP(MC33XS2410_GLB_CTRL_MODE, 1) 38 39 #define MC33XS2410_PWM_CTRL1 0x05 40 /* chan in { 1 ... 4 } */ 41 #define MC33XS2410_PWM_CTRL1_POL_INV(chan) BIT((chan) + 1) 42 43 #define MC33XS2410_PWM_CTRL3 0x07 44 /* chan in { 1 ... 4 } */ 45 #define MC33XS2410_PWM_CTRL3_EN(chan) BIT(4 + (chan) - 1) 46 47 /* chan in { 1 ... 4 } */ 48 #define MC33XS2410_PWM_FREQ(chan) (0x08 + (chan) - 1) 49 #define MC33XS2410_PWM_FREQ_STEP GENMASK(7, 6) 50 #define MC33XS2410_PWM_FREQ_COUNT GENMASK(5, 0) 51 52 /* chan in { 1 ... 4 } */ 53 #define MC33XS2410_PWM_DC(chan) (0x0c + (chan) - 1) 54 55 #define MC33XS2410_WDT 0x14 56 57 #define MC33XS2410_PWM_MIN_PERIOD 488282 58 /* step in { 0 ... 3 } */ 59 #define MC33XS2410_PWM_MAX_PERIOD(step) (2000000000 >> (2 * (step))) 60 61 #define MC33XS2410_FRAME_IN_ADDR GENMASK(15, 8) 62 #define MC33XS2410_FRAME_IN_DATA GENMASK(7, 0) 63 #define MC33XS2410_FRAME_IN_ADDR_WR BIT(7) 64 #define MC33XS2410_FRAME_IN_DATA_RD BIT(7) 65 #define MC33XS2410_FRAME_OUT_DATA GENMASK(13, 0) 66 67 #define MC33XS2410_MAX_TRANSFERS 5 68 69 static int mc33xs2410_write_regs(struct spi_device *spi, u8 *reg, u8 *val, 70 unsigned int len) 71 { 72 u16 tx[MC33XS2410_MAX_TRANSFERS]; 73 int i; 74 75 if (len > MC33XS2410_MAX_TRANSFERS) 76 return -EINVAL; 77 78 for (i = 0; i < len; i++) 79 tx[i] = FIELD_PREP(MC33XS2410_FRAME_IN_DATA, val[i]) | 80 FIELD_PREP(MC33XS2410_FRAME_IN_ADDR, 81 MC33XS2410_FRAME_IN_ADDR_WR | reg[i]); 82 83 return spi_write(spi, tx, len * 2); 84 } 85 86 static int mc33xs2410_read_regs(struct spi_device *spi, u8 *reg, u8 flag, 87 u16 *val, unsigned int len) 88 { 89 u16 tx[MC33XS2410_MAX_TRANSFERS]; 90 u16 rx[MC33XS2410_MAX_TRANSFERS]; 91 struct spi_transfer t = { 92 .tx_buf = tx, 93 .rx_buf = rx, 94 }; 95 int i, ret; 96 97 len++; 98 if (len > MC33XS2410_MAX_TRANSFERS) 99 return -EINVAL; 100 101 t.len = len * 2; 102 for (i = 0; i < len - 1; i++) 103 tx[i] = FIELD_PREP(MC33XS2410_FRAME_IN_DATA, flag) | 104 FIELD_PREP(MC33XS2410_FRAME_IN_ADDR, reg[i]); 105 106 ret = spi_sync_transfer(spi, &t, 1); 107 if (ret < 0) 108 return ret; 109 110 for (i = 1; i < len; i++) 111 val[i - 1] = FIELD_GET(MC33XS2410_FRAME_OUT_DATA, rx[i]); 112 113 return 0; 114 } 115 116 static int mc33xs2410_write_reg(struct spi_device *spi, u8 reg, u8 val) 117 { 118 return mc33xs2410_write_regs(spi, ®, &val, 1); 119 } 120 121 static int mc33xs2410_read_reg(struct spi_device *spi, u8 reg, u16 *val, u8 flag) 122 { 123 return mc33xs2410_read_regs(spi, ®, flag, val, 1); 124 } 125 126 int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) 127 { 128 return mc33xs2410_read_reg(spi, reg, val, MC33XS2410_FRAME_IN_DATA_RD); 129 } 130 EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_ctrl); 131 132 int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val) 133 { 134 return mc33xs2410_read_reg(spi, reg, val, 0); 135 } 136 EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_diag); 137 138 int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) 139 { 140 u16 tmp; 141 int ret; 142 143 ret = mc33xs2410_read_reg_ctrl(spi, reg, &tmp); 144 if (ret < 0) 145 return ret; 146 147 tmp &= ~mask; 148 tmp |= val & mask; 149 150 return mc33xs2410_write_reg(spi, reg, tmp); 151 } 152 EXPORT_SYMBOL_GPL(mc33xs2410_modify_reg); 153 154 static u8 mc33xs2410_pwm_get_freq(u64 period) 155 { 156 u8 step, count; 157 158 /* 159 * Check which step [0 .. 3] is appropriate for the given period. The 160 * period ranges for the different step values overlap. Prefer big step 161 * values as these allow more finegrained period and duty cycle 162 * selection. 163 */ 164 165 switch (period) { 166 case MC33XS2410_PWM_MIN_PERIOD ... MC33XS2410_PWM_MAX_PERIOD(3): 167 step = 3; 168 break; 169 case MC33XS2410_PWM_MAX_PERIOD(3) + 1 ... MC33XS2410_PWM_MAX_PERIOD(2): 170 step = 2; 171 break; 172 case MC33XS2410_PWM_MAX_PERIOD(2) + 1 ... MC33XS2410_PWM_MAX_PERIOD(1): 173 step = 1; 174 break; 175 case MC33XS2410_PWM_MAX_PERIOD(1) + 1 ... MC33XS2410_PWM_MAX_PERIOD(0): 176 step = 0; 177 break; 178 } 179 180 /* 181 * Round up here because a higher count results in a higher frequency 182 * and so a smaller period. 183 */ 184 count = DIV_ROUND_UP((u32)MC33XS2410_PWM_MAX_PERIOD(step), (u32)period); 185 return FIELD_PREP(MC33XS2410_PWM_FREQ_STEP, step) | 186 FIELD_PREP(MC33XS2410_PWM_FREQ_COUNT, count - 1); 187 } 188 189 static u64 mc33xs2410_pwm_get_period(u8 reg) 190 { 191 u32 doubled_freq, code, doubled_steps; 192 193 /* 194 * steps: 195 * - 0 = 0.5Hz 196 * - 1 = 2Hz 197 * - 2 = 8Hz 198 * - 3 = 32Hz 199 * frequency = (code + 1) x steps. 200 * 201 * To avoid losing precision in case steps value is zero, scale the 202 * steps value for now by two and keep it in mind when calculating the 203 * period that the frequency had been doubled. 204 */ 205 doubled_steps = 1 << (FIELD_GET(MC33XS2410_PWM_FREQ_STEP, reg) * 2); 206 code = FIELD_GET(MC33XS2410_PWM_FREQ_COUNT, reg); 207 doubled_freq = (code + 1) * doubled_steps; 208 209 /* Convert frequency to period, considering the doubled frequency. */ 210 return DIV_ROUND_UP(2 * NSEC_PER_SEC, doubled_freq); 211 } 212 213 /* 214 * The hardware cannot generate a 0% relative duty cycle for normal and inversed 215 * polarity. For normal polarity, the channel must be disabled, the device then 216 * emits a constant low signal. 217 * For inverted polarity, the channel must be enabled, the polarity must be set 218 * to normal and the relative duty cylce must be set to 100%. The device then 219 * emits a constant high signal. 220 */ 221 static int mc33xs2410_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 222 const struct pwm_state *state) 223 { 224 struct spi_device *spi = pwmchip_get_drvdata(chip); 225 u8 reg[4] = { 226 MC33XS2410_PWM_FREQ(pwm->hwpwm + 1), 227 MC33XS2410_PWM_DC(pwm->hwpwm + 1), 228 MC33XS2410_PWM_CTRL1, 229 MC33XS2410_PWM_CTRL3 230 }; 231 u64 period, duty_cycle; 232 int ret, rel_dc; 233 u16 rd_val[2]; 234 u8 wr_val[4]; 235 u8 mask; 236 237 period = min(state->period, MC33XS2410_PWM_MAX_PERIOD(0)); 238 if (period < MC33XS2410_PWM_MIN_PERIOD) 239 return -EINVAL; 240 241 ret = mc33xs2410_read_regs(spi, ®[2], MC33XS2410_FRAME_IN_DATA_RD, rd_val, 2); 242 if (ret < 0) 243 return ret; 244 245 /* Frequency */ 246 wr_val[0] = mc33xs2410_pwm_get_freq(period); 247 /* Continue calculations with the possibly truncated period */ 248 period = mc33xs2410_pwm_get_period(wr_val[0]); 249 250 /* Duty cycle */ 251 duty_cycle = min(period, state->duty_cycle); 252 rel_dc = div64_u64(duty_cycle * 256, period) - 1; 253 if (rel_dc >= 0) 254 wr_val[1] = rel_dc; 255 else if (state->polarity == PWM_POLARITY_NORMAL) 256 wr_val[1] = 0; 257 else 258 wr_val[1] = 255; 259 260 /* Polarity */ 261 mask = MC33XS2410_PWM_CTRL1_POL_INV(pwm->hwpwm + 1); 262 if (state->polarity == PWM_POLARITY_INVERSED && rel_dc >= 0) 263 wr_val[2] = rd_val[0] | mask; 264 else 265 wr_val[2] = rd_val[0] & ~mask; 266 267 /* Enable */ 268 mask = MC33XS2410_PWM_CTRL3_EN(pwm->hwpwm + 1); 269 if (state->enabled && 270 !(state->polarity == PWM_POLARITY_NORMAL && rel_dc < 0)) 271 wr_val[3] = rd_val[1] | mask; 272 else 273 wr_val[3] = rd_val[1] & ~mask; 274 275 return mc33xs2410_write_regs(spi, reg, wr_val, 4); 276 } 277 278 static int mc33xs2410_pwm_get_state(struct pwm_chip *chip, 279 struct pwm_device *pwm, 280 struct pwm_state *state) 281 { 282 struct spi_device *spi = pwmchip_get_drvdata(chip); 283 u8 reg[4] = { 284 MC33XS2410_PWM_FREQ(pwm->hwpwm + 1), 285 MC33XS2410_PWM_DC(pwm->hwpwm + 1), 286 MC33XS2410_PWM_CTRL1, 287 MC33XS2410_PWM_CTRL3, 288 }; 289 u16 val[4]; 290 int ret; 291 292 ret = mc33xs2410_read_regs(spi, reg, MC33XS2410_FRAME_IN_DATA_RD, val, 293 ARRAY_SIZE(reg)); 294 if (ret < 0) 295 return ret; 296 297 state->period = mc33xs2410_pwm_get_period(val[0]); 298 state->polarity = (val[2] & MC33XS2410_PWM_CTRL1_POL_INV(pwm->hwpwm + 1)) ? 299 PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; 300 state->enabled = !!(val[3] & MC33XS2410_PWM_CTRL3_EN(pwm->hwpwm + 1)); 301 state->duty_cycle = DIV_ROUND_UP_ULL((val[1] + 1) * state->period, 256); 302 303 return 0; 304 } 305 306 static const struct pwm_ops mc33xs2410_pwm_ops = { 307 .apply = mc33xs2410_pwm_apply, 308 .get_state = mc33xs2410_pwm_get_state, 309 }; 310 311 static int mc33xs2410_reset(struct device *dev) 312 { 313 struct gpio_desc *reset_gpio; 314 315 reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 316 if (IS_ERR_OR_NULL(reset_gpio)) 317 return PTR_ERR_OR_ZERO(reset_gpio); 318 319 /* Wake-up time */ 320 fsleep(10000); 321 322 return 0; 323 } 324 325 static int mc33xs2410_probe(struct spi_device *spi) 326 { 327 struct device *dev = &spi->dev; 328 struct auxiliary_device *adev; 329 struct pwm_chip *chip; 330 int ret; 331 332 chip = devm_pwmchip_alloc(dev, 4, 0); 333 if (IS_ERR(chip)) 334 return PTR_ERR(chip); 335 336 spi->bits_per_word = 16; 337 spi->mode |= SPI_CS_WORD; 338 ret = spi_setup(spi); 339 if (ret < 0) 340 return ret; 341 342 pwmchip_set_drvdata(chip, spi); 343 chip->ops = &mc33xs2410_pwm_ops; 344 345 /* 346 * Deasserts the reset of the device. Shouldn't change the output signal 347 * if the device was setup prior to probing. 348 */ 349 ret = mc33xs2410_reset(dev); 350 if (ret) 351 return ret; 352 353 /* 354 * Disable watchdog and keep in mind that the watchdog won't trigger a 355 * reset of the machine when running into an timeout, instead the 356 * control over the outputs is handed over to the INx input logic 357 * signals of the device. Disabling it here just deactivates this 358 * feature until a proper solution is found. 359 */ 360 ret = mc33xs2410_write_reg(spi, MC33XS2410_WDT, 0x0); 361 if (ret < 0) 362 return dev_err_probe(dev, ret, "Failed to disable watchdog\n"); 363 364 /* Transition to normal mode */ 365 ret = mc33xs2410_modify_reg(spi, MC33XS2410_GLB_CTRL, 366 MC33XS2410_GLB_CTRL_MODE, 367 MC33XS2410_GLB_CTRL_MODE_NORMAL); 368 if (ret < 0) 369 return dev_err_probe(dev, ret, 370 "Failed to transition to normal mode\n"); 371 372 ret = devm_pwmchip_add(dev, chip); 373 if (ret < 0) 374 return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); 375 376 adev = devm_auxiliary_device_create(dev, "hwmon", NULL); 377 if (!adev) 378 return dev_err_probe(dev, -ENODEV, "Failed to register hwmon device\n"); 379 380 return 0; 381 } 382 383 static const struct spi_device_id mc33xs2410_spi_id[] = { 384 { "mc33xs2410" }, 385 { } 386 }; 387 MODULE_DEVICE_TABLE(spi, mc33xs2410_spi_id); 388 389 static const struct of_device_id mc33xs2410_of_match[] = { 390 { .compatible = "nxp,mc33xs2410" }, 391 { } 392 }; 393 MODULE_DEVICE_TABLE(of, mc33xs2410_of_match); 394 395 static struct spi_driver mc33xs2410_driver = { 396 .driver = { 397 .name = "mc33xs2410-pwm", 398 .of_match_table = mc33xs2410_of_match, 399 }, 400 .probe = mc33xs2410_probe, 401 .id_table = mc33xs2410_spi_id, 402 }; 403 module_spi_driver(mc33xs2410_driver); 404 405 MODULE_DESCRIPTION("NXP MC33XS2410 high-side switch driver"); 406 MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>"); 407 MODULE_LICENSE("GPL"); 408