drivers/clk/clk-si514.c

Source file repositories/reference/linux-study-clean/drivers/clk/clk-si514.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/clk-si514.c
Extension
.c
Size
9662 bytes
Lines
406
Domain
Driver Families
Bucket
drivers/clk
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 clk_si514 {
	struct clk_hw hw;
	struct regmap *regmap;
	struct i2c_client *i2c_client;
};
#define to_clk_si514(_hw)	container_of(_hw, struct clk_si514, hw)

/* Multiplier/divider settings */
struct clk_si514_muldiv {
	u32 m_frac;  /* 29-bit Fractional part of multiplier M */
	u8 m_int; /* Integer part of multiplier M, 65..78 */
	u8 ls_div_bits; /* 2nd divider, as 2^x */
	u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */
};

/* Enables or disables the output driver */
static int si514_enable_output(struct clk_si514 *data, bool enable)
{
	return regmap_update_bits(data->regmap, SI514_REG_CONTROL,
		SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0);
}

static int si514_prepare(struct clk_hw *hw)
{
	struct clk_si514 *data = to_clk_si514(hw);

	return si514_enable_output(data, true);
}

static void si514_unprepare(struct clk_hw *hw)
{
	struct clk_si514 *data = to_clk_si514(hw);

	si514_enable_output(data, false);
}

static int si514_is_prepared(struct clk_hw *hw)
{
	struct clk_si514 *data = to_clk_si514(hw);
	unsigned int val;
	int err;

	err = regmap_read(data->regmap, SI514_REG_CONTROL, &val);
	if (err < 0)
		return err;

	return !!(val & SI514_CONTROL_OE);
}

/* Retrieve clock multiplier and dividers from hardware */
static int si514_get_muldiv(struct clk_si514 *data,
	struct clk_si514_muldiv *settings)
{
	int err;
	u8 reg[7];

	err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1,
			reg, ARRAY_SIZE(reg));
	if (err)
		return err;

	settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
			   (reg[3] & 0x1F) << 24;
	settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5;
	settings->ls_div_bits = (reg[6] >> 4) & 0x07;
	settings->hs_div = (reg[6] & 0x03) << 8 | reg[5];
	return 0;
}

static int si514_set_muldiv(struct clk_si514 *data,
	struct clk_si514_muldiv *settings)
{
	u8 lp;
	u8 reg[7];
	int err;

	/* Calculate LP1/LP2 according to table 13 in the datasheet */
	/* 65.259980246 */
	if (settings->m_int < 65 ||
		(settings->m_int == 65 && settings->m_frac <= 139575831))
		lp = 0x22;
	/* 67.859763463 */
	else if (settings->m_int < 67 ||
		(settings->m_int == 67 && settings->m_frac <= 461581994))
		lp = 0x23;
	/* 72.937624981 */
	else if (settings->m_int < 72 ||
		(settings->m_int == 72 && settings->m_frac <= 503383578))
		lp = 0x33;
	/* 75.843265046 */

Annotation

Implementation Notes