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 /* Disable PWM output. 67 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the 68 * counter is stopped, while in TCU1 mode the order does not matter. 69 */ 70 ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; 71 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 72 73 /* Stop counter */ 74 jz4740_timer_disable(pwm->hwpwm); 75 } 76 77 static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 78 int duty_ns, int period_ns) 79 { 80 struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); 81 unsigned long long tmp; 82 unsigned long period, duty; 83 unsigned int prescaler = 0; 84 uint16_t ctrl; 85 bool is_enabled; 86 87 tmp = (unsigned long long)clk_get_rate(jz4740->clk) * period_ns; 88 do_div(tmp, 1000000000); 89 period = tmp; 90 91 while (period > 0xffff && prescaler < 6) { 92 period >>= 2; 93 ++prescaler; 94 } 95 96 if (prescaler == 6) 97 return -EINVAL; 98 99 tmp = (unsigned long long)period * duty_ns; 100 do_div(tmp, period_ns); 101 duty = period - tmp; 102 103 if (duty >= period) 104 duty = period - 1; 105 106 is_enabled = jz4740_timer_is_enabled(pwm->hwpwm); 107 if (is_enabled) 108 jz4740_pwm_disable(chip, pwm); 109 110 jz4740_timer_set_count(pwm->hwpwm, 0); 111 jz4740_timer_set_duty(pwm->hwpwm, duty); 112 jz4740_timer_set_period(pwm->hwpwm, period); 113 114 ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | 115 JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; 116 117 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 118 119 if (is_enabled) 120 jz4740_pwm_enable(chip, pwm); 121 122 return 0; 123 } 124 125 static int jz4740_pwm_set_polarity(struct pwm_chip *chip, 126 struct pwm_device *pwm, enum pwm_polarity polarity) 127 { 128 uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); 129 130 switch (polarity) { 131 case PWM_POLARITY_NORMAL: 132 ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW; 133 break; 134 case PWM_POLARITY_INVERSED: 135 ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW; 136 break; 137 } 138 139 jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); 140 return 0; 141 } 142 143 static const struct pwm_ops jz4740_pwm_ops = { 144 .request = jz4740_pwm_request, 145 .free = jz4740_pwm_free, 146 .config = jz4740_pwm_config, 147 .set_polarity = jz4740_pwm_set_polarity, 148 .enable = jz4740_pwm_enable, 149 .disable = jz4740_pwm_disable, 150 .owner = THIS_MODULE, 151 }; 152 153 static int jz4740_pwm_probe(struct platform_device *pdev) 154 { 155 struct jz4740_pwm_chip *jz4740; 156 157 jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); 158 if (!jz4740) 159 return -ENOMEM; 160 161 jz4740->clk = devm_clk_get(&pdev->dev, "ext"); 162 if (IS_ERR(jz4740->clk)) 163 return PTR_ERR(jz4740->clk); 164 165 jz4740->chip.dev = &pdev->dev; 166 jz4740->chip.ops = &jz4740_pwm_ops; 167 jz4740->chip.npwm = NUM_PWM; 168 jz4740->chip.base = -1; 169 jz4740->chip.of_xlate = of_pwm_xlate_with_flags; 170 jz4740->chip.of_pwm_n_cells = 3; 171 172 platform_set_drvdata(pdev, jz4740); 173 174 return pwmchip_add(&jz4740->chip); 175 } 176 177 static int jz4740_pwm_remove(struct platform_device *pdev) 178 { 179 struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev); 180 181 return pwmchip_remove(&jz4740->chip); 182 } 183 184 #ifdef CONFIG_OF 185 static const struct of_device_id jz4740_pwm_dt_ids[] = { 186 { .compatible = "ingenic,jz4740-pwm", }, 187 { .compatible = "ingenic,jz4770-pwm", }, 188 { .compatible = "ingenic,jz4780-pwm", }, 189 {}, 190 }; 191 MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids); 192 #endif 193 194 static struct platform_driver jz4740_pwm_driver = { 195 .driver = { 196 .name = "jz4740-pwm", 197 .of_match_table = of_match_ptr(jz4740_pwm_dt_ids), 198 }, 199 .probe = jz4740_pwm_probe, 200 .remove = jz4740_pwm_remove, 201 }; 202 module_platform_driver(jz4740_pwm_driver); 203 204 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 205 MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver"); 206 MODULE_ALIAS("platform:jz4740-pwm"); 207 MODULE_LICENSE("GPL"); 208