arch/powerpc/platforms/512x/mpc512x_shared.c

Source file repositories/reference/linux-study-clean/arch/powerpc/platforms/512x/mpc512x_shared.c

File Facts

System
Linux kernel
Corpus path
arch/powerpc/platforms/512x/mpc512x_shared.c
Extension
.c
Size
13631 bytes
Lines
507
Domain
Architecture Layer
Bucket
arch/powerpc
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct fsl_diu_shared_fb {
	u8		gamma[0x300];	/* 32-bit aligned! */
	struct diu_ad	ad0;		/* 32-bit aligned! */
	phys_addr_t	fb_phys;
	size_t		fb_len;
	bool		in_use;
};

/* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */
static void mpc512x_set_pixel_clock(unsigned int pixclock)
{
	struct device_node *np;
	struct clk *clk_diu;
	unsigned long epsilon, minpixclock, maxpixclock;
	unsigned long offset, want, got, delta;

	/* lookup and enable the DIU clock */
	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
	if (!np) {
		pr_err("Could not find DIU device tree node.\n");
		return;
	}
	clk_diu = of_clk_get(np, 0);
	if (IS_ERR(clk_diu)) {
		/* backwards compat with device trees that lack clock specs */
		clk_diu = clk_get_sys(np->name, "ipg");
	}
	of_node_put(np);
	if (IS_ERR(clk_diu)) {
		pr_err("Could not lookup DIU clock.\n");
		return;
	}
	if (clk_prepare_enable(clk_diu)) {
		pr_err("Could not enable DIU clock.\n");
		return;
	}

	/*
	 * convert the picoseconds spec into the desired clock rate,
	 * determine the acceptable clock range for the monitor (+/- 5%),
	 * do the calculation in steps to avoid integer overflow
	 */
	pr_debug("DIU pixclock in ps - %u\n", pixclock);
	pixclock = (1000000000 / pixclock) * 1000;
	pr_debug("DIU pixclock freq  - %u\n", pixclock);
	epsilon = pixclock / 20; /* pixclock * 0.05 */
	pr_debug("DIU deviation      - %lu\n", epsilon);
	minpixclock = pixclock - epsilon;
	maxpixclock = pixclock + epsilon;
	pr_debug("DIU minpixclock    - %lu\n", minpixclock);
	pr_debug("DIU maxpixclock    - %lu\n", maxpixclock);

	/*
	 * check whether the DIU supports the desired pixel clock
	 *
	 * - simply request the desired clock and see what the
	 *   platform's clock driver will make of it, assuming that it
	 *   will setup the best approximation of the requested value
	 * - try other candidate frequencies in the order of decreasing
	 *   preference (i.e. with increasing distance from the desired
	 *   pixel clock, and checking the lower frequency before the
	 *   higher frequency to not overload the hardware) until the
	 *   first match is found -- any potential subsequent match
	 *   would only be as good as the former match or typically
	 *   would be less preferrable
	 *
	 * the offset increment of pixelclock divided by 64 is an
	 * arbitrary choice -- it's simple to calculate, in the typical
	 * case we expect the first check to succeed already, in the
	 * worst case seven frequencies get tested (the exact center and
	 * three more values each to the left and to the right) before
	 * the 5% tolerance window is exceeded, resulting in fast enough
	 * execution yet high enough probability of finding a suitable
	 * value, while the error rate will be in the order of single
	 * percents
	 */
	for (offset = 0; offset <= epsilon; offset += pixclock / 64) {
		want = pixclock - offset;
		pr_debug("DIU checking clock - %lu\n", want);
		clk_set_rate(clk_diu, want);
		got = clk_get_rate(clk_diu);
		delta = abs(pixclock - got);
		if (delta < epsilon)
			break;
		if (!offset)
			continue;
		want = pixclock + offset;
		pr_debug("DIU checking clock - %lu\n", want);
		clk_set_rate(clk_diu, want);
		got = clk_get_rate(clk_diu);

Annotation

Implementation Notes