| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Freescale FlexTimer Module (FTM) PWM Driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2012-2013 Freescale Semiconductor, Inc. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 8 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | | - * (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 22 | 18 | #include <linux/pwm.h> |
|---|
| 23 | 19 | #include <linux/regmap.h> |
|---|
| 24 | 20 | #include <linux/slab.h> |
|---|
| 21 | +#include <linux/fsl/ftm.h> |
|---|
| 25 | 22 | |
|---|
| 26 | | -#define FTM_SC 0x00 |
|---|
| 27 | | -#define FTM_SC_CLK_MASK_SHIFT 3 |
|---|
| 28 | | -#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) |
|---|
| 29 | 23 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) |
|---|
| 30 | | -#define FTM_SC_PS_MASK 0x7 |
|---|
| 31 | | - |
|---|
| 32 | | -#define FTM_CNT 0x04 |
|---|
| 33 | | -#define FTM_MOD 0x08 |
|---|
| 34 | | - |
|---|
| 35 | | -#define FTM_CSC_BASE 0x0C |
|---|
| 36 | | -#define FTM_CSC_MSB BIT(5) |
|---|
| 37 | | -#define FTM_CSC_MSA BIT(4) |
|---|
| 38 | | -#define FTM_CSC_ELSB BIT(3) |
|---|
| 39 | | -#define FTM_CSC_ELSA BIT(2) |
|---|
| 40 | | -#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) |
|---|
| 41 | | - |
|---|
| 42 | | -#define FTM_CV_BASE 0x10 |
|---|
| 43 | | -#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) |
|---|
| 44 | | - |
|---|
| 45 | | -#define FTM_CNTIN 0x4C |
|---|
| 46 | | -#define FTM_STATUS 0x50 |
|---|
| 47 | | - |
|---|
| 48 | | -#define FTM_MODE 0x54 |
|---|
| 49 | | -#define FTM_MODE_FTMEN BIT(0) |
|---|
| 50 | | -#define FTM_MODE_INIT BIT(2) |
|---|
| 51 | | -#define FTM_MODE_PWMSYNC BIT(3) |
|---|
| 52 | | - |
|---|
| 53 | | -#define FTM_SYNC 0x58 |
|---|
| 54 | | -#define FTM_OUTINIT 0x5C |
|---|
| 55 | | -#define FTM_OUTMASK 0x60 |
|---|
| 56 | | -#define FTM_COMBINE 0x64 |
|---|
| 57 | | -#define FTM_DEADTIME 0x68 |
|---|
| 58 | | -#define FTM_EXTTRIG 0x6C |
|---|
| 59 | | -#define FTM_POL 0x70 |
|---|
| 60 | | -#define FTM_FMS 0x74 |
|---|
| 61 | | -#define FTM_FILTER 0x78 |
|---|
| 62 | | -#define FTM_FLTCTRL 0x7C |
|---|
| 63 | | -#define FTM_QDCTRL 0x80 |
|---|
| 64 | | -#define FTM_CONF 0x84 |
|---|
| 65 | | -#define FTM_FLTPOL 0x88 |
|---|
| 66 | | -#define FTM_SYNCONF 0x8C |
|---|
| 67 | | -#define FTM_INVCTRL 0x90 |
|---|
| 68 | | -#define FTM_SWOCTRL 0x94 |
|---|
| 69 | | -#define FTM_PWMLOAD 0x98 |
|---|
| 70 | 24 | |
|---|
| 71 | 25 | enum fsl_pwm_clk { |
|---|
| 72 | 26 | FSL_PWM_CLK_SYS, |
|---|
| .. | .. |
|---|
| 80 | 34 | bool has_enable_bits; |
|---|
| 81 | 35 | }; |
|---|
| 82 | 36 | |
|---|
| 37 | +struct fsl_pwm_periodcfg { |
|---|
| 38 | + enum fsl_pwm_clk clk_select; |
|---|
| 39 | + unsigned int clk_ps; |
|---|
| 40 | + unsigned int mod_period; |
|---|
| 41 | +}; |
|---|
| 42 | + |
|---|
| 83 | 43 | struct fsl_pwm_chip { |
|---|
| 84 | 44 | struct pwm_chip chip; |
|---|
| 85 | | - |
|---|
| 86 | 45 | struct mutex lock; |
|---|
| 87 | | - |
|---|
| 88 | | - unsigned int cnt_select; |
|---|
| 89 | | - unsigned int clk_ps; |
|---|
| 90 | | - |
|---|
| 91 | 46 | struct regmap *regmap; |
|---|
| 92 | 47 | |
|---|
| 93 | | - int period_ns; |
|---|
| 48 | + /* This value is valid iff a pwm is running */ |
|---|
| 49 | + struct fsl_pwm_periodcfg period; |
|---|
| 94 | 50 | |
|---|
| 95 | 51 | struct clk *ipg_clk; |
|---|
| 96 | 52 | struct clk *clk[FSL_PWM_CLK_MAX]; |
|---|
| .. | .. |
|---|
| 101 | 57 | static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) |
|---|
| 102 | 58 | { |
|---|
| 103 | 59 | return container_of(chip, struct fsl_pwm_chip, chip); |
|---|
| 60 | +} |
|---|
| 61 | + |
|---|
| 62 | +static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc) |
|---|
| 63 | +{ |
|---|
| 64 | + u32 val; |
|---|
| 65 | + |
|---|
| 66 | + regmap_read(fpc->regmap, FTM_FMS, &val); |
|---|
| 67 | + if (val & FTM_FMS_WPEN) |
|---|
| 68 | + regmap_update_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS, |
|---|
| 69 | + FTM_MODE_WPDIS); |
|---|
| 70 | +} |
|---|
| 71 | + |
|---|
| 72 | +static void ftm_set_write_protection(struct fsl_pwm_chip *fpc) |
|---|
| 73 | +{ |
|---|
| 74 | + regmap_update_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN, FTM_FMS_WPEN); |
|---|
| 75 | +} |
|---|
| 76 | + |
|---|
| 77 | +static bool fsl_pwm_periodcfg_are_equal(const struct fsl_pwm_periodcfg *a, |
|---|
| 78 | + const struct fsl_pwm_periodcfg *b) |
|---|
| 79 | +{ |
|---|
| 80 | + if (a->clk_select != b->clk_select) |
|---|
| 81 | + return false; |
|---|
| 82 | + if (a->clk_ps != b->clk_ps) |
|---|
| 83 | + return false; |
|---|
| 84 | + if (a->mod_period != b->mod_period) |
|---|
| 85 | + return false; |
|---|
| 86 | + return true; |
|---|
| 104 | 87 | } |
|---|
| 105 | 88 | |
|---|
| 106 | 89 | static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
|---|
| .. | .. |
|---|
| 133 | 116 | clk_disable_unprepare(fpc->ipg_clk); |
|---|
| 134 | 117 | } |
|---|
| 135 | 118 | |
|---|
| 136 | | -static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc, |
|---|
| 137 | | - enum fsl_pwm_clk index) |
|---|
| 119 | +static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc, |
|---|
| 120 | + unsigned int ticks) |
|---|
| 138 | 121 | { |
|---|
| 139 | | - unsigned long sys_rate, cnt_rate; |
|---|
| 140 | | - unsigned long long ratio; |
|---|
| 122 | + unsigned long rate; |
|---|
| 123 | + unsigned long long exval; |
|---|
| 141 | 124 | |
|---|
| 142 | | - sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]); |
|---|
| 143 | | - if (!sys_rate) |
|---|
| 144 | | - return -EINVAL; |
|---|
| 145 | | - |
|---|
| 146 | | - cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]); |
|---|
| 147 | | - if (!cnt_rate) |
|---|
| 148 | | - return -EINVAL; |
|---|
| 149 | | - |
|---|
| 150 | | - switch (index) { |
|---|
| 151 | | - case FSL_PWM_CLK_SYS: |
|---|
| 152 | | - fpc->clk_ps = 1; |
|---|
| 153 | | - break; |
|---|
| 154 | | - case FSL_PWM_CLK_FIX: |
|---|
| 155 | | - ratio = 2 * cnt_rate - 1; |
|---|
| 156 | | - do_div(ratio, sys_rate); |
|---|
| 157 | | - fpc->clk_ps = ratio; |
|---|
| 158 | | - break; |
|---|
| 159 | | - case FSL_PWM_CLK_EXT: |
|---|
| 160 | | - ratio = 4 * cnt_rate - 1; |
|---|
| 161 | | - do_div(ratio, sys_rate); |
|---|
| 162 | | - fpc->clk_ps = ratio; |
|---|
| 163 | | - break; |
|---|
| 164 | | - default: |
|---|
| 165 | | - return -EINVAL; |
|---|
| 166 | | - } |
|---|
| 167 | | - |
|---|
| 168 | | - return 0; |
|---|
| 125 | + rate = clk_get_rate(fpc->clk[fpc->period.clk_select]); |
|---|
| 126 | + exval = ticks; |
|---|
| 127 | + exval *= 1000000000UL; |
|---|
| 128 | + do_div(exval, rate >> fpc->period.clk_ps); |
|---|
| 129 | + return exval; |
|---|
| 169 | 130 | } |
|---|
| 170 | 131 | |
|---|
| 171 | | -static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc, |
|---|
| 172 | | - unsigned long period_ns) |
|---|
| 132 | +static bool fsl_pwm_calculate_period_clk(struct fsl_pwm_chip *fpc, |
|---|
| 133 | + unsigned int period_ns, |
|---|
| 134 | + enum fsl_pwm_clk index, |
|---|
| 135 | + struct fsl_pwm_periodcfg *periodcfg |
|---|
| 136 | + ) |
|---|
| 173 | 137 | { |
|---|
| 174 | | - unsigned long long c, c0; |
|---|
| 138 | + unsigned long long c; |
|---|
| 139 | + unsigned int ps; |
|---|
| 175 | 140 | |
|---|
| 176 | | - c = clk_get_rate(fpc->clk[fpc->cnt_select]); |
|---|
| 141 | + c = clk_get_rate(fpc->clk[index]); |
|---|
| 177 | 142 | c = c * period_ns; |
|---|
| 178 | 143 | do_div(c, 1000000000UL); |
|---|
| 179 | 144 | |
|---|
| 180 | | - do { |
|---|
| 181 | | - c0 = c; |
|---|
| 182 | | - do_div(c0, (1 << fpc->clk_ps)); |
|---|
| 183 | | - if (c0 <= 0xFFFF) |
|---|
| 184 | | - return (unsigned long)c0; |
|---|
| 185 | | - } while (++fpc->clk_ps < 8); |
|---|
| 145 | + if (c == 0) |
|---|
| 146 | + return false; |
|---|
| 186 | 147 | |
|---|
| 187 | | - return 0; |
|---|
| 188 | | -} |
|---|
| 189 | | - |
|---|
| 190 | | -static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc, |
|---|
| 191 | | - unsigned long period_ns, |
|---|
| 192 | | - enum fsl_pwm_clk index) |
|---|
| 193 | | -{ |
|---|
| 194 | | - int ret; |
|---|
| 195 | | - |
|---|
| 196 | | - ret = fsl_pwm_calculate_default_ps(fpc, index); |
|---|
| 197 | | - if (ret) { |
|---|
| 198 | | - dev_err(fpc->chip.dev, |
|---|
| 199 | | - "failed to calculate default prescaler: %d\n", |
|---|
| 200 | | - ret); |
|---|
| 201 | | - return 0; |
|---|
| 148 | + for (ps = 0; ps < 8 ; ++ps, c >>= 1) { |
|---|
| 149 | + if (c <= 0x10000) { |
|---|
| 150 | + periodcfg->clk_select = index; |
|---|
| 151 | + periodcfg->clk_ps = ps; |
|---|
| 152 | + periodcfg->mod_period = c - 1; |
|---|
| 153 | + return true; |
|---|
| 154 | + } |
|---|
| 202 | 155 | } |
|---|
| 203 | | - |
|---|
| 204 | | - return fsl_pwm_calculate_cycles(fpc, period_ns); |
|---|
| 156 | + return false; |
|---|
| 205 | 157 | } |
|---|
| 206 | 158 | |
|---|
| 207 | | -static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, |
|---|
| 208 | | - unsigned long period_ns) |
|---|
| 159 | +static bool fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, |
|---|
| 160 | + unsigned int period_ns, |
|---|
| 161 | + struct fsl_pwm_periodcfg *periodcfg) |
|---|
| 209 | 162 | { |
|---|
| 210 | 163 | enum fsl_pwm_clk m0, m1; |
|---|
| 211 | | - unsigned long fix_rate, ext_rate, cycles; |
|---|
| 164 | + unsigned long fix_rate, ext_rate; |
|---|
| 165 | + bool ret; |
|---|
| 212 | 166 | |
|---|
| 213 | | - cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, |
|---|
| 214 | | - FSL_PWM_CLK_SYS); |
|---|
| 215 | | - if (cycles) { |
|---|
| 216 | | - fpc->cnt_select = FSL_PWM_CLK_SYS; |
|---|
| 217 | | - return cycles; |
|---|
| 218 | | - } |
|---|
| 167 | + ret = fsl_pwm_calculate_period_clk(fpc, period_ns, FSL_PWM_CLK_SYS, |
|---|
| 168 | + periodcfg); |
|---|
| 169 | + if (ret) |
|---|
| 170 | + return true; |
|---|
| 219 | 171 | |
|---|
| 220 | 172 | fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); |
|---|
| 221 | 173 | ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); |
|---|
| .. | .. |
|---|
| 228 | 180 | m1 = FSL_PWM_CLK_FIX; |
|---|
| 229 | 181 | } |
|---|
| 230 | 182 | |
|---|
| 231 | | - cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0); |
|---|
| 232 | | - if (cycles) { |
|---|
| 233 | | - fpc->cnt_select = m0; |
|---|
| 234 | | - return cycles; |
|---|
| 235 | | - } |
|---|
| 183 | + ret = fsl_pwm_calculate_period_clk(fpc, period_ns, m0, periodcfg); |
|---|
| 184 | + if (ret) |
|---|
| 185 | + return true; |
|---|
| 236 | 186 | |
|---|
| 237 | | - fpc->cnt_select = m1; |
|---|
| 238 | | - |
|---|
| 239 | | - return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1); |
|---|
| 187 | + return fsl_pwm_calculate_period_clk(fpc, period_ns, m1, periodcfg); |
|---|
| 240 | 188 | } |
|---|
| 241 | 189 | |
|---|
| 242 | | -static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, |
|---|
| 243 | | - unsigned long period_ns, |
|---|
| 244 | | - unsigned long duty_ns) |
|---|
| 190 | +static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, |
|---|
| 191 | + unsigned int duty_ns) |
|---|
| 245 | 192 | { |
|---|
| 246 | 193 | unsigned long long duty; |
|---|
| 247 | | - u32 val; |
|---|
| 248 | 194 | |
|---|
| 249 | | - regmap_read(fpc->regmap, FTM_MOD, &val); |
|---|
| 250 | | - duty = (unsigned long long)duty_ns * (val + 1); |
|---|
| 195 | + unsigned int period = fpc->period.mod_period + 1; |
|---|
| 196 | + unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period); |
|---|
| 197 | + |
|---|
| 198 | + duty = (unsigned long long)duty_ns * period; |
|---|
| 251 | 199 | do_div(duty, period_ns); |
|---|
| 252 | 200 | |
|---|
| 253 | | - return (unsigned long)duty; |
|---|
| 201 | + return (unsigned int)duty; |
|---|
| 254 | 202 | } |
|---|
| 255 | 203 | |
|---|
| 256 | | -static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
|---|
| 257 | | - int duty_ns, int period_ns) |
|---|
| 204 | +static bool fsl_pwm_is_any_pwm_enabled(struct fsl_pwm_chip *fpc, |
|---|
| 205 | + struct pwm_device *pwm) |
|---|
| 258 | 206 | { |
|---|
| 259 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
|---|
| 260 | | - u32 period, duty; |
|---|
| 207 | + u32 val; |
|---|
| 261 | 208 | |
|---|
| 262 | | - mutex_lock(&fpc->lock); |
|---|
| 209 | + regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
|---|
| 210 | + if (~val & 0xFF) |
|---|
| 211 | + return true; |
|---|
| 212 | + else |
|---|
| 213 | + return false; |
|---|
| 214 | +} |
|---|
| 263 | 215 | |
|---|
| 216 | +static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc, |
|---|
| 217 | + struct pwm_device *pwm) |
|---|
| 218 | +{ |
|---|
| 219 | + u32 val; |
|---|
| 220 | + |
|---|
| 221 | + regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
|---|
| 222 | + if (~(val | BIT(pwm->hwpwm)) & 0xFF) |
|---|
| 223 | + return true; |
|---|
| 224 | + else |
|---|
| 225 | + return false; |
|---|
| 226 | +} |
|---|
| 227 | + |
|---|
| 228 | +static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, |
|---|
| 229 | + struct pwm_device *pwm, |
|---|
| 230 | + const struct pwm_state *newstate) |
|---|
| 231 | +{ |
|---|
| 232 | + unsigned int duty; |
|---|
| 233 | + u32 reg_polarity; |
|---|
| 234 | + |
|---|
| 235 | + struct fsl_pwm_periodcfg periodcfg; |
|---|
| 236 | + bool do_write_period = false; |
|---|
| 237 | + |
|---|
| 238 | + if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) { |
|---|
| 239 | + dev_err(fpc->chip.dev, "failed to calculate new period\n"); |
|---|
| 240 | + return -EINVAL; |
|---|
| 241 | + } |
|---|
| 242 | + |
|---|
| 243 | + if (!fsl_pwm_is_any_pwm_enabled(fpc, pwm)) |
|---|
| 244 | + do_write_period = true; |
|---|
| 264 | 245 | /* |
|---|
| 265 | 246 | * The Freescale FTM controller supports only a single period for |
|---|
| 266 | | - * all PWM channels, therefore incompatible changes need to be |
|---|
| 267 | | - * refused. |
|---|
| 247 | + * all PWM channels, therefore verify if the newly computed period |
|---|
| 248 | + * is different than the current period being used. In such case |
|---|
| 249 | + * we allow to change the period only if no other pwm is running. |
|---|
| 268 | 250 | */ |
|---|
| 269 | | - if (fpc->period_ns && fpc->period_ns != period_ns) { |
|---|
| 270 | | - dev_err(fpc->chip.dev, |
|---|
| 271 | | - "conflicting period requested for PWM %u\n", |
|---|
| 272 | | - pwm->hwpwm); |
|---|
| 273 | | - mutex_unlock(&fpc->lock); |
|---|
| 274 | | - return -EBUSY; |
|---|
| 275 | | - } |
|---|
| 276 | | - |
|---|
| 277 | | - if (!fpc->period_ns && duty_ns) { |
|---|
| 278 | | - period = fsl_pwm_calculate_period(fpc, period_ns); |
|---|
| 279 | | - if (!period) { |
|---|
| 280 | | - dev_err(fpc->chip.dev, "failed to calculate period\n"); |
|---|
| 281 | | - mutex_unlock(&fpc->lock); |
|---|
| 282 | | - return -EINVAL; |
|---|
| 251 | + else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) { |
|---|
| 252 | + if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) { |
|---|
| 253 | + dev_err(fpc->chip.dev, |
|---|
| 254 | + "Cannot change period for PWM %u, disable other PWMs first\n", |
|---|
| 255 | + pwm->hwpwm); |
|---|
| 256 | + return -EBUSY; |
|---|
| 283 | 257 | } |
|---|
| 258 | + if (fpc->period.clk_select != periodcfg.clk_select) { |
|---|
| 259 | + int ret; |
|---|
| 260 | + enum fsl_pwm_clk oldclk = fpc->period.clk_select; |
|---|
| 261 | + enum fsl_pwm_clk newclk = periodcfg.clk_select; |
|---|
| 284 | 262 | |
|---|
| 285 | | - regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
|---|
| 286 | | - fpc->clk_ps); |
|---|
| 287 | | - regmap_write(fpc->regmap, FTM_MOD, period - 1); |
|---|
| 288 | | - |
|---|
| 289 | | - fpc->period_ns = period_ns; |
|---|
| 263 | + ret = clk_prepare_enable(fpc->clk[newclk]); |
|---|
| 264 | + if (ret) |
|---|
| 265 | + return ret; |
|---|
| 266 | + clk_disable_unprepare(fpc->clk[oldclk]); |
|---|
| 267 | + } |
|---|
| 268 | + do_write_period = true; |
|---|
| 290 | 269 | } |
|---|
| 291 | 270 | |
|---|
| 292 | | - mutex_unlock(&fpc->lock); |
|---|
| 271 | + ftm_clear_write_protection(fpc); |
|---|
| 293 | 272 | |
|---|
| 294 | | - duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); |
|---|
| 273 | + if (do_write_period) { |
|---|
| 274 | + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
|---|
| 275 | + FTM_SC_CLK(periodcfg.clk_select)); |
|---|
| 276 | + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
|---|
| 277 | + periodcfg.clk_ps); |
|---|
| 278 | + regmap_write(fpc->regmap, FTM_MOD, periodcfg.mod_period); |
|---|
| 279 | + |
|---|
| 280 | + fpc->period = periodcfg; |
|---|
| 281 | + } |
|---|
| 282 | + |
|---|
| 283 | + duty = fsl_pwm_calculate_duty(fpc, newstate->duty_cycle); |
|---|
| 295 | 284 | |
|---|
| 296 | 285 | regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), |
|---|
| 297 | 286 | FTM_CSC_MSB | FTM_CSC_ELSB); |
|---|
| 298 | 287 | regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); |
|---|
| 299 | 288 | |
|---|
| 289 | + reg_polarity = 0; |
|---|
| 290 | + if (newstate->polarity == PWM_POLARITY_INVERSED) |
|---|
| 291 | + reg_polarity = BIT(pwm->hwpwm); |
|---|
| 292 | + |
|---|
| 293 | + regmap_update_bits(fpc->regmap, FTM_POL, BIT(pwm->hwpwm), reg_polarity); |
|---|
| 294 | + |
|---|
| 295 | + ftm_set_write_protection(fpc); |
|---|
| 296 | + |
|---|
| 300 | 297 | return 0; |
|---|
| 301 | 298 | } |
|---|
| 302 | 299 | |
|---|
| 303 | | -static int fsl_pwm_set_polarity(struct pwm_chip *chip, |
|---|
| 304 | | - struct pwm_device *pwm, |
|---|
| 305 | | - enum pwm_polarity polarity) |
|---|
| 300 | +static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
|---|
| 301 | + const struct pwm_state *newstate) |
|---|
| 306 | 302 | { |
|---|
| 307 | 303 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
|---|
| 308 | | - u32 val; |
|---|
| 304 | + struct pwm_state *oldstate = &pwm->state; |
|---|
| 305 | + int ret = 0; |
|---|
| 309 | 306 | |
|---|
| 310 | | - regmap_read(fpc->regmap, FTM_POL, &val); |
|---|
| 307 | + /* |
|---|
| 308 | + * oldstate to newstate : action |
|---|
| 309 | + * |
|---|
| 310 | + * disabled to disabled : ignore |
|---|
| 311 | + * enabled to disabled : disable |
|---|
| 312 | + * enabled to enabled : update settings |
|---|
| 313 | + * disabled to enabled : update settings + enable |
|---|
| 314 | + */ |
|---|
| 311 | 315 | |
|---|
| 312 | | - if (polarity == PWM_POLARITY_INVERSED) |
|---|
| 313 | | - val |= BIT(pwm->hwpwm); |
|---|
| 314 | | - else |
|---|
| 315 | | - val &= ~BIT(pwm->hwpwm); |
|---|
| 316 | + mutex_lock(&fpc->lock); |
|---|
| 316 | 317 | |
|---|
| 317 | | - regmap_write(fpc->regmap, FTM_POL, val); |
|---|
| 318 | + if (!newstate->enabled) { |
|---|
| 319 | + if (oldstate->enabled) { |
|---|
| 320 | + regmap_update_bits(fpc->regmap, FTM_OUTMASK, |
|---|
| 321 | + BIT(pwm->hwpwm), BIT(pwm->hwpwm)); |
|---|
| 322 | + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 323 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
|---|
| 324 | + } |
|---|
| 318 | 325 | |
|---|
| 319 | | - return 0; |
|---|
| 320 | | -} |
|---|
| 321 | | - |
|---|
| 322 | | -static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) |
|---|
| 323 | | -{ |
|---|
| 324 | | - int ret; |
|---|
| 325 | | - |
|---|
| 326 | | - /* select counter clock source */ |
|---|
| 327 | | - regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
|---|
| 328 | | - FTM_SC_CLK(fpc->cnt_select)); |
|---|
| 329 | | - |
|---|
| 330 | | - ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
|---|
| 331 | | - if (ret) |
|---|
| 332 | | - return ret; |
|---|
| 333 | | - |
|---|
| 334 | | - ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 335 | | - if (ret) { |
|---|
| 336 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
|---|
| 337 | | - return ret; |
|---|
| 326 | + goto end_mutex; |
|---|
| 338 | 327 | } |
|---|
| 339 | 328 | |
|---|
| 340 | | - return 0; |
|---|
| 341 | | -} |
|---|
| 329 | + ret = fsl_pwm_apply_config(fpc, pwm, newstate); |
|---|
| 330 | + if (ret) |
|---|
| 331 | + goto end_mutex; |
|---|
| 342 | 332 | |
|---|
| 343 | | -static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
|---|
| 344 | | -{ |
|---|
| 345 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
|---|
| 346 | | - int ret; |
|---|
| 333 | + /* check if need to enable */ |
|---|
| 334 | + if (!oldstate->enabled) { |
|---|
| 335 | + ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); |
|---|
| 336 | + if (ret) |
|---|
| 337 | + goto end_mutex; |
|---|
| 347 | 338 | |
|---|
| 348 | | - mutex_lock(&fpc->lock); |
|---|
| 349 | | - regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); |
|---|
| 339 | + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 340 | + if (ret) { |
|---|
| 341 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
|---|
| 342 | + goto end_mutex; |
|---|
| 343 | + } |
|---|
| 350 | 344 | |
|---|
| 351 | | - ret = fsl_counter_clock_enable(fpc); |
|---|
| 345 | + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
|---|
| 346 | + 0); |
|---|
| 347 | + } |
|---|
| 348 | + |
|---|
| 349 | +end_mutex: |
|---|
| 352 | 350 | mutex_unlock(&fpc->lock); |
|---|
| 353 | | - |
|---|
| 354 | 351 | return ret; |
|---|
| 355 | | -} |
|---|
| 356 | | - |
|---|
| 357 | | -static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
|---|
| 358 | | -{ |
|---|
| 359 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
|---|
| 360 | | - u32 val; |
|---|
| 361 | | - |
|---|
| 362 | | - mutex_lock(&fpc->lock); |
|---|
| 363 | | - regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
|---|
| 364 | | - BIT(pwm->hwpwm)); |
|---|
| 365 | | - |
|---|
| 366 | | - clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 367 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
|---|
| 368 | | - |
|---|
| 369 | | - regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
|---|
| 370 | | - if ((val & 0xFF) == 0xFF) |
|---|
| 371 | | - fpc->period_ns = 0; |
|---|
| 372 | | - |
|---|
| 373 | | - mutex_unlock(&fpc->lock); |
|---|
| 374 | 352 | } |
|---|
| 375 | 353 | |
|---|
| 376 | 354 | static const struct pwm_ops fsl_pwm_ops = { |
|---|
| 377 | 355 | .request = fsl_pwm_request, |
|---|
| 378 | 356 | .free = fsl_pwm_free, |
|---|
| 379 | | - .config = fsl_pwm_config, |
|---|
| 380 | | - .set_polarity = fsl_pwm_set_polarity, |
|---|
| 381 | | - .enable = fsl_pwm_enable, |
|---|
| 382 | | - .disable = fsl_pwm_disable, |
|---|
| 357 | + .apply = fsl_pwm_apply, |
|---|
| 383 | 358 | .owner = THIS_MODULE, |
|---|
| 384 | 359 | }; |
|---|
| 385 | 360 | |
|---|
| .. | .. |
|---|
| 403 | 378 | static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) |
|---|
| 404 | 379 | { |
|---|
| 405 | 380 | switch (reg) { |
|---|
| 381 | + case FTM_FMS: |
|---|
| 382 | + case FTM_MODE: |
|---|
| 406 | 383 | case FTM_CNT: |
|---|
| 407 | 384 | return true; |
|---|
| 408 | 385 | } |
|---|
| .. | .. |
|---|
| 520 | 497 | continue; |
|---|
| 521 | 498 | |
|---|
| 522 | 499 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 523 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
|---|
| 500 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
|---|
| 524 | 501 | } |
|---|
| 525 | 502 | |
|---|
| 526 | 503 | return 0; |
|---|
| .. | .. |
|---|
| 542 | 519 | if (!pwm_is_enabled(pwm)) |
|---|
| 543 | 520 | continue; |
|---|
| 544 | 521 | |
|---|
| 545 | | - clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
|---|
| 522 | + clk_prepare_enable(fpc->clk[fpc->period.clk_select]); |
|---|
| 546 | 523 | clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
|---|
| 547 | 524 | } |
|---|
| 548 | 525 | |
|---|