drivers/hwmon/pwm-fan.c

Source file repositories/reference/linux-study-clean/drivers/hwmon/pwm-fan.c

File Facts

System
Linux kernel
Corpus path
drivers/hwmon/pwm-fan.c
Extension
.c
Size
17168 bytes
Lines
749
Domain
Driver Families
Bucket
drivers/hwmon
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.

Dependency Surface

Detected Declarations

Annotated Snippet

struct pwm_fan_tach {
	int irq;
	atomic_t pulses;
	unsigned int rpm;
};

enum pwm_fan_enable_mode {
	pwm_off_reg_off,
	pwm_disable_reg_enable,
	pwm_enable_reg_enable,
	pwm_disable_reg_disable,
};

struct pwm_fan_ctx {
	struct device *dev;

	struct mutex lock;
	struct pwm_device *pwm;
	struct pwm_state pwm_state;
	struct regulator *reg_en;
	enum pwm_fan_enable_mode enable_mode;
	bool regulator_enabled;
	bool enabled;

	int tach_count;
	struct pwm_fan_tach *tachs;
	u32 *pulses_per_revolution;
	ktime_t sample_start;
	struct timer_list rpm_timer;

	unsigned int pwm_value;
	unsigned int pwm_fan_state;
	unsigned int pwm_fan_max_state;
	unsigned int *pwm_fan_cooling_levels;
	struct thermal_cooling_device *cdev;

	struct hwmon_chip_info info;
	struct hwmon_channel_info fan_channel;

	u64 pwm_duty_cycle_from_stopped;
	u32 pwm_usec_from_stopped;
	u8 pwm_shutdown;
};

/* This handler assumes self resetting edge triggered interrupt. */
static irqreturn_t pulse_handler(int irq, void *dev_id)
{
	struct pwm_fan_tach *tach = dev_id;

	atomic_inc(&tach->pulses);

	return IRQ_HANDLED;
}

static void sample_timer(struct timer_list *t)
{
	struct pwm_fan_ctx *ctx = timer_container_of(ctx, t, rpm_timer);
	unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
	int i;

	if (delta) {
		for (i = 0; i < ctx->tach_count; i++) {
			struct pwm_fan_tach *tach = &ctx->tachs[i];
			int pulses;

			pulses = atomic_read(&tach->pulses);
			atomic_sub(pulses, &tach->pulses);
			tach->rpm = (unsigned int)(pulses * 1000 * 60) /
				(ctx->pulses_per_revolution[i] * delta);
		}

		ctx->sample_start = ktime_get();
	}

	mod_timer(&ctx->rpm_timer, jiffies + HZ);
}

static void pwm_fan_enable_mode_2_state(int enable_mode,
					struct pwm_state *state,
					bool *enable_regulator)
{
	switch (enable_mode) {
	case pwm_disable_reg_enable:
		/* disable pwm, keep regulator enabled */
		state->enabled = false;
		*enable_regulator = true;
		break;
	case pwm_enable_reg_enable:
		/* keep pwm and regulator enabled */
		state->enabled = true;

Annotation

Implementation Notes