diff options
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/pwm-sti.c | 140 |
1 files changed, 96 insertions, 44 deletions
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 76346e32f403..2d3260571b5a 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c | |||
@@ -58,6 +58,7 @@ struct sti_pwm_chip { | |||
58 | struct regmap_field *pwm_int_en; | 58 | struct regmap_field *pwm_int_en; |
59 | unsigned long *pwm_periods; | 59 | unsigned long *pwm_periods; |
60 | struct pwm_chip chip; | 60 | struct pwm_chip chip; |
61 | struct pwm_device *cur; | ||
61 | void __iomem *mmio; | 62 | void __iomem *mmio; |
62 | }; | 63 | }; |
63 | 64 | ||
@@ -99,6 +100,24 @@ static void sti_pwm_calc_periods(struct sti_pwm_chip *pc) | |||
99 | } | 100 | } |
100 | } | 101 | } |
101 | 102 | ||
103 | /* Calculate the number of PWM devices configured with a period. */ | ||
104 | static unsigned int sti_pwm_count_configured(struct pwm_chip *chip) | ||
105 | { | ||
106 | struct pwm_device *pwm; | ||
107 | unsigned int ncfg = 0; | ||
108 | unsigned int i; | ||
109 | |||
110 | for (i = 0; i < chip->npwm; i++) { | ||
111 | pwm = &chip->pwms[i]; | ||
112 | if (test_bit(PWMF_REQUESTED, &pwm->flags)) { | ||
113 | if (pwm_get_period(pwm)) | ||
114 | ncfg++; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | return ncfg; | ||
119 | } | ||
120 | |||
102 | static int sti_pwm_cmp_periods(const void *key, const void *elt) | 121 | static int sti_pwm_cmp_periods(const void *key, const void *elt) |
103 | { | 122 | { |
104 | unsigned long i = *(unsigned long *)key; | 123 | unsigned long i = *(unsigned long *)key; |
@@ -124,57 +143,90 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
124 | { | 143 | { |
125 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | 144 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); |
126 | struct sti_pwm_compat_data *cdata = pc->cdata; | 145 | struct sti_pwm_compat_data *cdata = pc->cdata; |
146 | struct pwm_device *cur = pc->cur; | ||
127 | struct device *dev = pc->dev; | 147 | struct device *dev = pc->dev; |
128 | unsigned int prescale, pwmvalx; | 148 | unsigned int prescale = 0, pwmvalx; |
129 | unsigned long *found; | 149 | unsigned long *found; |
130 | int ret; | 150 | int ret; |
131 | 151 | unsigned int ncfg; | |
132 | /* | 152 | bool period_same = false; |
133 | * Search for matching period value. The corresponding index is our | 153 | |
134 | * prescale value | 154 | ncfg = sti_pwm_count_configured(chip); |
155 | if (ncfg) | ||
156 | period_same = (period_ns == pwm_get_period(cur)); | ||
157 | |||
158 | /* Allow configuration changes if one of the | ||
159 | * following conditions satisfy. | ||
160 | * 1. No channels have been configured. | ||
161 | * 2. Only one channel has been configured and the new request | ||
162 | * is for the same channel. | ||
163 | * 3. Only one channel has been configured and the new request is | ||
164 | * for a new channel and period of the new channel is same as | ||
165 | * the current configured period. | ||
166 | * 4. More than one channels are configured and period of the new | ||
167 | * requestis the same as the current period. | ||
135 | */ | 168 | */ |
136 | found = bsearch(&period_ns, &pc->pwm_periods[0], | 169 | if (!ncfg || |
137 | cdata->max_prescale + 1, sizeof(unsigned long), | 170 | ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) || |
138 | sti_pwm_cmp_periods); | 171 | ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) || |
139 | if (!found) { | 172 | ((ncfg > 1) && period_same)) { |
140 | dev_err(dev, "failed to find matching period\n"); | 173 | /* Enable clock before writing to PWM registers. */ |
174 | ret = clk_enable(pc->clk); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | if (!period_same) { | ||
179 | /* | ||
180 | * Search for matching period value. | ||
181 | * The corresponding index is our prescale value. | ||
182 | */ | ||
183 | found = bsearch(&period_ns, &pc->pwm_periods[0], | ||
184 | cdata->max_prescale + 1, | ||
185 | sizeof(unsigned long), | ||
186 | sti_pwm_cmp_periods); | ||
187 | if (!found) { | ||
188 | dev_err(dev, | ||
189 | "failed to find matching period\n"); | ||
190 | ret = -EINVAL; | ||
191 | goto clk_dis; | ||
192 | } | ||
193 | prescale = found - &pc->pwm_periods[0]; | ||
194 | |||
195 | ret = | ||
196 | regmap_field_write(pc->prescale_low, | ||
197 | prescale & PWM_PRESCALE_LOW_MASK); | ||
198 | if (ret) | ||
199 | goto clk_dis; | ||
200 | |||
201 | ret = | ||
202 | regmap_field_write(pc->prescale_high, | ||
203 | (prescale & PWM_PRESCALE_HIGH_MASK) >> 4); | ||
204 | if (ret) | ||
205 | goto clk_dis; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * When PWMVal == 0, PWM pulse = 1 local clock cycle. | ||
210 | * When PWMVal == max_pwm_count, | ||
211 | * PWM pulse = (max_pwm_count + 1) local cycles, | ||
212 | * that is continuous pulse: signal never goes low. | ||
213 | */ | ||
214 | pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns; | ||
215 | |||
216 | ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx); | ||
217 | if (ret) | ||
218 | goto clk_dis; | ||
219 | |||
220 | ret = regmap_field_write(pc->pwm_int_en, 0); | ||
221 | |||
222 | pc->cur = pwm; | ||
223 | |||
224 | dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", | ||
225 | prescale, period_ns, duty_ns, pwmvalx); | ||
226 | } else { | ||
141 | return -EINVAL; | 227 | return -EINVAL; |
142 | } | 228 | } |
143 | 229 | ||
144 | prescale = found - &pc->pwm_periods[0]; | ||
145 | |||
146 | /* | ||
147 | * When PWMVal == 0, PWM pulse = 1 local clock cycle. | ||
148 | * When PWMVal == max_pwm_count, | ||
149 | * PWM pulse = (max_pwm_count + 1) local cycles, | ||
150 | * that is continuous pulse: signal never goes low. | ||
151 | */ | ||
152 | pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns; | ||
153 | |||
154 | dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", | ||
155 | prescale, period_ns, duty_ns, pwmvalx); | ||
156 | |||
157 | /* Enable clock before writing to PWM registers */ | ||
158 | ret = clk_enable(pc->clk); | ||
159 | if (ret) | ||
160 | return ret; | ||
161 | |||
162 | ret = regmap_field_write(pc->prescale_low, | ||
163 | prescale & PWM_PRESCALE_LOW_MASK); | ||
164 | if (ret) | ||
165 | goto clk_dis; | ||
166 | |||
167 | ret = regmap_field_write(pc->prescale_high, | ||
168 | (prescale & PWM_PRESCALE_HIGH_MASK) >> 4); | ||
169 | if (ret) | ||
170 | goto clk_dis; | ||
171 | |||
172 | ret = regmap_write(pc->regmap, STI_PWMVAL(pwm->hwpwm), pwmvalx); | ||
173 | if (ret) | ||
174 | goto clk_dis; | ||
175 | |||
176 | ret = regmap_field_write(pc->pwm_int_en, 0); | ||
177 | |||
178 | clk_dis: | 230 | clk_dis: |
179 | clk_disable(pc->clk); | 231 | clk_disable(pc->clk); |
180 | return ret; | 232 | return ret; |