| /* |
| * |
| * arch/arm/mach-u300/clock.c |
| * |
| * |
| * Copyright (C) 2007-2009 ST-Ericsson AB |
| * License terms: GNU General Public License (GPL) version 2 |
| * Define clocks in the app platform. |
| * Author: Linus Walleij <linus.walleij@stericsson.com> |
| * Author: Jonas Aaberg <jonas.aberg@stericsson.com> |
| * |
| */ |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/list.h> |
| #include <linux/errno.h> |
| #include <linux/err.h> |
| #include <linux/string.h> |
| #include <linux/clk.h> |
| #include <linux/mutex.h> |
| #include <linux/spinlock.h> |
| #include <linux/debugfs.h> |
| #include <linux/device.h> |
| #include <linux/init.h> |
| #include <linux/timer.h> |
| #include <linux/io.h> |
| #include <linux/seq_file.h> |
| |
| #include <asm/clkdev.h> |
| #include <mach/hardware.h> |
| #include <mach/syscon.h> |
| |
| #include "clock.h" |
| |
| /* |
| * TODO: |
| * - move all handling of the CCR register into this file and create |
| * a spinlock for the CCR register |
| * - switch to the clkdevice lookup mechanism that maps clocks to |
| * device ID:s instead when it becomes available in kernel 2.6.29. |
| * - implement rate get/set for all clocks that need it. |
| */ |
| |
| /* |
| * Syscon clock I/O registers lock so clock requests don't collide |
| * NOTE: this is a local lock only used to lock access to clock and |
| * reset registers in syscon. |
| */ |
| static DEFINE_SPINLOCK(syscon_clkreg_lock); |
| static DEFINE_SPINLOCK(syscon_resetreg_lock); |
| |
| /* |
| * The clocking hierarchy currently looks like this. |
| * NOTE: the idea is NOT to show how the clocks are routed on the chip! |
| * The ideas is to show dependencies, so a clock higher up in the |
| * hierarchy has to be on in order for another clock to be on. Now, |
| * both CPU and DMA can actually be on top of the hierarchy, and that |
| * is not modeled currently. Instead we have the backbone AMBA bus on |
| * top. This bus cannot be programmed in any way but conceptually it |
| * needs to be active for the bridges and devices to transport data. |
| * |
| * Please be aware that a few clocks are hw controlled, which mean that |
| * the hw itself can turn on/off or change the rate of the clock when |
| * needed! |
| * |
| * AMBA bus |
| * | |
| * +- CPU |
| * +- NANDIF NAND Flash interface |
| * +- SEMI Shared Memory interface |
| * +- ISP Image Signal Processor (U335 only) |
| * +- CDS (U335 only) |
| * +- DMA Direct Memory Access Controller |
| * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) |
| * +- APEX |
| * +- VIDEO_ENC AVE2/3 Video Encoder |
| * +- XGAM Graphics Accelerator Controller |
| * +- AHB |
| * | |
| * +- ahb:0 AHB Bridge |
| * | | |
| * | +- ahb:1 INTCON Interrupt controller |
| * | +- ahb:3 MSPRO Memory Stick Pro controller |
| * | +- ahb:4 EMIF External Memory interface |
| * | |
| * +- fast:0 FAST bridge |
| * | | |
| * | +- fast:1 MMCSD MMC/SD card reader controller |
| * | +- fast:2 I2S0 PCM I2S channel 0 controller |
| * | +- fast:3 I2S1 PCM I2S channel 1 controller |
| * | +- fast:4 I2C0 I2C channel 0 controller |
| * | +- fast:5 I2C1 I2C channel 1 controller |
| * | +- fast:6 SPI SPI controller |
| * | +- fast:7 UART1 Secondary UART (U335 only) |
| * | |
| * +- slow:0 SLOW bridge |
| * | |
| * +- slow:1 SYSCON (not possible to control) |
| * +- slow:2 WDOG Watchdog |
| * +- slow:3 UART0 primary UART |
| * +- slow:4 TIMER_APP Application timer - used in Linux |
| * +- slow:5 KEYPAD controller |
| * +- slow:6 GPIO controller |
| * +- slow:7 RTC controller |
| * +- slow:8 BT Bus Tracer (not used currently) |
| * +- slow:9 EH Event Handler (not used currently) |
| * +- slow:a TIMER_ACC Access style timer (not used currently) |
| * +- slow:b PPM (U335 only, what is that?) |
| */ |
| |
| /* |
| * Reset control functions. We remember if a block has been |
| * taken out of reset and don't remove the reset assertion again |
| * and vice versa. Currently we only remove resets so the |
| * enablement function is defined out. |
| */ |
| static void syscon_block_reset_enable(struct clk *clk) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| /* Not all blocks support resetting */ |
| if (!clk->res_reg || !clk->res_mask) |
| return; |
| spin_lock_irqsave(&syscon_resetreg_lock, iflags); |
| val = readw(clk->res_reg); |
| val |= clk->res_mask; |
| writew(val, clk->res_reg); |
| spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); |
| clk->reset = true; |
| } |
| |
| static void syscon_block_reset_disable(struct clk *clk) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| /* Not all blocks support resetting */ |
| if (!clk->res_reg || !clk->res_mask) |
| return; |
| spin_lock_irqsave(&syscon_resetreg_lock, iflags); |
| val = readw(clk->res_reg); |
| val &= ~clk->res_mask; |
| writew(val, clk->res_reg); |
| spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); |
| clk->reset = false; |
| } |
| |
| int __clk_get(struct clk *clk) |
| { |
| u16 val; |
| |
| /* The MMC and MSPRO clocks need some special set-up */ |
| if (!strcmp(clk->name, "MCLK")) { |
| /* Set default MMC clock divisor to 18.9 MHz */ |
| writew(0x0054U, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); |
| /* Disable the MMC feedback clock */ |
| val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; |
| /* Disable MSPRO frequency */ |
| val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); |
| } |
| if (!strcmp(clk->name, "MSPRO")) { |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); |
| /* Disable the MMC feedback clock */ |
| val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; |
| /* Enable MSPRO frequency */ |
| val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); |
| } |
| return 1; |
| } |
| EXPORT_SYMBOL(__clk_get); |
| |
| void __clk_put(struct clk *clk) |
| { |
| } |
| EXPORT_SYMBOL(__clk_put); |
| |
| static void syscon_clk_disable(struct clk *clk) |
| { |
| unsigned long iflags; |
| |
| /* Don't touch the hardware controlled clocks */ |
| if (clk->hw_ctrld) |
| return; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCDR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| static void syscon_clk_enable(struct clk *clk) |
| { |
| unsigned long iflags; |
| |
| /* Don't touch the hardware controlled clocks */ |
| if (clk->hw_ctrld) |
| return; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCER); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| static u16 syscon_clk_get_rate(void) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| return val; |
| } |
| |
| #ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER |
| static void enable_i2s0_vcxo(void) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| /* Set I2S0 to use the VCXO 26 MHz clock */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val |= U300_SYSCON_CCR_TURN_VCXO_ON; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val |= U300_SYSCON_CCR_I2S0_USE_VCXO; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| val |= U300_SYSCON_CEFR_I2S0_CLK_EN; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| static void enable_i2s1_vcxo(void) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| /* Set I2S1 to use the VCXO 26 MHz clock */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val |= U300_SYSCON_CCR_TURN_VCXO_ON; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val |= U300_SYSCON_CCR_I2S1_USE_VCXO; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| val |= U300_SYSCON_CEFR_I2S1_CLK_EN; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| static void disable_i2s0_vcxo(void) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| /* Disable I2S0 use of the VCXO 26 MHz clock */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val &= ~U300_SYSCON_CCR_I2S0_USE_VCXO; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| /* Deactivate VCXO if noone else is using VCXO */ |
| if (!(val & U300_SYSCON_CCR_I2S1_USE_VCXO)) |
| val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| static void disable_i2s1_vcxo(void) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| /* Disable I2S1 use of the VCXO 26 MHz clock */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val &= ~U300_SYSCON_CCR_I2S1_USE_VCXO; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| /* Deactivate VCXO if noone else is using VCXO */ |
| if (!(val & U300_SYSCON_CCR_I2S0_USE_VCXO)) |
| val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| #endif /* CONFIG_MACH_U300_USE_I2S_AS_MASTER */ |
| |
| |
| static void syscon_clk_rate_set_mclk(unsigned long rate) |
| { |
| u16 val; |
| u32 reg; |
| unsigned long iflags; |
| |
| switch (rate) { |
| case 18900000: |
| val = 0x0054; |
| break; |
| case 20800000: |
| val = 0x0044; |
| break; |
| case 23100000: |
| val = 0x0043; |
| break; |
| case 26000000: |
| val = 0x0033; |
| break; |
| case 29700000: |
| val = 0x0032; |
| break; |
| case 34700000: |
| val = 0x0022; |
| break; |
| case 41600000: |
| val = 0x0021; |
| break; |
| case 52000000: |
| val = 0x0011; |
| break; |
| case 104000000: |
| val = 0x0000; |
| break; |
| default: |
| printk(KERN_ERR "Trying to set MCLK to unknown speed! %ld\n", |
| rate); |
| return; |
| } |
| |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| reg = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & |
| ~U300_SYSCON_MMF0R_MASK; |
| writew(reg | val, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| |
| void syscon_clk_rate_set_cpuclk(unsigned long rate) |
| { |
| u16 val; |
| unsigned long iflags; |
| |
| switch (rate) { |
| case 13000000: |
| val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; |
| break; |
| case 52000000: |
| val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; |
| break; |
| case 104000000: |
| val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; |
| break; |
| case 208000000: |
| val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; |
| break; |
| default: |
| return; |
| } |
| spin_lock_irqsave(&syscon_clkreg_lock, iflags); |
| val |= readw(U300_SYSCON_VBASE + U300_SYSCON_CCR) & |
| ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); |
| } |
| EXPORT_SYMBOL(syscon_clk_rate_set_cpuclk); |
| |
| void clk_disable(struct clk *clk) |
| { |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&clk->lock, iflags); |
| if (clk->usecount > 0 && !(--clk->usecount)) { |
| /* some blocks lack clocking registers and cannot be disabled */ |
| if (clk->disable) |
| clk->disable(clk); |
| if (likely((u32)clk->parent)) |
| clk_disable(clk->parent); |
| } |
| #ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER |
| if (unlikely(!strcmp(clk->name, "I2S0"))) |
| disable_i2s0_vcxo(); |
| if (unlikely(!strcmp(clk->name, "I2S1"))) |
| disable_i2s1_vcxo(); |
| #endif |
| spin_unlock_irqrestore(&clk->lock, iflags); |
| } |
| EXPORT_SYMBOL(clk_disable); |
| |
| int clk_enable(struct clk *clk) |
| { |
| int ret = 0; |
| unsigned long iflags; |
| |
| spin_lock_irqsave(&clk->lock, iflags); |
| if (clk->usecount++ == 0) { |
| if (likely((u32)clk->parent)) |
| ret = clk_enable(clk->parent); |
| |
| if (unlikely(ret != 0)) |
| clk->usecount--; |
| else { |
| /* remove reset line (we never enable reset again) */ |
| syscon_block_reset_disable(clk); |
| /* clocks without enable function are always on */ |
| if (clk->enable) |
| clk->enable(clk); |
| #ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER |
| if (unlikely(!strcmp(clk->name, "I2S0"))) |
| enable_i2s0_vcxo(); |
| if (unlikely(!strcmp(clk->name, "I2S1"))) |
| enable_i2s1_vcxo(); |
| #endif |
| } |
| } |
| spin_unlock_irqrestore(&clk->lock, iflags); |
| return ret; |
| |
| } |
| EXPORT_SYMBOL(clk_enable); |
| |
| /* Returns the clock rate in Hz */ |
| static unsigned long clk_get_rate_cpuclk(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| return 13000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| return 52000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| return 104000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| return 208000000; |
| default: |
| break; |
| } |
| return clk->rate; |
| } |
| |
| static unsigned long clk_get_rate_ahb_clk(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| return 6500000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| return 26000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| return 52000000; |
| default: |
| break; |
| } |
| return clk->rate; |
| |
| } |
| |
| static unsigned long clk_get_rate_emif_clk(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| return 13000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| return 52000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| return 104000000; |
| default: |
| break; |
| } |
| return clk->rate; |
| |
| } |
| |
| static unsigned long clk_get_rate_xgamclk(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| return 6500000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| return 26000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| return 52000000; |
| default: |
| break; |
| } |
| |
| return clk->rate; |
| } |
| |
| static unsigned long clk_get_rate_mclk(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| /* |
| * Here, the 208 MHz PLL gets shut down and the always |
| * on 13 MHz PLL used for RTC etc kicks into use |
| * instead. |
| */ |
| return 13000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| { |
| /* |
| * This clock is under program control. The register is |
| * divided in two nybbles, bit 7-4 gives cycles-1 to count |
| * high, bit 3-0 gives cycles-1 to count low. Distribute |
| * these with no more than 1 cycle difference between |
| * low and high and add low and high to get the actual |
| * divisor. The base PLL is 208 MHz. Writing 0x00 will |
| * divide by 1 and 1 so the highest frequency possible |
| * is 104 MHz. |
| * |
| * e.g. 0x54 => |
| * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz |
| */ |
| u16 val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & |
| U300_SYSCON_MMF0R_MASK; |
| switch (val) { |
| case 0x0054: |
| return 18900000; |
| case 0x0044: |
| return 20800000; |
| case 0x0043: |
| return 23100000; |
| case 0x0033: |
| return 26000000; |
| case 0x0032: |
| return 29700000; |
| case 0x0022: |
| return 34700000; |
| case 0x0021: |
| return 41600000; |
| case 0x0011: |
| return 52000000; |
| case 0x0000: |
| return 104000000; |
| default: |
| break; |
| } |
| } |
| default: |
| break; |
| } |
| |
| return clk->rate; |
| } |
| |
| static unsigned long clk_get_rate_i2s_i2c_spi(struct clk *clk) |
| { |
| u16 val; |
| |
| val = syscon_clk_get_rate(); |
| |
| switch (val) { |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: |
| return 13000000; |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: |
| case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: |
| return 26000000; |
| default: |
| break; |
| } |
| |
| return clk->rate; |
| } |
| |
| unsigned long clk_get_rate(struct clk *clk) |
| { |
| if (clk->get_rate) |
| return clk->get_rate(clk); |
| else |
| return clk->rate; |
| } |
| EXPORT_SYMBOL(clk_get_rate); |
| |
| static unsigned long clk_round_rate_mclk(struct clk *clk, unsigned long rate) |
| { |
| if (rate <= 18900000) |
| return 18900000; |
| if (rate <= 20800000) |
| return 20800000; |
| if (rate <= 23100000) |
| return 23100000; |
| if (rate <= 26000000) |
| return 26000000; |
| if (rate <= 29700000) |
| return 29700000; |
| if (rate <= 34700000) |
| return 34700000; |
| if (rate <= 41600000) |
| return 41600000; |
| if (rate <= 52000000) |
| return 52000000; |
| return -EINVAL; |
| } |
| |
| static unsigned long clk_round_rate_cpuclk(struct clk *clk, unsigned long rate) |
| { |
| if (rate <= 13000000) |
| return 13000000; |
| if (rate <= 52000000) |
| return 52000000; |
| if (rate <= 104000000) |
| return 104000000; |
| if (rate <= 208000000) |
| return 208000000; |
| return -EINVAL; |
| } |
| |
| /* |
| * This adjusts a requested rate to the closest exact rate |
| * a certain clock can provide. For a fixed clock it's |
| * mostly clk->rate. |
| */ |
| long clk_round_rate(struct clk *clk, unsigned long rate) |
| { |
| /* TODO: get apropriate switches for EMIFCLK, AHBCLK and MCLK */ |
| /* Else default to fixed value */ |
| |
| if (clk->round_rate) { |
| return (long) clk->round_rate(clk, rate); |
| } else { |
| printk(KERN_ERR "clock: Failed to round rate of %s\n", |
| clk->name); |
| } |
| return (long) clk->rate; |
| } |
| EXPORT_SYMBOL(clk_round_rate); |
| |
| static int clk_set_rate_mclk(struct clk *clk, unsigned long rate) |
| { |
| syscon_clk_rate_set_mclk(clk_round_rate(clk, rate)); |
| return 0; |
| } |
| |
| static int clk_set_rate_cpuclk(struct clk *clk, unsigned long rate) |
| { |
| syscon_clk_rate_set_cpuclk(clk_round_rate(clk, rate)); |
| return 0; |
| } |
| |
| int clk_set_rate(struct clk *clk, unsigned long rate) |
| { |
| /* TODO: set for EMIFCLK and AHBCLK */ |
| /* Else assume the clock is fixed and fail */ |
| if (clk->set_rate) { |
| return clk->set_rate(clk, rate); |
| } else { |
| printk(KERN_ERR "clock: Failed to set %s to %ld hz\n", |
| clk->name, rate); |
| return -EINVAL; |
| } |
| } |
| EXPORT_SYMBOL(clk_set_rate); |
| |
| /* |
| * Clock definitions. The clock parents are set to respective |
| * bridge and the clock framework makes sure that the clocks have |
| * parents activated and are brought out of reset when in use. |
| * |
| * Clocks that have hw_ctrld = true are hw controlled, and the hw |
| * can by itself turn these clocks on and off. |
| * So in other words, we don't really have to care about them. |
| */ |
| |
| static struct clk amba_clk = { |
| .name = "AMBA", |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = false, |
| .lock = __SPIN_LOCK_UNLOCKED(amba_clk.lock), |
| }; |
| |
| /* |
| * These blocks are connected directly to the AMBA bus |
| * with no bridge. |
| */ |
| |
| static struct clk cpu_clk = { |
| .name = "CPU", |
| .parent = &amba_clk, |
| .rate = 208000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_CPU_RESET_EN, |
| .set_rate = clk_set_rate_cpuclk, |
| .get_rate = clk_get_rate_cpuclk, |
| .round_rate = clk_round_rate_cpuclk, |
| .lock = __SPIN_LOCK_UNLOCKED(cpu_clk.lock), |
| }; |
| |
| static struct clk nandif_clk = { |
| .name = "NANDIF", |
| .parent = &amba_clk, |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_NANDIF_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_NANDIF_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(nandif_clk.lock), |
| }; |
| |
| static struct clk semi_clk = { |
| .name = "SEMI", |
| .parent = &amba_clk, |
| .rate = 0, /* FIXME */ |
| /* It is not possible to reset SEMI */ |
| .hw_ctrld = false, |
| .reset = false, |
| .clk_val = U300_SYSCON_SBCER_SEMI_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(semi_clk.lock), |
| }; |
| |
| #ifdef CONFIG_MACH_U300_BS335 |
| static struct clk isp_clk = { |
| .name = "ISP", |
| .parent = &amba_clk, |
| .rate = 0, /* FIXME */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_ISP_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_ISP_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(isp_clk.lock), |
| }; |
| |
| static struct clk cds_clk = { |
| .name = "CDS", |
| .parent = &amba_clk, |
| .rate = 0, /* FIXME */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_CDS_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_CDS_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(cds_clk.lock), |
| }; |
| #endif |
| |
| static struct clk dma_clk = { |
| .name = "DMA", |
| .parent = &amba_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_DMAC_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_DMAC_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(dma_clk.lock), |
| }; |
| |
| static struct clk aaif_clk = { |
| .name = "AAIF", |
| .parent = &amba_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_AAIF_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_AAIF_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(aaif_clk.lock), |
| }; |
| |
| static struct clk apex_clk = { |
| .name = "APEX", |
| .parent = &amba_clk, |
| .rate = 0, /* FIXME */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_APEX_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_APEX_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(apex_clk.lock), |
| }; |
| |
| static struct clk video_enc_clk = { |
| .name = "VIDEO_ENC", |
| .parent = &amba_clk, |
| .rate = 208000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = false, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| /* This has XGAM in the name but refers to the video encoder */ |
| .res_mask = U300_SYSCON_RRR_XGAM_VC_SYNC_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_VIDEO_ENC_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(video_enc_clk.lock), |
| }; |
| |
| static struct clk xgam_clk = { |
| .name = "XGAMCLK", |
| .parent = &amba_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_XGAM_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_XGAM_CLK_EN, |
| .get_rate = clk_get_rate_xgamclk, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(xgam_clk.lock), |
| }; |
| |
| /* This clock is used to activate the video encoder */ |
| static struct clk ahb_clk = { |
| .name = "AHB", |
| .parent = &amba_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = false, /* This one is set to false due to HW bug */ |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_AHB_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_AHB_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_ahb_clk, |
| .lock = __SPIN_LOCK_UNLOCKED(ahb_clk.lock), |
| }; |
| |
| |
| /* |
| * Clocks on the AHB bridge |
| */ |
| |
| static struct clk ahb_subsys_clk = { |
| .name = "AHB_SUBSYS", |
| .parent = &amba_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = false, |
| .clk_val = U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_ahb_clk, |
| .lock = __SPIN_LOCK_UNLOCKED(ahb_subsys_clk.lock), |
| }; |
| |
| static struct clk intcon_clk = { |
| .name = "INTCON", |
| .parent = &ahb_subsys_clk, |
| .rate = 52000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_INTCON_RESET_EN, |
| /* INTCON can be reset but not clock-gated */ |
| .lock = __SPIN_LOCK_UNLOCKED(intcon_clk.lock), |
| |
| }; |
| |
| static struct clk mspro_clk = { |
| .name = "MSPRO", |
| .parent = &ahb_subsys_clk, |
| .rate = 0, /* FIXME */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_MSPRO_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_MSPRO_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(mspro_clk.lock), |
| }; |
| |
| static struct clk emif_clk = { |
| .name = "EMIF", |
| .parent = &ahb_subsys_clk, |
| .rate = 104000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, |
| .res_mask = U300_SYSCON_RRR_EMIF_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_EMIF_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_emif_clk, |
| .lock = __SPIN_LOCK_UNLOCKED(emif_clk.lock), |
| }; |
| |
| |
| /* |
| * Clocks on the FAST bridge |
| */ |
| static struct clk fast_clk = { |
| .name = "FAST_BRIDGE", |
| .parent = &amba_clk, |
| .rate = 13000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_FAST_BRIDGE_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(fast_clk.lock), |
| }; |
| |
| static struct clk mmcsd_clk = { |
| .name = "MCLK", |
| .parent = &fast_clk, |
| .rate = 18900000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_MMC_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_MMC_CLK_EN, |
| .get_rate = clk_get_rate_mclk, |
| .set_rate = clk_set_rate_mclk, |
| .round_rate = clk_round_rate_mclk, |
| .disable = syscon_clk_disable, |
| .enable = syscon_clk_enable, |
| .lock = __SPIN_LOCK_UNLOCKED(mmcsd_clk.lock), |
| }; |
| |
| static struct clk i2s0_clk = { |
| .name = "i2s0", |
| .parent = &fast_clk, |
| .rate = 26000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_PCM_I2S0_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_I2S0_CORE_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_i2s_i2c_spi, |
| .lock = __SPIN_LOCK_UNLOCKED(i2s0_clk.lock), |
| }; |
| |
| static struct clk i2s1_clk = { |
| .name = "i2s1", |
| .parent = &fast_clk, |
| .rate = 26000000, /* this varies! */ |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_PCM_I2S1_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_I2S1_CORE_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_i2s_i2c_spi, |
| .lock = __SPIN_LOCK_UNLOCKED(i2s1_clk.lock), |
| }; |
| |
| static struct clk i2c0_clk = { |
| .name = "I2C0", |
| .parent = &fast_clk, |
| .rate = 26000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_I2C0_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_I2C0_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_i2s_i2c_spi, |
| .lock = __SPIN_LOCK_UNLOCKED(i2c0_clk.lock), |
| }; |
| |
| static struct clk i2c1_clk = { |
| .name = "I2C1", |
| .parent = &fast_clk, |
| .rate = 26000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_I2C1_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_I2C1_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_i2s_i2c_spi, |
| .lock = __SPIN_LOCK_UNLOCKED(i2c1_clk.lock), |
| }; |
| |
| static struct clk spi_clk = { |
| .name = "SPI", |
| .parent = &fast_clk, |
| .rate = 26000000, /* this varies! */ |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_SPI_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_SPI_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .get_rate = clk_get_rate_i2s_i2c_spi, |
| .lock = __SPIN_LOCK_UNLOCKED(spi_clk.lock), |
| }; |
| |
| #ifdef CONFIG_MACH_U300_BS335 |
| static struct clk uart1_clk = { |
| .name = "UART1", |
| .parent = &fast_clk, |
| .rate = 13000000, |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, |
| .res_mask = U300_SYSCON_RFR_UART1_RESET_ENABLE, |
| .clk_val = U300_SYSCON_SBCER_UART1_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(uart1_clk.lock), |
| }; |
| #endif |
| |
| |
| /* |
| * Clocks on the SLOW bridge |
| */ |
| static struct clk slow_clk = { |
| .name = "SLOW_BRIDGE", |
| .parent = &amba_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_SLOW_BRIDGE_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(slow_clk.lock), |
| }; |
| |
| /* TODO: implement SYSCON clock? */ |
| |
| static struct clk wdog_clk = { |
| .name = "WDOG", |
| .parent = &slow_clk, |
| .hw_ctrld = false, |
| .rate = 32768, |
| .reset = false, |
| /* This is always on, cannot be enabled/disabled or reset */ |
| .lock = __SPIN_LOCK_UNLOCKED(wdog_clk.lock), |
| }; |
| |
| /* This one is hardwired to PLL13 */ |
| static struct clk uart_clk = { |
| .name = "UARTCLK", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_UART_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_UART_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(uart_clk.lock), |
| }; |
| |
| static struct clk keypad_clk = { |
| .name = "KEYPAD", |
| .parent = &slow_clk, |
| .rate = 32768, |
| .hw_ctrld = false, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_KEYPAD_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_KEYPAD_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(keypad_clk.lock), |
| }; |
| |
| static struct clk gpio_clk = { |
| .name = "GPIO", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_GPIO_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_GPIO_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(gpio_clk.lock), |
| }; |
| |
| static struct clk rtc_clk = { |
| .name = "RTC", |
| .parent = &slow_clk, |
| .rate = 32768, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_RTC_RESET_EN, |
| /* This clock is always on, cannot be enabled/disabled */ |
| .lock = __SPIN_LOCK_UNLOCKED(rtc_clk.lock), |
| }; |
| |
| static struct clk bustr_clk = { |
| .name = "BUSTR", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_BTR_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_BTR_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(bustr_clk.lock), |
| }; |
| |
| static struct clk evhist_clk = { |
| .name = "EVHIST", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_EH_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_EH_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(evhist_clk.lock), |
| }; |
| |
| static struct clk timer_clk = { |
| .name = "TIMER", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_ACC_TMR_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_ACC_TMR_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(timer_clk.lock), |
| }; |
| |
| static struct clk app_timer_clk = { |
| .name = "TIMER_APP", |
| .parent = &slow_clk, |
| .rate = 13000000, |
| .hw_ctrld = true, |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_APP_TMR_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_APP_TMR_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(app_timer_clk.lock), |
| }; |
| |
| #ifdef CONFIG_MACH_U300_BS335 |
| static struct clk ppm_clk = { |
| .name = "PPM", |
| .parent = &slow_clk, |
| .rate = 0, /* FIXME */ |
| .hw_ctrld = true, /* TODO: Look up if it is hw ctrld or not */ |
| .reset = true, |
| .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, |
| .res_mask = U300_SYSCON_RSR_PPM_RESET_EN, |
| .clk_val = U300_SYSCON_SBCER_PPM_CLK_EN, |
| .enable = syscon_clk_enable, |
| .disable = syscon_clk_disable, |
| .lock = __SPIN_LOCK_UNLOCKED(ppm_clk.lock), |
| }; |
| #endif |
| |
| static struct clk dummy_apb_pclk; |
| |
| #define DEF_LOOKUP(devid, clkref) \ |
| { \ |
| .dev_id = devid, \ |
| .clk = clkref, \ |
| } |
| |
| /* |
| * Here we only define clocks that are meaningful to |
| * look up through clockdevice. |
| */ |
| static struct clk_lookup lookups[] = { |
| { |
| .con_id = "apb_pclk", |
| .clk = &dummy_apb_pclk, |
| }, |
| /* Connected directly to the AMBA bus */ |
| DEF_LOOKUP("amba", &amba_clk), |
| DEF_LOOKUP("cpu", &cpu_clk), |
| DEF_LOOKUP("fsmc", &nandif_clk), |
| DEF_LOOKUP("semi", &semi_clk), |
| #ifdef CONFIG_MACH_U300_BS335 |
| DEF_LOOKUP("isp", &isp_clk), |
| DEF_LOOKUP("cds", &cds_clk), |
| #endif |
| DEF_LOOKUP("dma", &dma_clk), |
| DEF_LOOKUP("msl", &aaif_clk), |
| DEF_LOOKUP("apex", &apex_clk), |
| DEF_LOOKUP("video_enc", &video_enc_clk), |
| DEF_LOOKUP("xgam", &xgam_clk), |
| DEF_LOOKUP("ahb", &ahb_clk), |
| /* AHB bridge clocks */ |
| DEF_LOOKUP("ahb_subsys", &ahb_subsys_clk), |
| DEF_LOOKUP("intcon", &intcon_clk), |
| DEF_LOOKUP("mspro", &mspro_clk), |
| DEF_LOOKUP("pl172", &emif_clk), |
| /* FAST bridge clocks */ |
| DEF_LOOKUP("fast", &fast_clk), |
| DEF_LOOKUP("mmci", &mmcsd_clk), |
| /* |
| * The .0 and .1 identifiers on these comes from the platform device |
| * .id field and are assigned when the platform devices are registered. |
| */ |
| DEF_LOOKUP("i2s.0", &i2s0_clk), |
| DEF_LOOKUP("i2s.1", &i2s1_clk), |
| DEF_LOOKUP("stu300.0", &i2c0_clk), |
| DEF_LOOKUP("stu300.1", &i2c1_clk), |
| DEF_LOOKUP("pl022", &spi_clk), |
| #ifdef CONFIG_MACH_U300_BS335 |
| DEF_LOOKUP("uart1", &uart1_clk), |
| #endif |
| /* SLOW bridge clocks */ |
| DEF_LOOKUP("slow", &slow_clk), |
| DEF_LOOKUP("coh901327_wdog", &wdog_clk), |
| DEF_LOOKUP("uart0", &uart_clk), |
| DEF_LOOKUP("apptimer", &app_timer_clk), |
| DEF_LOOKUP("coh901461-keypad", &keypad_clk), |
| DEF_LOOKUP("u300-gpio", &gpio_clk), |
| DEF_LOOKUP("rtc-coh901331", &rtc_clk), |
| DEF_LOOKUP("bustr", &bustr_clk), |
| DEF_LOOKUP("evhist", &evhist_clk), |
| DEF_LOOKUP("timer", &timer_clk), |
| #ifdef CONFIG_MACH_U300_BS335 |
| DEF_LOOKUP("ppm", &ppm_clk), |
| #endif |
| }; |
| |
| static void __init clk_register(void) |
| { |
| /* Register the lookups */ |
| clkdev_add_table(lookups, ARRAY_SIZE(lookups)); |
| } |
| |
| /* |
| * These are the clocks for cells registered as primecell drivers |
| * on the AMBA bus. These must be on during AMBA device registration |
| * since the bus probe will attempt to read magic configuration |
| * registers for these devices. If they are deactivated these probes |
| * will fail. |
| * |
| * |
| * Please note that on emif, both RAM and NAND is connected in dual |
| * RAM phones. On single RAM phones, ram is on semi and NAND on emif. |
| * |
| */ |
| void u300_clock_primecells(void) |
| { |
| clk_enable(&intcon_clk); |
| clk_enable(&uart_clk); |
| #ifdef CONFIG_MACH_U300_BS335 |
| clk_enable(&uart1_clk); |
| #endif |
| clk_enable(&spi_clk); |
| |
| clk_enable(&mmcsd_clk); |
| |
| } |
| EXPORT_SYMBOL(u300_clock_primecells); |
| |
| void u300_unclock_primecells(void) |
| { |
| |
| clk_disable(&intcon_clk); |
| clk_disable(&uart_clk); |
| #ifdef CONFIG_MACH_U300_BS335 |
| clk_disable(&uart1_clk); |
| #endif |
| clk_disable(&spi_clk); |
| clk_disable(&mmcsd_clk); |
| |
| } |
| EXPORT_SYMBOL(u300_unclock_primecells); |
| |
| /* |
| * The interrupt controller is enabled before the clock API is registered. |
| */ |
| void u300_enable_intcon_clock(void) |
| { |
| clk_enable(&intcon_clk); |
| } |
| EXPORT_SYMBOL(u300_enable_intcon_clock); |
| |
| /* |
| * The timer is enabled before the clock API is registered. |
| */ |
| void u300_enable_timer_clock(void) |
| { |
| clk_enable(&app_timer_clk); |
| } |
| EXPORT_SYMBOL(u300_enable_timer_clock); |
| |
| #if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) |
| /* |
| * The following makes it possible to view the status (especially |
| * reference count and reset status) for the clocks in the platform |
| * by looking into the special file <debugfs>/u300_clocks |
| */ |
| |
| /* A list of all clocks in the platform */ |
| static struct clk *clks[] = { |
| /* Top node clock for the AMBA bus */ |
| &amba_clk, |
| /* Connected directly to the AMBA bus */ |
| &cpu_clk, |
| &nandif_clk, |
| &semi_clk, |
| #ifdef CONFIG_MACH_U300_BS335 |
| &isp_clk, |
| &cds_clk, |
| #endif |
| &dma_clk, |
| &aaif_clk, |
| &apex_clk, |
| &video_enc_clk, |
| &xgam_clk, |
| &ahb_clk, |
| |
| /* AHB bridge clocks */ |
| &ahb_subsys_clk, |
| &intcon_clk, |
| &mspro_clk, |
| &emif_clk, |
| /* FAST bridge clocks */ |
| &fast_clk, |
| &mmcsd_clk, |
| &i2s0_clk, |
| &i2s1_clk, |
| &i2c0_clk, |
| &i2c1_clk, |
| &spi_clk, |
| #ifdef CONFIG_MACH_U300_BS335 |
| &uart1_clk, |
| #endif |
| /* SLOW bridge clocks */ |
| &slow_clk, |
| &wdog_clk, |
| &uart_clk, |
| &app_timer_clk, |
| &keypad_clk, |
| &gpio_clk, |
| &rtc_clk, |
| &bustr_clk, |
| &evhist_clk, |
| &timer_clk, |
| #ifdef CONFIG_MACH_U300_BS335 |
| &ppm_clk, |
| #endif |
| }; |
| |
| static int u300_clocks_show(struct seq_file *s, void *data) |
| { |
| struct clk *clk; |
| int i; |
| |
| seq_printf(s, "CLOCK DEVICE RESET STATE\t" \ |
| "ACTIVE\tUSERS\tHW CTRL FREQ\n"); |
| seq_printf(s, "---------------------------------------------" \ |
| "-----------------------------------------\n"); |
| for (i = 0; i < ARRAY_SIZE(clks); i++) { |
| clk = clks[i]; |
| if (clk != ERR_PTR(-ENOENT)) { |
| /* Format clock and device name nicely */ |
| char cdp[33]; |
| int chars; |
| |
| chars = snprintf(&cdp[0], 17, "%s", clk->name); |
| while (chars < 16) { |
| cdp[chars] = ' '; |
| chars++; |
| } |
| chars = snprintf(&cdp[16], 17, "%s", clk->dev ? |
| dev_name(clk->dev) : "N/A"); |
| while (chars < 16) { |
| cdp[chars+16] = ' '; |
| chars++; |
| } |
| cdp[32] = '\0'; |
| if (clk->get_rate) |
| seq_printf(s, |
| "%s%s\t%s\t%d\t%s\t%lu Hz\n", |
| &cdp[0], |
| clk->reset ? |
| "ASSERTED" : "RELEASED", |
| clk->usecount ? "ON" : "OFF", |
| clk->usecount, |
| clk->hw_ctrld ? "YES" : "NO ", |
| clk->get_rate(clk)); |
| else |
| seq_printf(s, |
| "%s%s\t%s\t%d\t%s\t" \ |
| "(unknown rate)\n", |
| &cdp[0], |
| clk->reset ? |
| "ASSERTED" : "RELEASED", |
| clk->usecount ? "ON" : "OFF", |
| clk->usecount, |
| clk->hw_ctrld ? "YES" : "NO "); |
| } |
| } |
| return 0; |
| } |
| |
| static int u300_clocks_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, u300_clocks_show, NULL); |
| } |
| |
| static const struct file_operations u300_clocks_operations = { |
| .open = u300_clocks_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| static int __init init_clk_read_debugfs(void) |
| { |
| /* Expose a simple debugfs interface to view all clocks */ |
| (void) debugfs_create_file("u300_clocks", S_IFREG | S_IRUGO, |
| NULL, NULL, |
| &u300_clocks_operations); |
| return 0; |
| } |
| /* |
| * This needs to come in after the core_initcall() for the |
| * overall clocks, because debugfs is not available until |
| * the subsystems come up. |
| */ |
| module_init(init_clk_read_debugfs); |
| #endif |
| |
| static int __init u300_clock_init(void) |
| { |
| u16 val; |
| |
| /* |
| * FIXME: shall all this powermanagement stuff really live here??? |
| */ |
| |
| /* Set system to run at PLL208, max performance, a known state. */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); |
| /* Wait for the PLL208 to lock if not locked in yet */ |
| while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & |
| U300_SYSCON_CSR_PLL208_LOCK_IND)); |
| |
| /* Power management enable */ |
| val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMCR); |
| val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; |
| writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMCR); |
| |
| clk_register(); |
| |
| /* |
| * Some of these may be on when we boot the system so make sure they |
| * are turned OFF. |
| */ |
| syscon_block_reset_enable(&timer_clk); |
| timer_clk.disable(&timer_clk); |
| |
| /* |
| * These shall be turned on by default when we boot the system |
| * so make sure they are ON. (Adding CPU here is a bit too much.) |
| * These clocks will be claimed by drivers later. |
| */ |
| syscon_block_reset_disable(&semi_clk); |
| syscon_block_reset_disable(&emif_clk); |
| semi_clk.enable(&semi_clk); |
| emif_clk.enable(&emif_clk); |
| |
| return 0; |
| } |
| /* initialize clocking early to be available later in the boot */ |
| core_initcall(u300_clock_init); |