#include <asm/clkdev.h>
+#include <plat/mtu.h>
#include <mach/hardware.h>
#include "clock.h"
#define PRCM_DMACLK_MGT 0x074
#define PRCM_B2R2CLK_MGT 0x078
#define PRCM_TVCLK_MGT 0x07C
+#define PRCM_TCR 0x1C8
+#define PRCM_TCR_STOPPED (1 << 16)
+#define PRCM_TCR_DOZE_MODE (1 << 17)
#define PRCM_UNIPROCLK_MGT 0x278
#define PRCM_SSPCLK_MGT 0x280
#define PRCM_RNGCLK_MGT 0x284
}
EXPORT_SYMBOL(clk_disable);
+/*
+ * The MTU has a separate, rather complex muxing setup
+ * with alternative parents (peripheral cluster or
+ * ULP or fixed 32768 Hz) depending on settings
+ */
+static unsigned long clk_mtu_get_rate(struct clk *clk)
+{
+ void __iomem *addr = __io_address(U8500_PRCMU_BASE)
+ + PRCM_TCR;
+ u32 tcr = readl(addr);
+ int mtu = (int) clk->data;
+ /*
+ * One of these is selected eventually
+ * TODO: Replace the constant with a reference
+ * to the ULP source once this is modeled.
+ */
+ unsigned long clk32k = 32768;
+ unsigned long mturate;
+ unsigned long retclk;
+
+ /* Get the rate from the parent as a default */
+ if (clk->parent_periph)
+ mturate = clk_get_rate(clk->parent_periph);
+ else if (clk->parent_cluster)
+ mturate = clk_get_rate(clk->parent_cluster);
+ else
+ /* We need to be connected SOMEWHERE */
+ BUG();
+
+ /*
+ * Are we in doze mode?
+ * In this mode the parent peripheral or the fixed 32768 Hz
+ * clock is fed into the block.
+ */
+ if (!(tcr & PRCM_TCR_DOZE_MODE)) {
+ /*
+ * Here we're using the clock input from the APE ULP
+ * clock domain. But first: are the timers stopped?
+ */
+ if (tcr & PRCM_TCR_STOPPED) {
+ clk32k = 0;
+ mturate = 0;
+ } else {
+ /* Else default mode: 0 and 2.4 MHz */
+ clk32k = 0;
+ if (cpu_is_u5500())
+ /* DB5500 divides by 8 */
+ mturate /= 8;
+ else if (cpu_is_u8500ed()) {
+ /*
+ * This clocking setting must not be used
+ * in the ED chip, it is simply not
+ * connected anywhere!
+ */
+ mturate = 0;
+ BUG();
+ } else
+ /*
+ * In this mode the ulp38m4 clock is divided
+ * by a factor 16, on the DB8500 typically
+ * 38400000 / 16 ~ 2.4 MHz.
+ * TODO: Replace the constant with a reference
+ * to the ULP source once this is modeled.
+ */
+ mturate = 38400000 / 16;
+ }
+ }
+
+ /* Return the clock selected for this MTU */
+ if (tcr & (1 << mtu))
+ retclk = clk32k;
+ else
+ retclk = mturate;
+
+ pr_info("MTU%d clock rate: %lu Hz\n", mtu, retclk);
+ return retclk;
+}
+
unsigned long clk_get_rate(struct clk *clk)
{
unsigned long rate;
+ /*
+ * If there is a custom getrate callback for this clock,
+ * it will take precedence.
+ */
+ if (clk->get_rate)
+ return clk->get_rate(clk);
+
if (clk->ops && clk->ops->get_rate)
return clk->ops->get_rate(clk);
/* Peripheral Cluster #6 */
-static DEFINE_PRCC_CLK(6, mtu1_v1, 8, -1, NULL);
-static DEFINE_PRCC_CLK(6, mtu0_v1, 7, -1, NULL);
+/* MTU ID in data */
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu1_v1, 8, -1, NULL, clk_mtu_get_rate, 1);
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu0_v1, 7, -1, NULL, clk_mtu_get_rate, 0);
static DEFINE_PRCC_CLK(6, cfgreg_v1, 6, 6, NULL);
static DEFINE_PRCC_CLK(6, dmc_ed, 6, 6, NULL);
static DEFINE_PRCC_CLK(6, hash1, 5, -1, NULL);
/* Peripheral Cluster #7 */
static DEFINE_PRCC_CLK(7, tzpc0_ed, 4, -1, NULL);
-static DEFINE_PRCC_CLK(7, mtu1_ed, 3, -1, NULL);
-static DEFINE_PRCC_CLK(7, mtu0_ed, 2, -1, NULL);
+/* MTU ID in data */
+static DEFINE_PRCC_CLK_CUSTOM(7, mtu1_ed, 3, -1, NULL, clk_mtu_get_rate, 1);
+static DEFINE_PRCC_CLK_CUSTOM(7, mtu0_ed, 2, -1, NULL, clk_mtu_get_rate, 0);
static DEFINE_PRCC_CLK(7, wdg_ed, 1, -1, NULL);
static DEFINE_PRCC_CLK(7, cfgreg_ed, 0, -1, NULL);
CLK(uiccclk, "uicc", NULL),
};
-static int __init clk_init(void)
+int __init clk_init(void)
{
if (cpu_is_u8500ed()) {
clk_prcmu_ops.enable = clk_prcmu_ed_enable;
clk_prcmu_ops.disable = clk_prcmu_ed_disable;
+ clk_per6clk.rate = 100000000;
} else if (cpu_is_u5500()) {
/* Clock tree for U5500 not implemented yet */
clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;
clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL;
+ clk_per6clk.rate = 26000000;
}
clkdev_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks));
return 0;
}
-arch_initcall(clk_init);
* @ops: pointer to clkops struct used to control this clock
* @name: name, for debugging
* @enabled: refcount. positive if enabled, zero if disabled
+ * @get_rate: custom callback for getting the clock rate
+ * @data: custom per-clock data for example for the get_rate
+ * callback
* @rate: fixed rate for clocks which don't implement
* ops->getrate
* @prcmu_cg_off: address offset of the combined enable/disable register
const struct clkops *ops;
const char *name;
unsigned int enabled;
+ unsigned long (*get_rate)(struct clk *);
+ void *data;
unsigned long rate;
struct list_head list;
.parent_periph = _kernclk \
}
+#define DEFINE_PRCC_CLK_CUSTOM(_pclust, _name, _bus_en, _kernel_en, _kernclk, _callback, _data) \
+struct clk clk_##_name = { \
+ .name = #_name, \
+ .ops = &clk_prcc_ops, \
+ .cluster = _pclust, \
+ .prcc_bus = _bus_en, \
+ .prcc_kernel = _kernel_en, \
+ .parent_cluster = &clk_per##_pclust##clk, \
+ .parent_periph = _kernclk, \
+ .get_rate = _callback, \
+ .data = (void *) _data \
+ }
+
+
#define CLK(_clk, _devname, _conname) \
{ \
.clk = &clk_##_clk, \
.dev_id = _devname, \
.con_id = _conname, \
}
+
+int __init clk_db8500_ed_fixup(void);
+int __init clk_init(void);
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/clockchips.h>
+#include <linux/clk.h>
#include <linux/jiffies.h>
+#include <linux/err.h>
#include <asm/mach/time.h>
#include <plat/mtu.h>
void __init nmdk_timer_init(void)
{
unsigned long rate;
- u32 cr = MTU_CRn_32BITS;;
+ struct clk *clk0;
+ struct clk *clk1;
+ u32 cr;
+
+ clk0 = clk_get_sys("mtu0", NULL);
+ BUG_ON(IS_ERR(clk0));
+
+ clk1 = clk_get_sys("mtu1", NULL);
+ BUG_ON(IS_ERR(clk1));
+
+ clk_enable(clk0);
+ clk_enable(clk1);
/*
* Tick rate is 2.4MHz for Nomadik and 110MHz for ux500:
* use a divide-by-16 counter if it's more than 16MHz
*/
- rate = CLOCK_TICK_RATE;
+ cr = MTU_CRn_32BITS;;
+ rate = clk_get_rate(clk0);
if (rate > 16 << 20) {
rate /= 16;
cr |= MTU_CRn_PRESCALE_16;
nmdk_clksrc.name);
/* Timer 1 is used for events, fix according to rate */
+ cr = MTU_CRn_32BITS;
+ rate = clk_get_rate(clk1);
+ if (rate > 16 << 20) {
+ rate /= 16;
+ cr |= MTU_CRn_PRESCALE_16;
+ } else {
+ cr |= MTU_CRn_PRESCALE_1;
+ }
writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */
nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
nmdk_clkevt.max_delta_ns =