/* * Each channel occupies 4 bits in TCON register, but there is a gap of 4 * bits (one channel) after channel 0, so channels have different numbering * when accessing TCON register. See to_tcon_channel() function. * * In addition, the location of autoreload bit for channel 4 (TCON channel 5) * in its set of bits is 2 as opposed to 3 for other channels.
*/ #define TCON_START(chan) BIT(4 * (chan) + 0) #define TCON_MANUALUPDATE(chan) BIT(4 * (chan) + 1) #define TCON_INVERT(chan) BIT(4 * (chan) + 2) #define _TCON_AUTORELOAD(chan) BIT(4 * (chan) + 3) #define _TCON_AUTORELOAD4(chan) BIT(4 * (chan) + 2) #define TCON_AUTORELOAD(chan) \
((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan))
/** * struct samsung_pwm_channel - private data of PWM channel * @period_ns: current period in nanoseconds programmed to the hardware * @duty_ns: current duty time in nanoseconds programmed to the hardware * @tin_ns: time of one timer tick in nanoseconds with current timer rate
*/ struct samsung_pwm_channel {
u32 period_ns;
u32 duty_ns;
u32 tin_ns;
};
/** * struct samsung_pwm_chip - private data of PWM chip * @variant: local copy of hardware variant data * @inverter_mask: inverter status for all channels - one bit per channel * @disabled_mask: disabled status for all channels - one bit per channel * @base: base address of mapped PWM registers * @base_clk: base clock used to drive the timers * @tclk0: external clock 0 (can be ERR_PTR if not present) * @tclk1: external clock 1 (can be ERR_PTR if not present) * @channel: per channel driver data
*/ struct samsung_pwm_chip { struct samsung_pwm_variant variant;
u8 inverter_mask;
u8 disabled_mask;
#ifndef CONFIG_CLKSRC_SAMSUNG_PWM /* * PWM block is shared between pwm-samsung and samsung_pwm_timer drivers * and some registers need access synchronization. If both drivers are * compiled in, the spinlock is defined in the clocksource driver, * otherwise following definition is used. * * Currently we do not need any more complex synchronization method * because all the supported SoCs contain only one instance of the PWM * IP. Should this change, both drivers will need to be modified to * properly synchronize accesses to particular instances.
*/ static DEFINE_SPINLOCK(samsung_pwm_lock); #endif
if (!pwm_samsung_is_tdiv(our_chip, chan)) {
clk = (chan < 2) ? our_chip->tclk0 : our_chip->tclk1; if (!IS_ERR(clk)) {
rate = clk_get_rate(clk); if (rate) return rate;
}
dev_warn(pwmchip_parent(chip), "tclk of PWM %d is inoperational, using tdiv\n", chan);
}
rate = pwm_samsung_get_tin_rate(our_chip, chan);
dev_dbg(pwmchip_parent(chip), "tin parent at %lu\n", rate);
/* * Compare minimum PWM frequency that can be achieved with possible * divider settings and choose the lowest divisor that can generate * frequencies lower than requested.
*/ if (variant->bits < 32) { /* Only for s3c24xx */ for (div = variant->div_base; div < 4; ++div) if ((rate >> (variant->bits + div)) < freq) break;
} else { /* * Other variants have enough counter bits to generate any * requested rate, so no need to check higher divisors.
*/
div = variant->div_base;
}
/* * In case the PWM is at 100% duty cycle, force a manual * update to prevent the signal from staying high.
*/ if (readl(our_chip->base + REG_TCMPB(pwm->hwpwm)) == (u32)-1U)
__pwm_samsung_manual_update(our_chip, pwm);
/* We need tick count for calculation, not last tick. */
++tcnt;
/* Check to see if we are changing the clock rate of the PWM. */ if (chan->period_ns != period_ns || force_period) { unsignedlong tin_rate;
u32 period;
/* * In case the PWM is currently at 100% duty cycle, force a manual * update to prevent the signal staying high if the PWM is disabled * shortly afer this update (before it autoreloaded the new values).
*/ if (oldtcmp == (u32) -1) {
dev_dbg(pwmchip_parent(chip), "Forcing manual update");
pwm_samsung_manual_update(our_chip, pwm);
}
if (state->polarity != pwm->state.polarity) { if (enabled) {
pwm_samsung_disable(chip, pwm);
enabled = false;
}
err = pwm_samsung_set_polarity(chip, pwm, state->polarity); if (err) return err;
}
if (!state->enabled) { if (enabled)
pwm_samsung_disable(chip, pwm);
return 0;
}
/* * We currently avoid using 64bit arithmetic by using the * fact that anything faster than 1Hz is easily representable * by 32bits.
*/ if (state->period > NSEC_PER_SEC) return -ERANGE;
err = pwm_samsung_config(chip, pwm, state->duty_cycle, state->period); if (err) return err;
if (!pwm->state.enabled)
err = pwm_samsung_enable(chip, pwm);
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
ret = pwm_samsung_parse_dt(chip); if (ret) return ret;
} else { if (!pdev->dev.platform_data) return dev_err_probe(&pdev->dev, -EINVAL, "no platform data specified\n");
our_chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(our_chip->base)) return PTR_ERR(our_chip->base);
our_chip->base_clk = devm_clk_get_enabled(&pdev->dev, "timers"); if (IS_ERR(our_chip->base_clk)) return dev_err_probe(dev, PTR_ERR(our_chip->base_clk), "failed to get timer base clk\n");
for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) if (our_chip->variant.output_mask & BIT(chan))
pwm_samsung_set_invert(our_chip, chan, true);
/* Following clocks are optional. */
our_chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0");
our_chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1");
platform_set_drvdata(pdev, chip);
ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) return dev_err_probe(dev, ret, "failed to register PWM chip\n");
dev_dbg(dev, "base_clk at %lu, tclk0 at %lu, tclk1 at %lu\n",
clk_get_rate(our_chip->base_clk),
!IS_ERR(our_chip->tclk0) ? clk_get_rate(our_chip->tclk0) : 0,
!IS_ERR(our_chip->tclk1) ? clk_get_rate(our_chip->tclk1) : 0);
for (i = 0; i < SAMSUNG_PWM_NUM; i++) { struct pwm_device *pwm = &chip->pwms[i]; struct samsung_pwm_channel *chan = &our_chip->channel[i];
if (!test_bit(PWMF_REQUESTED, &pwm->flags)) continue;
if (our_chip->variant.output_mask & BIT(i))
pwm_samsung_set_invert(our_chip, i,
our_chip->inverter_mask & BIT(i));
if (chan->period_ns) {
__pwm_samsung_config(chip, pwm, chan->duty_ns,
chan->period_ns, true); /* needed to make PWM disable work on Odroid-XU3 */
pwm_samsung_manual_update(our_chip, pwm);
}
if (our_chip->disabled_mask & BIT(i))
pwm_samsung_disable(chip, pwm); else
pwm_samsung_enable(chip, pwm);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.