drivers/rtc/rtc-rs5c372.c

Source file repositories/reference/linux-study-clean/drivers/rtc/rtc-rs5c372.c

File Facts

System
Linux kernel
Corpus path
drivers/rtc/rtc-rs5c372.c
Extension
.c
Size
23027 bytes
Lines
931
Domain
Driver Families
Bucket
drivers/rtc
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 rs5c372 {
	struct i2c_client	*client;
	struct rtc_device	*rtc;
	enum rtc_type		type;
	unsigned		time24:1;
	unsigned		has_irq:1;
	unsigned		smbus:1;
	char			buf[17];
	char			*regs;
};

static int rs5c_get_regs(struct rs5c372 *rs5c)
{
	struct i2c_client	*client = rs5c->client;
	struct i2c_msg		msgs[] = {
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = sizeof(rs5c->buf),
			.buf = rs5c->buf
		},
	};

	/* This implements the third reading method from the datasheet, using
	 * an internal address that's reset after each transaction (by STOP)
	 * to 0x0f ... so we read extra registers, and skip the first one.
	 *
	 * The first method doesn't work with the iop3xx adapter driver, on at
	 * least 80219 chips; this works around that bug.
	 *
	 * The third method on the other hand doesn't work for the SMBus-only
	 * configurations, so we use the first method there, stripping off
	 * the extra register in the process.
	 */
	if (rs5c->smbus) {
		int addr = RS5C_ADDR(RS5C372_REG_SECS);
		int size = sizeof(rs5c->buf) - 1;

		if (i2c_smbus_read_i2c_block_data(client, addr, size,
						  rs5c->buf + 1) != size) {
			dev_warn(&client->dev, "can't read registers\n");
			return -EIO;
		}
	} else {
		if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
			dev_warn(&client->dev, "can't read registers\n");
			return -EIO;
		}
	}

	dev_dbg(&client->dev,
		"%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n",
		rs5c->regs + 0, rs5c->regs[3],
		rs5c->regs + 4, rs5c->regs[7],
		rs5c->regs + 8, rs5c->regs + 11,
		rs5c->regs[14], rs5c->regs[15]);

	return 0;
}

static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg)
{
	unsigned	hour;

	if (rs5c->time24)
		return bcd2bin(reg & 0x3f);

	hour = bcd2bin(reg & 0x1f);
	if (hour == 12)
		hour = 0;
	if (reg & 0x20)
		hour += 12;
	return hour;
}

static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour)
{
	if (rs5c->time24)
		return bin2bcd(hour);

	if (hour > 12)
		return 0x20 | bin2bcd(hour - 12);
	if (hour == 12)
		return 0x20 | bin2bcd(12);
	if (hour == 0)
		return bin2bcd(12);
	return bin2bcd(hour);
}

static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)

Annotation

Implementation Notes