drivers/hwmon/pmbus/adm1266.c

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

File Facts

System
Linux kernel
Corpus path
drivers/hwmon/pmbus/adm1266.c
Extension
.c
Size
17820 bytes
Lines
674
Domain
Driver Families
Bucket
drivers/hwmon
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern 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

static const struct file_operations adm1266_clear_blackbox_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.write = adm1266_clear_blackbox_write,
	.llseek = noop_llseek,
};

/*
 * SET_RTC (0xDF) is a 6-byte block (datasheet Rev. D, Table 84):
 *   bytes [1:0] - fractional seconds (1/65536 s, written as zero)
 *   bytes [5:2] - seconds since 1970-01-01 UTC, little-endian
 *
 * The driver seeds it once at probe via adm1266_rtc_set().  Over a
 * long uptime the chip's counter drifts away from host wall-clock,
 * so expose it via debugfs:
 *
 *   read  -- returns the chip's current seconds counter, which lets
 *            userspace observe host-vs-chip drift.
 *   write -- the kernel re-reads ktime_get_real_seconds() and writes
 *            it to SET_RTC.  The write payload is ignored; userspace
 *            does not get to supply its own timestamp value, so
 *            there is no way to push a wrong time into the chip.
 *
 * A small userspace agent (chrony hook, systemd-timesyncd script,
 * or a periodic cron job) can write to this file to keep the
 * timestamp embedded in each blackbox record aligned with
 * wall-clock across long uptimes.
 */
static int adm1266_rtc_get(void *data, u64 *val)
{
	struct i2c_client *client = data;
	u8 buf[I2C_SMBUS_BLOCK_MAX];
	u32 seconds = 0;
	int ret, i;

	guard(pmbus_lock)(client);
	ret = i2c_smbus_read_block_data(client, ADM1266_SET_RTC, buf);
	if (ret < 0)
		return ret;
	if (ret < 6)
		return -EIO;

	for (i = 0; i < 4; i++)
		seconds |= (u32)buf[2 + i] << (i * 8);

	*val = seconds;

	return 0;
}

static int adm1266_rtc_set(void *data, u64 val)
{
	struct i2c_client *client = data;
	time64_t kt = ktime_get_real_seconds();
	u8 write_buf[6] = { 0 };
	int i;

	/* User-supplied @val is ignored on purpose; the kernel owns the
	 * time source so userspace cannot push a wrong value into the chip.
	 */
	for (i = 0; i < 4; i++)
		write_buf[2 + i] = (kt >> (i * 8)) & 0xFF;

	guard(pmbus_lock)(client);
	return i2c_smbus_write_block_data(client, ADM1266_SET_RTC,
					  sizeof(write_buf), write_buf);
}
DEFINE_DEBUGFS_ATTRIBUTE(adm1266_rtc_fops,
			 adm1266_rtc_get, adm1266_rtc_set, "%llu\n");

static void adm1266_init_debugfs(struct adm1266_data *data)
{
	struct dentry *root;

	root = pmbus_get_debugfs_dir(data->client);
	if (!root)
		return;

	data->debugfs_dir = debugfs_create_dir(data->client->name, root);

	debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir,
				    adm1266_state_read);
	debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir,
				    adm1266_firmware_revision_read);
	debugfs_create_devm_seqfile(&data->client->dev, "powerup_counter", data->debugfs_dir,
				    adm1266_powerup_counter_read);
	debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client,
			    &adm1266_clear_blackbox_fops);
	debugfs_create_file("rtc", 0600, data->debugfs_dir, data->client,
			    &adm1266_rtc_fops);

Annotation

Implementation Notes