drivers/pwm/pwm-sifive.c
Source file repositories/reference/linux-study-clean/drivers/pwm/pwm-sifive.c
File Facts
- System
- Linux kernel
- Corpus path
drivers/pwm/pwm-sifive.c- Extension
.c- Size
- 10678 bytes
- Lines
- 375
- 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.
- Uses kernel synchronization; read lock ordering, sleepability, and interrupt context assumptions before translating.
- Defines or uses C structs; map object ownership, embedded links, reference counts, and lock ownership.
Dependency Surface
linux/clk.hlinux/io.hlinux/mod_devicetable.hlinux/module.hlinux/platform_device.hlinux/pwm.hlinux/slab.hlinux/bitfield.h
Detected Declarations
struct pwm_sifive_ddatafunction pwm_sifive_requestfunction pwm_sifive_freefunction pwm_sifive_update_clockfunction pwm_sifive_get_statefunction pwm_sifive_applyfunction pwm_sifive_clock_notifierfunction pwm_sifive_probefunction pwm_sifive_remove
Annotated Snippet
struct pwm_sifive_ddata {
struct device *parent;
struct mutex lock; /* lock to protect user_count and approx_period */
struct notifier_block notifier;
struct clk *clk;
void __iomem *regs;
unsigned int real_period;
unsigned int approx_period;
int user_count;
};
static inline
struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *chip)
{
return pwmchip_get_drvdata(chip);
}
static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
mutex_lock(&ddata->lock);
ddata->user_count++;
mutex_unlock(&ddata->lock);
return 0;
}
static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
mutex_lock(&ddata->lock);
ddata->user_count--;
mutex_unlock(&ddata->lock);
}
/* Called holding ddata->lock */
static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
unsigned long rate)
{
unsigned long long num;
unsigned long scale_pow;
int scale;
u32 val;
/*
* The PWM unit is used with pwmzerocmp=0, so the only way to modify the
* period length is using pwmscale which provides the number of bits the
* counter is shifted before being feed to the comparators. A period
* lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
* (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
*/
scale_pow = div64_ul(ddata->approx_period * (u64)rate, NSEC_PER_SEC);
scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
val = PWM_SIFIVE_PWMCFG_EN_ALWAYS |
FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
writel(val, ddata->regs + PWM_SIFIVE_PWMCFG);
/* As scale <= 15 the shift operation cannot overflow. */
num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
ddata->real_period = DIV_ROUND_UP_ULL(num, rate);
dev_dbg(ddata->parent,
"New real_period = %u ns\n", ddata->real_period);
}
static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
u32 duty, val, inactive;
inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
/*
* PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty.
* Here, 'inactive' is the low time and we compute duty as max_count - inactive.
*/
duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive;
state->enabled = duty > 0;
val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
if (!(val & PWM_SIFIVE_PWMCFG_EN_ALWAYS))
state->enabled = false;
state->period = ddata->real_period;
state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period,
(1U << PWM_SIFIVE_CMPWIDTH));
state->polarity = PWM_POLARITY_NORMAL;
Annotation
- Immediate include surface: `linux/clk.h`, `linux/io.h`, `linux/mod_devicetable.h`, `linux/module.h`, `linux/platform_device.h`, `linux/pwm.h`, `linux/slab.h`, `linux/bitfield.h`.
- Detected declarations: `struct pwm_sifive_ddata`, `function pwm_sifive_request`, `function pwm_sifive_free`, `function pwm_sifive_update_clock`, `function pwm_sifive_get_state`, `function pwm_sifive_apply`, `function pwm_sifive_clock_notifier`, `function pwm_sifive_probe`, `function pwm_sifive_remove`.
- Atlas domain: Driver Families / drivers/pwm.
- Implementation status: source implementation candidate.
- Synchronization appears in or near this file; preserve lock ordering, sleepability, and interrupt-context constraints.
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.