drivers/pwm/pwm-microchip-core.c
Source file repositories/reference/linux-study-clean/drivers/pwm/pwm-microchip-core.c
File Facts
- System
- Linux kernel
- Corpus path
drivers/pwm/pwm-microchip-core.c- Extension
.c- Size
- 16646 bytes
- Lines
- 491
- Domain
- Driver Families
- Bucket
- drivers/pwm
- Inferred role
- Driver Families: implementation source
- Status
- source implementation candidate
Why This File Exists
Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Defines or uses C structs; map object ownership, embedded links, reference counts, and lock ownership.
Dependency Surface
linux/clk.hlinux/delay.hlinux/err.hlinux/io.hlinux/ktime.hlinux/math.hlinux/module.hlinux/of.hlinux/platform_device.hlinux/pwm.h
Detected Declarations
struct mchp_core_pwm_chipfunction mchp_core_pwm_enablefunction mchp_core_pwm_wait_for_sync_updatefunction mchp_core_pwm_calc_dutyfunction mchp_core_pwm_apply_dutyfunction mchp_core_pwm_calc_periodfunction mchp_core_pwm_apply_lockedfunction mchp_core_pwm_applyfunction mchp_core_pwm_get_statefunction mchp_core_pwm_probe
Annotated Snippet
struct mchp_core_pwm_chip {
struct clk *clk;
void __iomem *base;
ktime_t update_timestamp;
u32 sync_update_mask;
u16 channel_enabled;
};
static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip *chip)
{
return pwmchip_get_drvdata(chip);
}
static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
bool enable, u64 period)
{
struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
u8 channel_enable, reg_offset, shift;
/*
* There are two adjacent 8 bit control regs, the lower reg controls
* 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg
* and if so, offset by the bus width.
*/
reg_offset = MCHPCOREPWM_EN(pwm->hwpwm >> 3);
shift = pwm->hwpwm & 7;
channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset);
channel_enable &= ~(1 << shift);
channel_enable |= (enable << shift);
writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset);
mchp_core_pwm->channel_enabled &= ~BIT(pwm->hwpwm);
mchp_core_pwm->channel_enabled |= enable << pwm->hwpwm;
/*
* The updated values will not appear on the bus until they have been
* applied to the waveform at the beginning of the next period.
* This is a NO-OP if the channel does not have shadow registers.
*/
if (mchp_core_pwm->sync_update_mask & (1 << pwm->hwpwm))
mchp_core_pwm->update_timestamp = ktime_add_ns(ktime_get(), period);
}
static void mchp_core_pwm_wait_for_sync_update(struct mchp_core_pwm_chip *mchp_core_pwm,
unsigned int channel)
{
/*
* If a shadow register is used for this PWM channel, and iff there is
* a pending update to the waveform, we must wait for it to be applied
* before attempting to read its state. Reading the registers yields
* the currently implemented settings & the new ones are only readable
* once the current period has ended.
*/
if (mchp_core_pwm->sync_update_mask & (1 << channel)) {
ktime_t current_time = ktime_get();
s64 remaining_ns;
u32 delay_us;
remaining_ns = ktime_to_ns(ktime_sub(mchp_core_pwm->update_timestamp,
current_time));
/*
* If the update has gone through, don't bother waiting for
* obvious reasons. Otherwise wait around for an appropriate
* amount of time for the update to go through.
*/
if (remaining_ns <= 0)
return;
delay_us = DIV_ROUND_UP_ULL(remaining_ns, NSEC_PER_USEC);
fsleep(delay_us);
}
}
static u64 mchp_core_pwm_calc_duty(const struct pwm_state *state, u64 clk_rate,
u8 prescale, u8 period_steps)
{
u64 duty_steps, tmp;
/*
* Calculate the duty cycle in multiples of the prescaled period:
* duty_steps = duty_in_ns / step_in_ns
* step_in_ns = (prescale * NSEC_PER_SEC) / clk_rate
* The code below is rearranged slightly to only divide once.
*/
tmp = (((u64)prescale) + 1) * NSEC_PER_SEC;
duty_steps = mul_u64_u64_div_u64(state->duty_cycle, clk_rate, tmp);
Annotation
- Immediate include surface: `linux/clk.h`, `linux/delay.h`, `linux/err.h`, `linux/io.h`, `linux/ktime.h`, `linux/math.h`, `linux/module.h`, `linux/of.h`.
- Detected declarations: `struct mchp_core_pwm_chip`, `function mchp_core_pwm_enable`, `function mchp_core_pwm_wait_for_sync_update`, `function mchp_core_pwm_calc_duty`, `function mchp_core_pwm_apply_duty`, `function mchp_core_pwm_calc_period`, `function mchp_core_pwm_apply_locked`, `function mchp_core_pwm_apply`, `function mchp_core_pwm_get_state`, `function mchp_core_pwm_probe`.
- Atlas domain: Driver Families / drivers/pwm.
- Implementation status: source implementation candidate.
Implementation Notes
- This generated page is the file-by-file coverage layer; curated subsystem chapters should link here when they synthesize a multi-file control flow.
- Core OS pages should be promoted from atlas-only to deep-reviewed when they explain data structures, invariants, locking, lifecycle, and C implementation snippets.
- Driver-family pages are intentionally pattern-oriented unless they are part of the selected PCIe/NVMe representative device path.