1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 4 * JZ4740 platform PWM support 5 */ 6 7 #include <linux/clk.h> 8 #include <linux/err.h> 9 #include <linux/gpio.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/of_device.h> 13 #include <linux/platform_device.h> 14 #include <linux/pwm.h> 15 16 #include <asm/mach-jz4740/timer.h> 17 18 #define NUM_PWM 8 19 20 struct jz4740_pwm_chip { 21 struct pwm_chip chip; 22 struct clk *clk; 23 }; 24 25 static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) 26 { 27 return container_of(chip, struct jz4740_pwm_chip, chip); 28 } 29 30 static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 31 { 32 /* 33 * Timers 0 and 1 are used for system tasks, so they are unavailable 34 * for use as PWMs. 35 */ 36 if (pwm->hwpwm < 2) 37 return -EBUSY; 38 39 jz4740_timer_start(pwm->hwpwm); 40 41 return 0; 42 } 43 44 static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 45 { 46 jz4740_timer_set_ctrl(pwm->hwpwm, 0); 47 48 jz4740_timer_stop(pwm->hwpwm); 49 } 50 51 static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 52 { 53 uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); 54 55 ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; 56 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 57 jz4740_timer_enable(pwm->hwpwm); 58 59 return 0; 60 } 61 62 static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 63 { 64 uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); 65 66 /* 67 * Set duty > period. This trick allows the TCU channels in TCU2 mode to 68 * properly return to their init level. 69 */ 70 jz4740_timer_set_duty(pwm->hwpwm, 0xffff); 71 jz4740_timer_set_period(pwm->hwpwm, 0x0); 72 73 /* 74 * Disable PWM output. 75 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the 76 * counter is stopped, while in TCU1 mode the order does not matter. 77 */ 78 ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; 79 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 80 81 /* Stop counter */ 82 jz4740_timer_disable(pwm->hwpwm); 83 } 84 85 static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 86 struct pwm_state *state) 87 { 88 struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); 89 unsigned long long tmp; 90 unsigned long period, duty; 91 unsigned int prescaler = 0; 92 uint16_t ctrl; 93 94 tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period; 95 do_div(tmp, 1000000000); 96 period = tmp; 97 98 while (period > 0xffff && prescaler < 6) { 99 period >>= 2; 100 ++prescaler; 101 } 102 103 if (prescaler == 6) 104 return -EINVAL; 105 106 tmp = (unsigned long long)period * state->duty_cycle; 107 do_div(tmp, state->period); 108 duty = period - tmp; 109 110 if (duty >= period) 111 duty = period - 1; 112 113 jz4740_pwm_disable(chip, pwm); 114 115 jz4740_timer_set_count(pwm->hwpwm, 0); 116 jz4740_timer_set_duty(pwm->hwpwm, duty); 117 jz4740_timer_set_period(pwm->hwpwm, period); 118 119 ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | 120 JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; 121 122 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 123 124 switch (state->polarity) { 125 case PWM_POLARITY_NORMAL: 126 ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW; 127 break; 128 case PWM_POLARITY_INVERSED: 129 ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW; 130 break; 131 } 132 133 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 134 135 if (state->enabled) 136 jz4740_pwm_enable(chip, pwm); 137 138 return 0; 139 } 140 141 static const struct pwm_ops jz4740_pwm_ops = { 142 .request = jz4740_pwm_request, 143 .free = jz4740_pwm_free, 144 .apply = jz4740_pwm_apply, 145 .owner = THIS_MODULE, 146 }; 147 148 static int jz4740_pwm_probe(struct platform_device *pdev) 149 { 150 struct jz4740_pwm_chip *jz4740; 151 152 jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); 153 if (!jz4740) 154 return -ENOMEM; 155 156 jz4740->clk = devm_clk_get(&pdev->dev, "ext"); 157 if (IS_ERR(jz4740->clk)) 158 return PTR_ERR(jz4740->clk); 159 160 jz4740->chip.dev = &pdev->dev; 161 jz4740->chip.ops = &jz4740_pwm_ops; 162 jz4740->chip.npwm = NUM_PWM; 163 jz4740->chip.base = -1; 164 jz4740->chip.of_xlate = of_pwm_xlate_with_flags; 165 jz4740->chip.of_pwm_n_cells = 3; 166 167 platform_set_drvdata(pdev, jz4740); 168 169 return pwmchip_add(&jz4740->chip); 170 } 171 172 static int jz4740_pwm_remove(struct platform_device *pdev) 173 { 174 struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev); 175 176 return pwmchip_remove(&jz4740->chip); 177 } 178 179 #ifdef CONFIG_OF 180 static const struct of_device_id jz4740_pwm_dt_ids[] = { 181 { .compatible = "ingenic,jz4740-pwm", }, 182 {}, 183 }; 184 MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids); 185 #endif 186 187 static struct platform_driver jz4740_pwm_driver = { 188 .driver = { 189 .name = "jz4740-pwm", 190 .of_match_table = of_match_ptr(jz4740_pwm_dt_ids), 191 }, 192 .probe = jz4740_pwm_probe, 193 .remove = jz4740_pwm_remove, 194 }; 195 module_platform_driver(jz4740_pwm_driver); 196 197 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 198 MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver"); 199 MODULE_ALIAS("platform:jz4740-pwm"); 200 MODULE_LICENSE("GPL"); 201