drivers/hwmon/axi-fan-control.c

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

File Facts

System
Linux kernel
Corpus path
drivers/hwmon/axi-fan-control.c
Extension
.c
Size
15276 bytes
Lines
539
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 axi_fan_control_data {
	void __iomem *base;
	struct device *hdev;
	unsigned long clk_rate;
	int irq;
	/* pulses per revolution */
	u32 ppr;
	bool hw_pwm_req;
	bool update_tacho_params;
	u8 fan_fault;
};

static inline void axi_iowrite(const u32 val, const u32 reg,
			       const struct axi_fan_control_data *ctl)
{
	iowrite32(val, ctl->base + reg);
}

static inline u32 axi_ioread(const u32 reg,
			     const struct axi_fan_control_data *ctl)
{
	return ioread32(ctl->base + reg);
}

/*
 * The core calculates the temperature as:
 *	T = /raw * 509.3140064 / 65535) - 280.2308787
 */
static ssize_t axi_fan_control_show(struct device *dev, struct device_attribute *da, char *buf)
{
	struct axi_fan_control_data *ctl = dev_get_drvdata(dev);
	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
	u32 temp = axi_ioread(attr->index, ctl);

	temp = DIV_ROUND_CLOSEST_ULL(temp * 509314ULL, 65535) - 280230;

	return sysfs_emit(buf, "%u\n", temp);
}

static ssize_t axi_fan_control_store(struct device *dev, struct device_attribute *da,
				     const char *buf, size_t count)
{
	struct axi_fan_control_data *ctl = dev_get_drvdata(dev);
	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
	u32 temp;
	int ret;

	ret = kstrtou32(buf, 10, &temp);
	if (ret)
		return ret;

	temp = DIV_ROUND_CLOSEST_ULL((temp + 280230) * 65535ULL, 509314);
	axi_iowrite(temp, attr->index, ctl);

	return count;
}

static long axi_fan_control_get_pwm_duty(const struct axi_fan_control_data *ctl)
{
	u32 pwm_width = axi_ioread(ADI_REG_PWM_WIDTH, ctl);
	u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl);
	/*
	 * PWM_PERIOD is a RO register set by the core. It should never be 0.
	 * For now we are trusting the HW...
	 */
	return DIV_ROUND_CLOSEST(pwm_width * SYSFS_PWM_MAX, pwm_period);
}

static int axi_fan_control_set_pwm_duty(const long val,
					struct axi_fan_control_data *ctl)
{
	u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl);
	u32 new_width;
	long __val = clamp_val(val, 0, SYSFS_PWM_MAX);

	new_width = DIV_ROUND_CLOSEST(__val * pwm_period, SYSFS_PWM_MAX);

	axi_iowrite(new_width, ADI_REG_PWM_WIDTH, ctl);

	return 0;
}

static long axi_fan_control_get_fan_rpm(const struct axi_fan_control_data *ctl)
{
	const u32 tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl);

	if (tach == 0)
		/* should we return error, EAGAIN maybe? */
		return 0;
	/*

Annotation

Implementation Notes