drivers/rtc/rtc-sun6i.c

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

File Facts

System
Linux kernel
Corpus path
drivers/rtc/rtc-sun6i.c
Extension
.c
Size
23669 bytes
Lines
884
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 sun6i_rtc_clk_data {
	unsigned long rc_osc_rate;
	unsigned int fixed_prescaler : 16;
	unsigned int has_prescaler : 1;
	unsigned int has_out_clk : 1;
	unsigned int has_losc_en : 1;
	unsigned int has_auto_swt : 1;
};

#define RTC_LINEAR_DAY	BIT(0)

struct sun6i_rtc_dev {
	struct rtc_device *rtc;
	const struct sun6i_rtc_clk_data *data;
	void __iomem *base;
	int irq;
	time64_t alarm;
	unsigned long flags;

	struct clk_hw hw;
	struct clk_hw *int_osc;
	struct clk *losc;
	struct clk *ext_losc;

	spinlock_t lock;
};

static struct sun6i_rtc_dev *sun6i_rtc;

static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw,
					       unsigned long parent_rate)
{
	struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
	u32 val = 0;

	val = readl(rtc->base + SUN6I_LOSC_CTRL);
	if (val & SUN6I_LOSC_CTRL_EXT_OSC)
		return parent_rate;

	if (rtc->data->fixed_prescaler)
		parent_rate /= rtc->data->fixed_prescaler;

	if (rtc->data->has_prescaler) {
		val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL);
		val &= GENMASK(4, 0);
	}

	return parent_rate / (val + 1);
}

static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw)
{
	struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);

	return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC;
}

static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index)
{
	struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
	unsigned long flags;
	u32 val;

	if (index > 1)
		return -EINVAL;

	spin_lock_irqsave(&rtc->lock, flags);
	val = readl(rtc->base + SUN6I_LOSC_CTRL);
	val &= ~SUN6I_LOSC_CTRL_EXT_OSC;
	val |= SUN6I_LOSC_CTRL_KEY;
	val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0;
	if (rtc->data->has_losc_en) {
		val &= ~SUN6I_LOSC_CTRL_EXT_LOSC_EN;
		val |= index ? SUN6I_LOSC_CTRL_EXT_LOSC_EN : 0;
	}
	writel(val, rtc->base + SUN6I_LOSC_CTRL);
	spin_unlock_irqrestore(&rtc->lock, flags);

	return 0;
}

static const struct clk_ops sun6i_rtc_osc_ops = {
	.recalc_rate	= sun6i_rtc_osc_recalc_rate,
	.determine_rate	= clk_hw_determine_rate_no_reparent,

	.get_parent	= sun6i_rtc_osc_get_parent,
	.set_parent	= sun6i_rtc_osc_set_parent,
};

static void __init sun6i_rtc_clk_init(struct device_node *node,

Annotation

Implementation Notes