/* * GSC3280 SoC Clock Controller Driver * * Copyright (C) 2013 BLX IC Design Corp.,Ltd. * Author: LiuJianhua * * 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 #include //#include #include #include 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<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 "); MODULE_DESCRIPTION("clk for gsc3280"); MODULE_LICENSE("GPL");