ckfwq/linux-3.0.4/arch/mips/loongson/gsc3280/clock.c

824 lines
17 KiB
C

/*
* GSC3280 SoC Clock Controller Driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: LiuJianhua <liujianhua@china-cpu.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
//#include <asm/clock.h>
#include <gsc3280/sysctl.h>
#include <loongson.h>
static LIST_HEAD(clock_list);
static unsigned long ref_clk=12*1000*1000;
static unsigned long sysctl_base;
extern void serial_get_clk(void);
struct clk
{
char name[32];
short mod;
unsigned short div_offset;
unsigned int max_div;
int iomux;
struct list_head list;
struct clk *parent;
unsigned long (*get_rate)(struct clk *clk);
unsigned long (*set_rate)(struct clk *clk, unsigned long rate);
};
unsigned long pll_get_rate(struct clk *clk);
unsigned long pll_set_rate(struct clk *clk, unsigned long rate);
unsigned long integral_get_rate(struct clk *clk);
unsigned long integral_set_rate(struct clk *clk, unsigned long rate);
unsigned long even_get_rate(struct clk *clk);
unsigned long even_set_rate(struct clk *clk, unsigned long rate);
unsigned long same_get_rate(struct clk *clk);
unsigned long same_set_rate(struct clk *clk, unsigned long rate);
unsigned long aclk_get_rate(struct clk *clk);
unsigned long aclk_set_rate(struct clk *clk, unsigned long rate);
unsigned long cpu_set_rate(struct clk *clk, unsigned long rate);
static struct clk pll =
{
.name = "pll",
//.flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
.mod = -1,
.iomux = -1,
.get_rate = pll_get_rate,
.set_rate = pll_set_rate,
};
static struct clk pll_clkout =
{
.name = "pll_clkout",
.div_offset = 0x94,
.max_div = 512,
.mod = SYSCTL_MOD_CLKOUT,
.iomux = SYSCTL_IOMUX_CLKOUT,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk aclk =
{
.name = "aclk",
.mod = -1,
.iomux = -1,
.get_rate = aclk_get_rate,
.set_rate = aclk_set_rate,
};
static struct clk cpu =
{
.name = "cpu",
.mod = -1,
.iomux = -1,
.get_rate = aclk_get_rate,
.set_rate = cpu_set_rate,
};
static struct clk ddr2 =
{
.name = "ddr2",
.mod = SYSCTL_MOD_DDR2,
.iomux = -1,
.get_rate = aclk_get_rate,
.set_rate = aclk_set_rate,
};
static struct clk hclk =
{
.name = "hclk",
.div_offset = 0x18,
.max_div = 8,
.iomux = -1,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk sdio =
{
.name = "sdio",
.div_offset = 0x20,
.max_div = 64,
.mod = SYSCTL_MOD_SDIO,
.iomux = SYSCTL_IOMUX_SDIO,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk lcdc =
{
.name = "lcd",
.div_offset = 0x24,
.max_div = 64,
.mod = SYSCTL_MOD_LCDC,
.iomux = SYSCTL_IOMUX_LCDC,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk ddr2phy =
{
.name = "ddr2phy",
.mod = -1,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk i2s =
{
.name = "i2s",
.div_offset = 0x2c,
.max_div = 32,
.mod = SYSCTL_MOD_I2S,
.iomux = SYSCTL_IOMUX_I2S,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk i2c =
{
.name = "i2c",
.div_offset = 0x28,
.max_div = 64,
.mod = SYSCTL_MOD_I2C,
.iomux = SYSCTL_IOMUX_I2C,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk spi1 =
{
.name = "spi1",
.div_offset = 0x38,
.max_div = 64,
.mod = SYSCTL_MOD_SPI1,
.iomux = SYSCTL_IOMUX_SPI1,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
# if 0
static struct clk misc =
{
.name = "misc",
.mod = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
#endif
static struct clk dma =
{
.name = "dma",
.mod = SYSCTL_MOD_DMA,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk ictl =
{
.name = "ictl",
.mod = SYSCTL_MOD_ICTL,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk nfc =
{
.name = "nfc",
.mod = SYSCTL_MOD_NFC,
.iomux = SYSCTL_IOMUX_NFC,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk sysctl =
{
.name = "sysctl",
.mod = SYSCTL_MOD_SYSCTL,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk emi =
{
.name = "emi",
.mod = SYSCTL_MOD_EMI,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk pclk =
{
.name = "pclk",
.div_offset = 0x1c,
.max_div = 8,
.mod = SYSCTL_MOD_APB,
.iomux = -1,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk ps2_0 =
{
.name = "ps2_0",
.div_offset = 0x58,
.max_div = 1024,
.mod = SYSCTL_MOD_PS2_0,
.iomux = SYSCTL_IOMUX_PS2_0,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk ps2_1 =
{
.name = "ps2_1",
.div_offset = 0x5c,
.max_div = 1024,
.mod = SYSCTL_MOD_PS2_1,
.iomux = SYSCTL_IOMUX_PS2_1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk spi0 =
{
.name = "spi0",
.div_offset = 0x34,
.max_div = 32,
.mod = SYSCTL_MOD_SPI0,
.iomux = SYSCTL_IOMUX_SPI0,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk keypad =
{
.name = "keypad",
.div_offset = 0x3c,
.max_div = 1024,
.mod = SYSCTL_MOD_KPD,
.iomux = SYSCTL_IOMUX_KEYPAD,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk wdt =
{
.name = "wdt",
.mod = SYSCTL_MOD_WDT,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk sci0 =
{
.name = "sci0",
.div_offset = 0x40,
.max_div = 64,
.mod = SYSCTL_MOD_SCI0,
.iomux = SYSCTL_IOMUX_SCI0,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk sci1 =
{
.name = "sci1",
.div_offset = 0x44,
.max_div = 64,
.mod = SYSCTL_MOD_SCI1,
.iomux = -1/*SYSCTL_IOMUX_SCI1_1*/,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk can =
{
.name = "can",
.div_offset = 0x30,
.max_div = 32,
.mod = SYSCTL_MOD_CAN,
.iomux = -1,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart0 =
{
.name = "uart0",
.div_offset = 0x60,
.max_div = 64,
.mod = SYSCTL_MOD_UART0,
.iomux = SYSCTL_IOMUX_UART0,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart1 =
{
.name = "uart1",
.div_offset = 0x64,
.max_div = 64,
.mod = SYSCTL_MOD_UART1,
.iomux = SYSCTL_IOMUX_UART1,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart2 =
{
.name = "uart2",
.div_offset = 0x68,
.max_div = 64,
.mod = SYSCTL_MOD_UART2,
.iomux = SYSCTL_IOMUX_UART2,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart3 =
{
.name = "uart3",
.div_offset = 0x6c,
.max_div = 128,
.mod = SYSCTL_MOD_UART3,
.iomux = -1 /*SYSCTL_IOMUX_UART3_1*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart4 =
{
.name = "uart4",
.div_offset = 0x70,
.max_div = 128,
.mod = SYSCTL_MOD_UART4,
.iomux = -1/*SYSCTL_IOMUX_UART4_1*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart5 =
{
.name = "uart5",
.div_offset = 0x74,
.max_div = 128,
.mod = SYSCTL_MOD_UART5,
.iomux = -1/*SYSCTL_IOMUX_UART5*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart6 =
{
.name = "uart6",
.div_offset = 0x78,
.max_div = 64,
.mod = SYSCTL_MOD_UART6,
.iomux = SYSCTL_IOMUX_UART6,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk uart7 =
{
.name = "uart7",
.div_offset = 0x7c,
.max_div = 64,
.mod = SYSCTL_MOD_UART7,
.iomux = SYSCTL_IOMUX_UART7,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk gpio =
{
.name = "gpio",
.mod = SYSCTL_MOD_GPIO,
.iomux = -1,
.get_rate = same_get_rate,
.set_rate = same_set_rate,
};
static struct clk gpiodb =
{
.name = "gpiodb",
.div_offset = 0x90,
.max_div = 128,
.mod = -1,
.iomux = -1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk timer0 =
{
.name = "timer0",
.div_offset = 0x80,
.max_div = 1024,
.mod = SYSCTL_MOD_TIMER0,
.iomux = -1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk timer1 =
{
.name = "timer1",
.div_offset = 0x84,
.max_div = 1024,
.mod = SYSCTL_MOD_TIMER1,
.iomux = -1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk timer2 =
{
.name = "timer2",
.div_offset = 0x88,
.max_div = 1024,
.mod = SYSCTL_MOD_TIMER2,
.iomux = -1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk timer3 =
{
.name = "timer3",
.div_offset = 0x8c,
.max_div = 1024,
.mod = SYSCTL_MOD_TIMER3,
.iomux = -1,
.get_rate = even_get_rate,
.set_rate = even_set_rate,
};
static struct clk pwm0 =
{
.name = "pwm0",
.div_offset = 0x48,
.max_div = 64,
.mod = SYSCTL_MOD_PWM0,
.iomux = -1/*SYSCTL_IOMUX_PWM*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk pwm1 =
{
.name = "pwm1",
.div_offset = 0x4c,
.max_div = 64,
.mod = SYSCTL_MOD_PWM1,
.iomux = -1/*SYSCTL_IOMUX_PWM*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk pwm2 =
{
.name = "pwm2",
.div_offset = 0x50,
.max_div = 64,
.mod = SYSCTL_MOD_PWM2,
.iomux = -1/*SYSCTL_IOMUX_PWM*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
static struct clk pwmr =
{
.name = "pwmr",
.div_offset = 0x54,
.max_div = 64,
.mod = SYSCTL_MOD_PWMR,
.iomux = -1/*SYSCTL_IOMUX_PWM*/,
.get_rate = integral_get_rate,
.set_rate = integral_set_rate,
};
unsigned long pll_get_rate(struct clk *clk)
{
unsigned long M,N,OD;
unsigned long freq;
freq = *(unsigned int *)sysctl_base;
M = freq & 0x7f;
N = (freq >> 7) & 0xf;
OD = (freq >> 11) & 0x2;
OD = ((OD == 0)?1:(2<<OD));
//printk("M=%ld N=%ld OD=%ld\n",M,N,OD);
if ((N == 0) || (OD == 0))
return 0;
//printk("pll=%ld\n",ref_clk*M/(N*OD));
return (ref_clk * M/(N * OD));
}
void set_pll(unsigned int pll_freq)
{
__asm__ __volatile__ (
".set push\n\t"
".set noreorder\n\t"
);
*(volatile unsigned int *) (0xbc113004UL) = 0x3;
while (((*(volatile unsigned int *)(0xbc113008UL)) & 0x7) != 0x5);
*(volatile unsigned int *)(0xbc04a000UL) = pll_freq;
*(volatile unsigned int *) (0xbc113004UL) = 0x4;
while (((*(volatile unsigned int *)(0xbc113008UL)) & 0x7) != 0x3);
__asm__ __volatile__(
".set pop\n\t"
);
}
void (*nocache_set_pll)(unsigned int);
unsigned long pll_set_rate(struct clk *clk, unsigned long rate)
{
unsigned int pll_freq;
unsigned long flags;
switch (rate) {
case 600*1000*1000:
pll_freq = 0x964;
break;
case 534*1000*1000:
pll_freq = 0x959;
break;
case 500*1000*1000:
pll_freq = 0x9fd;
break;
case 468*1000*1000:
pll_freq = 0x9f5;
break;
case 400*1000*1000:
pll_freq = 0x9e4;
break;
case 333*1000*1000:
pll_freq = 0xa6f;
break;
case 267*1000*1000:
pll_freq = 0x1159;
break;
case 200*1000*1000:
pll_freq = 0x11e4;
break;
default:
return 0;
}
local_irq_save(flags);
memcpy((void *)0xbfc00000,&set_pll,0x100);
barrier();
nocache_set_pll(pll_freq);
serial_get_clk();
local_irq_restore(flags);
return rate;
}
unsigned long same_get_rate(struct clk *clk)
{
return clk->parent->get_rate(clk->parent);
}
unsigned long aclk_get_rate(struct clk *clk)
{
return (clk->parent->get_rate(clk->parent)>>1);
}
unsigned long integral_get_rate(struct clk *clk)
{
unsigned int div;
div = *(unsigned int *)(sysctl_base + clk->div_offset);
return clk->parent->get_rate(clk->parent)/(div + 1);
}
unsigned long even_get_rate(struct clk *clk)
{
unsigned int div;
div = *(unsigned int*)(sysctl_base + clk->div_offset);
return clk->parent->get_rate(clk->parent)/((div+1)<<1);
}
unsigned long same_set_rate(struct clk *clk, unsigned long rate)
{
return 0;
}
unsigned long aclk_set_rate(struct clk *clk, unsigned long rate)
{
return 0;
}
unsigned long cpu_set_rate(struct clk *clk, unsigned long rate)
{
return (clk->parent->set_rate(clk->parent, rate<<1))>>1;
}
unsigned long integral_set_rate(struct clk *clk, unsigned long rate)
{
unsigned int div;
unsigned long parent_rate;
parent_rate = clk->parent->get_rate(clk->parent);
div = parent_rate/rate;
if (div > clk->max_div)
div = clk->max_div;
if (div < 2)
div = 2;
*(unsigned int *)(sysctl_base + clk->div_offset) = div-1;
return parent_rate/div;
}
unsigned long even_set_rate(struct clk *clk, unsigned long rate)
{
unsigned int div;
unsigned int parent_rate;
parent_rate = clk->parent->get_rate(clk->parent);
div = parent_rate/(rate<<1);
if (div > clk->max_div)
div = clk->max_div;
if (div < 2)
div = 2;
*(unsigned int *)(sysctl_base + clk->div_offset) = (div >> 1) - 1;
return parent_rate/(div<<1);
}
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *pos;
if (id == NULL)
return NULL;
list_for_each_entry_reverse(pos,&clock_list,list) {
if (!strcmp(pos->name,id)) {
return pos;
}
}
return NULL;
}
EXPORT_SYMBOL(clk_get);
int clk_enable(struct clk *clk)
{
int ret = 0;
if (clk && (clk->mod >= 0)) {
sysctl_mod_enable(clk->mod);
if (clk->iomux >= 0)
ret = sysctl_iomux_enable(clk->iomux);
}
else
ret = -EACCES;
return ret;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
if (clk && (clk->mod >= 0)) {
sysctl_mod_disable(clk->mod);
if (clk->iomux >= 0)
sysctl_iomux_disable(clk->iomux);
}
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
#ifndef CONFIG_BLX_GSC3280_FPGA
if (clk->get_rate)
return (unsigned long)clk->get_rate(clk);
return 0;
#else
return 50000000;
#endif
}
EXPORT_SYMBOL(clk_get_rate);
void clk_put(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_put);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
if (clk->set_rate)
return clk->set_rate(clk,rate);
return -EACCES;
}
EXPORT_SYMBOL_GPL(clk_set_rate);
void __init clk_init(void)
{
list_add_tail(&(pll.list),&clock_list);
pll_clkout.parent = &pll;
list_add_tail(&(pll_clkout.list),&clock_list);
aclk.parent = &pll;
list_add_tail(&(aclk.list),&clock_list);
cpu.parent = &pll;
list_add_tail(&(cpu.list),&clock_list);
ddr2.parent = &pll;
list_add_tail(&(ddr2.list),&clock_list);
hclk.parent = &pll;
list_add_tail(&(hclk.list),&clock_list);
sdio.parent = &pll;
list_add_tail(&(sdio.list),&clock_list);
lcdc.parent = &pll;
list_add_tail(&(lcdc.list),&clock_list);
ddr2phy.parent = &pll;
list_add_tail(&(ddr2phy.list),&clock_list);
i2s.parent = &pll_clkout;
list_add_tail(&(i2s.list),&clock_list);
i2c.parent = &hclk;
list_add_tail(&(i2c.list),&clock_list);
uart3.parent = &hclk;
list_add_tail(&(uart3.list),&clock_list);
uart4.parent = &hclk;
list_add_tail(&(uart4.list),&clock_list);
uart5.parent = &hclk;
list_add_tail(&(uart5.list),&clock_list);
spi1.parent = &hclk;
list_add_tail(&(spi1.list),&clock_list);
dma.parent = &hclk;
list_add_tail(&(dma.list),&clock_list);
ictl.parent = &hclk;
list_add_tail(&(ictl.list),&clock_list);
nfc.parent = &hclk;
list_add_tail(&(nfc.list),&clock_list);
sysctl.parent = &hclk;
list_add_tail(&(sysctl.list),&clock_list);
emi.parent = &hclk;
list_add_tail(&(emi.list),&clock_list);
pclk.parent = &hclk;
list_add_tail(&(pclk.list),&clock_list);
ps2_0.parent = &pclk;
list_add_tail(&(ps2_0.list),&clock_list);
ps2_1.parent = &pclk;
list_add_tail(&(ps2_1.list),&clock_list);
spi0.parent = &pclk;
list_add_tail(&(spi0.list),&clock_list);
keypad.parent = &pclk;
list_add_tail(&(keypad.list),&clock_list);
wdt.parent = &pclk;
list_add_tail(&(wdt.list),&clock_list);
sci0.parent = &pclk;
list_add_tail(&(sci0.list),&clock_list);
sci1.parent = &pclk;
list_add_tail(&(sci1.list),&clock_list);
can.parent = &pclk;
list_add_tail(&(can.list),&clock_list);
uart0.parent = &pclk;
list_add_tail(&(uart0.list),&clock_list);
uart1.parent = &pclk;
list_add_tail(&(uart1.list),&clock_list);
uart2.parent = &pclk;
list_add_tail(&(uart2.list),&clock_list);
uart6.parent = &pclk;
list_add_tail(&(uart6.list),&clock_list);
uart7.parent = &pclk;
list_add_tail(&(uart7.list),&clock_list);
gpio.parent = &pclk;
list_add_tail(&(gpio.list),&clock_list);
gpiodb.parent = &pclk;
list_add_tail(&(gpiodb.list),&clock_list);
timer0.parent = &pclk;
list_add_tail(&(timer0.list),&clock_list);
timer1.parent = &pclk;
list_add_tail(&(timer1.list),&clock_list);
timer2.parent = &pclk;
list_add_tail(&(timer2.list),&clock_list);
timer3.parent = &pclk;
list_add_tail(&(timer3.list),&clock_list);
pwm0.parent = &pclk;
list_add_tail(&(pwm0.list),&clock_list);
pwm1.parent = &pclk;
list_add_tail(&(pwm1.list),&clock_list);
pwm2.parent = &pclk;
list_add_tail(&(pwm2.list),&clock_list);
pwmr.parent = &pclk;
list_add_tail(&(pwmr.list),&clock_list);
sysctl_base = 0xbc04a000UL;
if (!sysctl_mod_is_enabled(SYSCTL_MOD_SYSCTL))
sysctl_mod_enable(SYSCTL_MOD_SYSCTL);
nocache_set_pll = (void *)0xbfc00000;
}
EXPORT_SYMBOL(clk_init);
/*
* This is the simple version of Loongson-2 wait, Maybe we need do this in
* interrupt disabled content
*/
MODULE_AUTHOR("LiuJianhua <liujianhua@china-cpu.com>");
MODULE_DESCRIPTION("clk for gsc3280");
MODULE_LICENSE("GPL");