drivers/hwmon/ltc2945.c

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

File Facts

System
Linux kernel
Corpus path
drivers/hwmon/ltc2945.c
Extension
.c
Size
15597 bytes
Lines
531
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 ltc2945_data {
	struct regmap *regmap;
	u32 shunt_resistor;
};

static inline bool is_power_reg(u8 reg)
{
	return reg < LTC2945_SENSE_H;
}

/* Return the value from the given register in uW, mV, or mA */
static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
{
	struct ltc2945_data *data = dev_get_drvdata(dev);
	struct regmap *regmap = data->regmap;
	u32 shunt_resistor = data->shunt_resistor;
	unsigned int control;
	u8 buf[3];
	long long val;
	int ret;

	ret = regmap_bulk_read(regmap, reg, buf,
			       is_power_reg(reg) ? 3 : 2);
	if (ret < 0)
		return ret;

	if (is_power_reg(reg)) {
		/* 24-bit power */
		val = (buf[0] << 16) + (buf[1] << 8) + buf[2];
	} else {
		/* 12-bit current, voltage */
		val = (buf[0] << 4) + (buf[1] >> 4);
	}

	switch (reg) {
	case LTC2945_POWER_H:
	case LTC2945_MAX_POWER_H:
	case LTC2945_MIN_POWER_H:
	case LTC2945_MAX_POWER_THRES_H:
	case LTC2945_MIN_POWER_THRES_H:
		/*
		 * Convert to uW
		 * Control register bit 0 selects if voltage at SENSE+/VDD
		 * or voltage at ADIN is used to measure power.
		 */
		ret = regmap_read(regmap, LTC2945_CONTROL, &control);
		if (ret < 0)
			return ret;
		if (control & CONTROL_MULT_SELECT) {
			/* 25 mV * 25 uV = 0.625 uV resolution. */
			val *= 625LL;
		} else {
			/* 0.5 mV * 25 uV = 0.0125 uV resolution. */
			val = (val * 25LL) >> 1;
		}
		val *= 1000;
		/* Overflow check: Assuming max 24-bit power, val is at most 53 bits right now. */
		val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor);
		/*
		 * Overflow check: After division, depending on shunt resistor,
		 * val can still be > 32 bits so returning long long makes sense
		 */

		break;
	case LTC2945_VIN_H:
	case LTC2945_MAX_VIN_H:
	case LTC2945_MIN_VIN_H:
	case LTC2945_MAX_VIN_THRES_H:
	case LTC2945_MIN_VIN_THRES_H:
		/* 25 mV resolution. Convert to mV. */
		val *= 25;
		break;
	case LTC2945_ADIN_H:
	case LTC2945_MAX_ADIN_H:
	case LTC2945_MIN_ADIN_THRES_H:
	case LTC2945_MAX_ADIN_THRES_H:
	case LTC2945_MIN_ADIN_H:
		/* 0.5mV resolution. Convert to mV. */
		val = val >> 1;
		break;
	case LTC2945_SENSE_H:
	case LTC2945_MAX_SENSE_H:
	case LTC2945_MIN_SENSE_H:
	case LTC2945_MAX_SENSE_THRES_H:
	case LTC2945_MIN_SENSE_THRES_H:
		/* 25 uV resolution. Convert to mA. */
		val *= 25 * 1000;
		/* Overflow check: Assuming max 12-bit sense, val is at most 27 bits right now */
		val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor);
		/* Overflow check: After division, <= 27 bits */

Annotation

Implementation Notes