/*
* Copyright (C) 2005-2006 Atmel Corporation
*
* 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
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/arch/at32ap7000.h>
#include <asm/arch/board.h>
#include <asm/arch/portmux.h>
#include <asm/arch/sm.h>
#include "clock.h"
#include "pio.h"
#include "sm.h"
#define PBMEM(base) \
{ \
.start = base, \
.end = base + 0x3ff, \
.flags = IORESOURCE_MEM, \
}
#define IRQ(num) \
{ \
.start = num, \
.end = num, \
.flags = IORESOURCE_IRQ, \
}
#define NAMED_IRQ(num, _name) \
{ \
.start = num, \
.end = num, \
.name = _name, \
.flags = IORESOURCE_IRQ, \
}
#define DEFINE_DEV(_name, _id) \
static struct platform_device _name##_id##_device = { \
.name = #_name, \
.id = _id, \
.resource = _name##_id##_resource, \
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
}
#define DEFINE_DEV_DATA(_name, _id) \
static struct platform_device _name##_id##_device = { \
.name = #_name, \
.id = _id, \
.dev = { \
.platform_data = &_name##_id##_data, \
}, \
.resource = _name##_id##_resource, \
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
}
#define select_peripheral(pin, periph, flags) \
at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags)
#define DEV_CLK(_name, devname, bus, _index) \
static struct clk devname##_##_name = { \
.name = #_name, \
.dev = &devname##_device.dev, \
.parent = &bus##_clk, \
.mode = bus##_clk_mode, \
.get_rate = bus##_clk_get_rate, \
.index = _index, \
}
unsigned long at32ap7000_osc_rates[3] = {
[0] = 32768,
/* FIXME: these are ATSTK1002-specific */
[1] = 20000000,
[2] = 12000000,
};
static unsigned long osc_get_rate(struct clk *clk)
{
return at32ap7000_osc_rates[clk->index];
}
static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
{
unsigned long div, mul, rate;
if (!(control & SM_BIT(PLLEN)))
return 0;
div = SM_BFEXT(PLLDIV, control) + 1;
mul = SM_BFEXT(PLLMUL, control) + 1;
rate = clk->parent->get_rate(clk->parent);
rate = (rate + div / 2) / div;
rate *= mul;
return rate;
}
static unsigned long pll0_get_rate(struct clk *clk)
{
u32 control;
control = sm_readl(&system_manager, PM_PLL0);
return pll_get_rate(clk, control);
}
static unsigned long pll1_get_rate(struct clk *clk)
{
u32 control;
control = sm_readl(&system_manager, PM_PLL1);
return pll_get_rate(clk, control);
}
/*
* The AT32AP7000 has five primary clock sources: One 32kHz
* oscillator, two crystal oscillators and two PLLs.
*/
static struct clk osc32k = {
.name = "osc32k",
.get_rate = osc_get_rate,
.users = 1,
.index = 0,
};
static struct clk osc0 = {
.name = "osc0",
.get_rate = osc_get_rate,
.users = 1,
.index = 1,
};
static struct clk osc1 = {
.name = "osc1",
.get_rate = osc_get_rate,
.index = 2,
};
static struct clk pll0 = {
.name = "pll0",
.get_rate = pll0_get_rate,
.parent = &osc0,
};
static struct clk pll1 = {
.name = "pll1",
.get_rate = pll1_get_rate,
.parent = &osc0,
};
/*
* The main clock can be either osc0 or pll0. The boot loader may
* have chosen one for us, so we don't really know which one until we
* have a look at the SM.
*/
static struct clk *main_clock;
/*
* Synchronous clocks are generated from the main clock. The clocks
* must satisfy the constraint
* fCPU >= fHSB >= fPB
* i.e. each clock must not be faster than its parent.
*/
static unsigned long bus_clk_get_rate(struct clk *clk, unsigned int shift)
{
return main_clock->get_rate(main_clock) >> shift;
};
static void cpu_clk_mode(struct clk *clk, int enabled)
{
struct at32_sm *sm = &system_manager;
unsigned long flags;
u32 mask;
spin_lock_irqsave(&sm->lock, flags);
mask = sm_readl(sm, PM_CPU_MASK);
if (enabled)
mask |= 1 << clk->index;
else
mask &= ~(1 << clk->index);
sm_writel(sm, PM_CPU_MASK, mask);
spin_unlock_irqrestore(&sm->lock, flags);
}
static unsigned long cpu_clk_get_rate(struct clk *clk)
{
unsigned long cksel, shift = 0;
cksel = sm_readl(&system_manager, PM_CKSEL);
if (cksel & SM_BIT(CPUDIV))
shift = SM_BFEXT(CPUSEL, cksel) + 1;
return bus_clk_get_rate(clk, shift);
}
static void hsb_clk_mode(struct clk *clk, int enabled)
{
struct at32_sm *sm = &