aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm
diff options
context:
space:
mode:
authorAjit Pal Singh <ajitpal.singh@st.com>2014-07-14 10:33:30 -0400
committerThierry Reding <thierry.reding@gmail.com>2014-08-08 07:12:45 -0400
commit5165166e8a1551fb80afa5fdaad6f2ea34d92285 (patch)
tree353cdc0d267e7f438c5023706827c773194feb44 /drivers/pwm
parentbf9cc80b6c3ec2754b570d377ed7f7e2ec96868a (diff)
pwm: sti: Ensure same period values for all channels
ST PWM IP shares the same clock prescaler across all the PWM channels. Hence configuration requests which change the period will affect all the channels. Do not allow period changes which will stomp period settings of the already configured channels. Signed-off-by: Ajit Pal Singh <ajitpal.singh@st.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/pwm-sti.c140
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. */
104static 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
102static int sti_pwm_cmp_periods(const void *key, const void *elt) 121static 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
178clk_dis: 230clk_dis:
179 clk_disable(pc->clk); 231 clk_disable(pc->clk);
180 return ret; 232 return ret;