Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5b736d0 authored by Kevin Hilman's avatar Kevin Hilman
Browse files

davinci: major rework of clock, PLL, PSC infrastructure



This is a significant rework of the low-level clock, PLL and Power
Sleep Controller (PSC) implementation for the DaVinci family.  The
primary goal is to have better modeling if the hardware clocks and
features with the aim of DVFS functionality.

Highlights:
- model PLLs and all PLL-derived clocks
- model parent/child relationships of PLLs and clocks
- convert to new clkdev layer
- view clock frequency and refcount via /proc/davinci_clocks

Special thanks to significant contributions and testing by David
Brownell.

Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarKevin Hilman <khilman@deeprootsystems.com>
parent e653034e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -586,6 +586,7 @@ config ARCH_DAVINCI
	select HAVE_CLK
	select ZONE_DMA
	select HAVE_IDE
	select COMMON_CLKDEV
	help
	  Support for TI's DaVinci platform.

+12 −0
Original line number Diff line number Diff line
@@ -18,6 +18,18 @@ config MACH_DAVINCI_EVM
	  Configure this option to specify the whether the board used
	  for development is a DaVinci EVM

config DAVINCI_RESET_CLOCKS
	bool "Reset unused clocks during boot"
	depends on ARCH_DAVINCI
	help
	  Say Y if you want to reset unused clocks during boot.
	  This option saves power, but assumes all drivers are
	  using the clock framework. Broken drivers that do not
	  yet use clock framework may not work with this option.
	  If you are booting from another operating system, you
	  probably do not want this option enabled until your
	  device drivers work properly.

endmenu

endif
+0 −2
Original line number Diff line number Diff line
@@ -406,8 +406,6 @@ davinci_evm_map_io(void)

static __init void davinci_evm_init(void)
{
	davinci_psc_init();

#if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \
    defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE)
#if defined(CONFIG_MTD_PHYSMAP) || \
+228 −157
Original line number Diff line number Diff line
/*
 * TI DaVinci clock config file
 * Clock and PLL control for DaVinci devices
 *
 * Copyright (C) 2006 Texas Instruments.
 * Copyright (C) 2006-2007 Texas Instruments.
 * Copyright (C) 2008-2009 Deep Root Systems, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -13,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -21,98 +23,50 @@
#include <mach/hardware.h>

#include <mach/psc.h>
#include <mach/cputype.h>
#include "clock.h"

/* PLL/Reset register offsets */
#define PLLM		0x110

static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
static DEFINE_SPINLOCK(clockfw_lock);

static unsigned int commonrate;
static unsigned int armrate;
static unsigned int fixedrate = 27000000;	/* 27 MHZ */

extern void davinci_psc_config(unsigned int domain, unsigned int id, char enable);

/*
 * Returns a clock. Note that we first try to use device id on the bus
 * and clock name. If this fails, we try to use clock name only.
 */
struct clk *clk_get(struct device *dev, const char *id)
static unsigned psc_domain(struct clk *clk)
{
	struct clk *p, *clk = ERR_PTR(-ENOENT);
	int idno;

	if (dev == NULL || dev->bus != &platform_bus_type)
		idno = -1;
	else
		idno = to_platform_device(dev)->id;

	mutex_lock(&clocks_mutex);

	list_for_each_entry(p, &clocks, node) {
		if (p->id == idno &&
		    strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
			clk = p;
			goto found;
		}
	}

	list_for_each_entry(p, &clocks, node) {
		if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
			clk = p;
			break;
		}
	}

found:
	mutex_unlock(&clocks_mutex);

	return clk;
	return (clk->flags & PSC_DSP)
		? DAVINCI_GPSC_DSPDOMAIN
		: DAVINCI_GPSC_ARMDOMAIN;
}
EXPORT_SYMBOL(clk_get);

void clk_put(struct clk *clk)
static void __clk_enable(struct clk *clk)
{
	if (clk && !IS_ERR(clk))
		module_put(clk->owner);
}
EXPORT_SYMBOL(clk_put);

static int __clk_enable(struct clk *clk)
{
	if (clk->flags & ALWAYS_ENABLED)
		return 0;

	davinci_psc_config(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 1);
	return 0;
	if (clk->parent)
		__clk_enable(clk->parent);
	if (clk->usecount++ == 0 && (clk->flags & CLK_PSC))
		davinci_psc_config(psc_domain(clk), clk->lpsc, 1);
}

static void __clk_disable(struct clk *clk)
{
	if (clk->usecount)
	if (WARN_ON(clk->usecount == 0))
		return;

	davinci_psc_config(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 0);
	if (--clk->usecount == 0 && !(clk->flags & CLK_PLL))
		davinci_psc_config(psc_domain(clk), clk->lpsc, 0);
	if (clk->parent)
		__clk_disable(clk->parent);
}

int clk_enable(struct clk *clk)
{
	unsigned long flags;
	int ret = 0;

	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

	if (clk->usecount++ == 0) {
	spin_lock_irqsave(&clockfw_lock, flags);
		ret = __clk_enable(clk);
	__clk_enable(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
	}

	return ret;
	return 0;
}
EXPORT_SYMBOL(clk_enable);

@@ -123,12 +77,10 @@ void clk_disable(struct clk *clk)
	if (clk == NULL || IS_ERR(clk))
		return;

	if (clk->usecount > 0 && !(--clk->usecount)) {
	spin_lock_irqsave(&clockfw_lock, flags);
	__clk_disable(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
}
EXPORT_SYMBOL(clk_disable);

unsigned long clk_get_rate(struct clk *clk)
@@ -136,7 +88,7 @@ unsigned long clk_get_rate(struct clk *clk)
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

	return *(clk->rate);
	return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);

@@ -145,7 +97,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

	return *(clk->rate);
	return clk->rate;
}
EXPORT_SYMBOL(clk_round_rate);

@@ -164,10 +116,23 @@ int clk_register(struct clk *clk)
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

	if (WARN(clk->parent && !clk->parent->rate,
			"CLK: %s parent %s has no rate!\n",
			clk->name, clk->parent->name))
		return -EINVAL;

	mutex_lock(&clocks_mutex);
	list_add(&clk->node, &clocks);
	list_add_tail(&clk->node, &clocks);
	mutex_unlock(&clocks_mutex);

	/* If rate is already set, use it */
	if (clk->rate)
		return 0;

	/* Otherwise, default to parent rate */
	if (clk->parent)
		clk->rate = clk->parent->rate;

	return 0;
}
EXPORT_SYMBOL(clk_register);
@@ -183,84 +148,150 @@ void clk_unregister(struct clk *clk)
}
EXPORT_SYMBOL(clk_unregister);

static struct clk davinci_clks[] = {
	{
		.name = "ARMCLK",
		.rate = &armrate,
		.lpsc = -1,
		.flags = ALWAYS_ENABLED,
	},
	{
		.name = "UART",
		.rate = &fixedrate,
		.lpsc = DAVINCI_LPSC_UART0,
	},
	{
		.name = "EMACCLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_EMAC_WRAPPER,
	},
	{
		.name = "I2CCLK",
		.rate = &fixedrate,
		.lpsc = DAVINCI_LPSC_I2C,
	},
	{
		.name = "IDECLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_ATA,
	},
	{
		.name = "McBSPCLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_McBSP,
	},
	{
		.name = "MMCSDCLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_MMC_SD,
	},
	{
		.name = "SPICLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_SPI,
	},
#ifdef CONFIG_DAVINCI_RESET_CLOCKS
/*
 * Disable any unused clocks left on by the bootloader
 */
static int __init clk_disable_unused(void)
{
		.name = "gpio",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_GPIO,
	},
	struct clk *ck;

	spin_lock_irq(&clockfw_lock);
	list_for_each_entry(ck, &clocks, node) {
		if (ck->usecount > 0)
			continue;
		if (!(ck->flags & CLK_PSC))
			continue;

		/* ignore if in Disabled or SwRstDisable states */
		if (!davinci_psc_is_clk_active(ck->lpsc))
			continue;

		pr_info("Clocks: disable unused %s\n", ck->name);
		davinci_psc_config(psc_domain(ck), ck->lpsc, 0);
	}
	spin_unlock_irq(&clockfw_lock);

	return 0;
}
late_initcall(clk_disable_unused);
#endif

static void clk_sysclk_recalc(struct clk *clk)
{
		.name = "usb",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_USB,
	},
	u32 v, plldiv;
	struct pll_data *pll;

	/* If this is the PLL base clock, no more calculations needed */
	if (clk->pll_data)
		return;

	if (WARN_ON(!clk->parent))
		return;

	clk->rate = clk->parent->rate;

	/* Otherwise, the parent must be a PLL */
	if (WARN_ON(!clk->parent->pll_data))
		return;

	pll = clk->parent->pll_data;

	/* If pre-PLL, source clock is before the multiplier and divider(s) */
	if (clk->flags & PRE_PLL)
		clk->rate = pll->input_rate;

	if (!clk->div_reg)
		return;

	v = __raw_readl(pll->base + clk->div_reg);
	if (v & PLLDIV_EN) {
		plldiv = (v & PLLDIV_RATIO_MASK) + 1;
		if (plldiv)
			clk->rate /= plldiv;
	}
}

static void __init clk_pll_init(struct clk *clk)
{
		.name = "AEMIFCLK",
		.rate = &commonrate,
		.lpsc = DAVINCI_LPSC_AEMIF,
		.usecount = 1,
	u32 ctrl, mult = 1, prediv = 1, postdiv = 1;
	u8 bypass;
	struct pll_data *pll = clk->pll_data;

	pll->base = IO_ADDRESS(pll->phys_base);
	ctrl = __raw_readl(pll->base + PLLCTL);
	clk->rate = pll->input_rate = clk->parent->rate;

	if (ctrl & PLLCTL_PLLEN) {
		bypass = 0;
		mult = __raw_readl(pll->base + PLLM);
		mult = (mult & PLLM_PLLM_MASK) + 1;
	} else
		bypass = 1;

	if (pll->flags & PLL_HAS_PREDIV) {
		prediv = __raw_readl(pll->base + PREDIV);
		if (prediv & PLLDIV_EN)
			prediv = (prediv & PLLDIV_RATIO_MASK) + 1;
		else
			prediv = 1;
	}
};

int __init davinci_clk_init(void)
	/* pre-divider is fixed, but (some?) chips won't report that */
	if (cpu_is_davinci_dm355() && pll->num == 1)
		prediv = 8;

	if (pll->flags & PLL_HAS_POSTDIV) {
		postdiv = __raw_readl(pll->base + POSTDIV);
		if (postdiv & PLLDIV_EN)
			postdiv = (postdiv & PLLDIV_RATIO_MASK) + 1;
		else
			postdiv = 1;
	}

	if (!bypass) {
		clk->rate /= prediv;
		clk->rate *= mult;
		clk->rate /= postdiv;
	}

	pr_debug("PLL%d: input = %lu MHz [ ",
		 pll->num, clk->parent->rate / 1000000);
	if (bypass)
		pr_debug("bypass ");
	if (prediv > 1)
		pr_debug("/ %d ", prediv);
	if (mult > 1)
		pr_debug("* %d ", mult);
	if (postdiv > 1)
		pr_debug("/ %d ", postdiv);
	pr_debug("] --> %lu MHz output.\n", clk->rate / 1000000);
}

int __init davinci_clk_init(struct davinci_clk *clocks)
  {
	struct clk *clkp;
	int count = 0;
	u32 pll_mult;

	pll_mult = davinci_readl(DAVINCI_PLL_CNTRL0_BASE + PLLM);
	commonrate = ((pll_mult + 1) * 27000000) / 6;
	armrate = ((pll_mult + 1) * 27000000) / 2;

	for (clkp = davinci_clks; count < ARRAY_SIZE(davinci_clks);
	     count++, clkp++) {
		clk_register(clkp);

		/* Turn on clocks that have been enabled in the
		 * table above */
		if (clkp->usecount)
			clk_enable(clkp);
	struct davinci_clk *c;
	struct clk *clk;

	for (c = clocks; c->lk.clk; c++) {
		clk = c->lk.clk;

		if (clk->pll_data)
			clk_pll_init(clk);

		/* Calculate rates for PLL-derived clocks */
		else if (clk->flags & CLK_PLL)
			clk_sysclk_recalc(clk);

		if (clk->lpsc)
			clk->flags |= CLK_PSC;

		clkdev_add(&c->lk);
		clk_register(clk);

		/* Turn on clocks that Linux doesn't otherwise manage */
		if (clk->flags & ALWAYS_ENABLED)
			clk_enable(clk);
	}

	return 0;
@@ -285,12 +316,52 @@ static void davinci_ck_stop(struct seq_file *m, void *v)
{
}

static int davinci_ck_show(struct seq_file *m, void *v)
#define CLKNAME_MAX	10		/* longest clock name */
#define NEST_DELTA	2
#define NEST_MAX	4

static void
dump_clock(struct seq_file *s, unsigned nest, struct clk *parent)
{
	struct clk *cp;
	char		*state;
	char		buf[CLKNAME_MAX + NEST_DELTA * NEST_MAX];
	struct clk	*clk;
	unsigned	i;

	if (parent->flags & CLK_PLL)
		state = "pll";
	else if (parent->flags & CLK_PSC)
		state = "psc";
	else
		state = "";

	/* <nest spaces> name <pad to end> */
	memset(buf, ' ', sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = 0;
	i = strlen(parent->name);
	memcpy(buf + nest, parent->name,
			min(i, (unsigned)(sizeof(buf) - 1 - nest)));

	seq_printf(s, "%s users=%2d %-3s %9ld Hz\n",
		   buf, parent->usecount, state, clk_get_rate(parent));
	/* REVISIT show device associations too */

	/* cost is now small, but not linear... */
	list_for_each_entry(clk, &clocks, node) {
		if (clk->parent == parent)
			dump_clock(s, nest + NEST_DELTA, clk);
	}
}

	list_for_each_entry(cp, &clocks, node)
		seq_printf(m,"%s %d %d\n", cp->name, *(cp->rate), cp->usecount);
static int davinci_ck_show(struct seq_file *m, void *v)
{
	/* Show clock tree; we know the main oscillator is first.
	 * We trust nonzero usecounts equate to PSC enables...
	 */
	mutex_lock(&clocks_mutex);
	if (!list_empty(&clocks))
		dump_clock(m, 0, list_first_entry(&clocks, struct clk, node));
	mutex_unlock(&clocks_mutex);

	return 0;
}
+75 −12
Original line number Diff line number Diff line
/*
 * TI DaVinci clock definitions
 *
 * Copyright (C) 2006 Texas Instruments.
 * Copyright (C) 2006-2007 Texas Instruments.
 * Copyright (C) 2008-2009 Deep Root Systems, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
@@ -11,23 +12,85 @@
#ifndef __ARCH_ARM_DAVINCI_CLOCK_H
#define __ARCH_ARM_DAVINCI_CLOCK_H

#include <linux/list.h>
#include <asm/clkdev.h>

#define DAVINCI_PLL1_BASE 0x01c40800
#define DAVINCI_PLL2_BASE 0x01c40c00
#define MAX_PLL 2

/* PLL/Reset register offsets */
#define PLLCTL          0x100
#define PLLCTL_PLLEN    BIT(0)
#define PLLCTL_CLKMODE  BIT(8)

#define PLLM		0x110
#define PLLM_PLLM_MASK  0xff

#define PREDIV          0x114
#define PLLDIV1         0x118
#define PLLDIV2         0x11c
#define PLLDIV3         0x120
#define POSTDIV         0x128
#define BPDIV           0x12c
#define PLLCMD		0x138
#define PLLSTAT		0x13c
#define PLLALNCTL	0x140
#define PLLDCHANGE	0x144
#define PLLCKEN		0x148
#define PLLCKSTAT	0x14c
#define PLLSYSTAT	0x150
#define PLLDIV4         0x160
#define PLLDIV5         0x164
#define PLLDIV6         0x168
#define PLLDIV7         0x16c
#define PLLDIV8         0x170
#define PLLDIV9         0x174
#define PLLDIV_EN       BIT(15)
#define PLLDIV_RATIO_MASK 0x1f

struct pll_data {
	u32 phys_base;
	void __iomem *base;
	u32 num;
	u32 flags;
	u32 input_rate;
};
#define PLL_HAS_PREDIV          0x01
#define PLL_HAS_POSTDIV         0x02

struct clk {
	struct list_head	node;
	struct module		*owner;
	const char		*name;
	unsigned int		*rate;
	int			id;
	__s8			usecount;
	__u8			flags;
	__u8			lpsc;
	unsigned long		rate;
	u8			usecount;
	u8			flags;
	u8			lpsc;
	struct clk              *parent;
	struct pll_data         *pll_data;
	u32                     div_reg;
};

/* Clock flags */
#define RATE_CKCTL		1
#define RATE_FIXED		2
#define RATE_PROPAGATES		4
#define VIRTUAL_CLOCK		8
#define ALWAYS_ENABLED		16
#define ENABLE_REG_32BIT	32
#define ALWAYS_ENABLED		BIT(1)
#define CLK_PSC                 BIT(2)
#define PSC_DSP                 BIT(3) /* PSC uses DSP domain, not ARM */
#define CLK_PLL			BIT(4) /* PLL-derived clock */
#define PRE_PLL                 BIT(5) /* source is before PLL mult/div */

struct davinci_clk {
	struct clk_lookup lk;
};

#define CLK(dev, con, ck) 		\
	{				\
		.lk = {			\
			.dev_id = dev,	\
			.con_id = con,	\
			.clk = ck,	\
		},			\
	}

int davinci_clk_init(struct davinci_clk *clocks);
#endif
Loading