drivers/clk/tegra/clk-divider.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clk/tegra/clk-divider.c
Extension
.c
Size
4459 bytes
Lines
195
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

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>

#include "clk.h"

#define pll_out_override(p) (BIT((p->shift - 6)))
#define div_mask(d) ((1 << (d->width)) - 1)
#define get_mul(d) (1 << d->frac_width)
#define get_max_div(d) div_mask(d)

#define PERIPH_CLK_UART_DIV_ENB BIT(24)

static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
		   unsigned long parent_rate)
{
	int div;

	div = div_frac_get(rate, parent_rate, divider->width,
			   divider->frac_width, divider->flags);

	if (div < 0)
		return 0;

	return div;
}

static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
					     unsigned long parent_rate)
{
	struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
	u32 reg;
	int div, mul;
	u64 rate = parent_rate;

	reg = readl_relaxed(divider->reg);

	if ((divider->flags & TEGRA_DIVIDER_UART) &&
	    !(reg & PERIPH_CLK_UART_DIV_ENB))
		return rate;

	div = (reg >> divider->shift) & div_mask(divider);

	mul = get_mul(divider);
	div += mul;

	rate *= mul;
	rate += div - 1;
	do_div(rate, div);

	return rate;
}

static int clk_frac_div_determine_rate(struct clk_hw *hw,
				       struct clk_rate_request *req)
{
	struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
	int div, mul;
	unsigned long output_rate = req->best_parent_rate;

	if (!req->rate) {
		req->rate = output_rate;

		return 0;
	}

	div = get_div(divider, req->rate, output_rate);
	if (div < 0) {
		req->rate = req->best_parent_rate;

		return 0;
	}

	mul = get_mul(divider);

	req->rate = DIV_ROUND_UP(output_rate * mul, div + mul);

	return 0;
}

static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{

Annotation

Implementation Notes