drivers/pwm/pwm-ipq.c
Source file repositories/reference/linux-study-clean/drivers/pwm/pwm-ipq.c
File Facts
- System
- Linux kernel
- Corpus path
drivers/pwm/pwm-ipq.c- Extension
.c- Size
- 7576 bytes
- Lines
- 265
- 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/module.hlinux/platform_device.hlinux/pwm.hlinux/clk.hlinux/io.hlinux/of.hlinux/math64.hlinux/of_device.hlinux/bitfield.hlinux/units.h
Detected Declarations
struct ipq_pwm_chipfunction ipq_pwm_reg_readfunction ipq_pwm_reg_writefunction ipq_pwm_applyfunction ipq_pwm_get_statefunction ipq_pwm_probe
Annotated Snippet
struct ipq_pwm_chip {
void __iomem *mem;
unsigned long clk_rate;
};
static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip)
{
return pwmchip_get_drvdata(chip);
}
static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg)
{
struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
unsigned int off = 8 * pwm->hwpwm + reg;
return readl(ipq_chip->mem + off);
}
static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg,
unsigned int val)
{
struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
unsigned int off = 8 * pwm->hwpwm + reg;
writel(val, ipq_chip->mem + off);
}
static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
unsigned int pre_div, pwm_div;
u64 period_ns, duty_ns;
unsigned long val = 0;
unsigned long hi_dur;
if (!state->enabled) {
/* clear IPQ_PWM_REG1_ENABLE */
ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE);
return 0;
}
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
/*
* Check the upper and lower bounds for the period as per
* hardware limits
*/
if (state->period < IPQ_PWM_MIN_PERIOD_NS)
return -ERANGE;
period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS);
duty_ns = min(state->duty_cycle, period_ns);
/*
* Pick the maximal value for PWM_DIV that still allows a
* 100% relative duty cycle. This allows a fine grained
* selection of duty cycles.
*/
pwm_div = IPQ_PWM_MAX_DIV - 1;
/*
* although mul_u64_u64_div_u64 returns a u64, in practice it
* won't overflow due to above constraints. Take the max period
* of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV)
* 10^9 * 10^8
* ------------- => which fits well into a 32-bit unsigned int.
* 10^9 * 65,535
*/
pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate,
(u64)NSEC_PER_SEC * (pwm_div + 1));
if (!pre_div)
return -ERANGE;
pre_div -= 1;
if (pre_div > IPQ_PWM_MAX_DIV)
pre_div = IPQ_PWM_MAX_DIV;
/* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */
hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate,
(u64)NSEC_PER_SEC * (pre_div + 1));
val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) |
FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div);
ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val);
val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div);
ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
Annotation
- Immediate include surface: `linux/module.h`, `linux/platform_device.h`, `linux/pwm.h`, `linux/clk.h`, `linux/io.h`, `linux/of.h`, `linux/math64.h`, `linux/of_device.h`.
- Detected declarations: `struct ipq_pwm_chip`, `function ipq_pwm_reg_read`, `function ipq_pwm_reg_write`, `function ipq_pwm_apply`, `function ipq_pwm_get_state`, `function ipq_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.