aboutsummaryrefslogblamecommitdiffstats
path: root/arch/arm/mach-u300/clock.c
blob: 5535dd0a78c9832572e02951770eb7e462149de4 (plain) (tree)

























                                                            
                           
                         
 






































                                                                       
                                       



































































































































































































                                                                       
                                                          


















                                                            
                                                          





































































































































































































































































































































                                                                                
                             
                                
                             
                                
                             
                                
                             
                                
                             
                                
                             
                                
                             
                                
                             





                                                                               
                             
                                
                             
                                
                              
                                 
                              










                                                          
                                                                         
































                                                                      
                               


















                                                                 
                                                          

















                                                          
                                                         


                                
                             







                                                          
                                                            











                                                    
                                                          













                                                          
                                                         












                                                          
                                                         













                                                          
                                                         












                                                          
                                                          












                                                          
                                                          













                                                                       
                                                               













                                                          
                                                          














                                                                         
                                                         
















                                                                 
                                                                










                                                          

                                                            












                                                          
                                                           













                                                          
                                                          
















                                                               
                                                          

  



                                                             













                                                          
                                                           













                                                            
                                                          













                                                            
                                                          













                                                          
                                                          













                                                          
                                                          

  



                                                            











                                                          
                                                         


                             

                                   
                                






                                                          







                                                            
                                                           

















                                                           
                                                          










                                                                    
                                                          

  

                                   
                                






                                                          









                                                            












                                                          
                                                            












                                                          
                                                          










                                                                 
                                                         












                                                          
                                                           












                                                          
                                                            












                                                          
                                                           

  



                                                         


                                   
                              






                                                          
                                                               













                                                                        
                                                         








                                                 






                                                 





                                                    

                                           
                                             
                                           
                             

                                          
      


                                           
                                                

                                           
                               

                                                  
                                                          

                                            
                                                       
                                

                                            
                                                       



                                                                               




                                           
                                                      
                             
                                            
                                                         

                                

                                                     

                                                         

                                                       
                                           



                                                   
                             
                                          




                                     
                                  
                                                       

 








































                                                                   
                    



                                

                    







































                                                                       
                                                            







                                                                          
                                                              


























                                                                           
                                             


                                                                    


                                                            
 





                                                          

      
                                





















                                                                          













                                                                            

                              


                 
/*
 *
 * 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 <linux/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
 *  +- FSMC 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 no one 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 no one 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 appropriate 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       = "FSMC",
	.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),
};

/*
 * The MMCI apb_pclk is hardwired to the same terminal as the
 * external MCI clock. Thus this will be referenced twice.
 */
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),
};

/*
 * The SPI apb_pclk is hardwired to the same terminal as the
 * external SPI clock. Thus this will be referenced twice.
 */
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_pclk = {
	.name	    = "UART1_PCLK",
	.parent	    = &fast_clk,
	.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_pclk.lock),
};

/* This one is hardwired to PLL13 */
static struct clk uart1_clk = {
	.name	    = "UART1_CLK",
	.rate	    = 13000000,
	.hw_ctrld   = true,
	.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),
};

static struct clk uart0_pclk = {
	.name	    = "UART0_PCLK",
	.parent	    = &slow_clk,
	.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(uart0_pclk.lock),
};

/* This one is hardwired to PLL13 */
static struct clk uart0_clk = {
	.name	    = "UART0_CLK",
	.parent	    = &slow_clk,
	.rate	    = 13000000,
	.hw_ctrld   = true,
	.lock       = __SPIN_LOCK_UNLOCKED(uart0_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),
};

/*
 * There is a binary divider in the hardware that divides
 * the 13MHz PLL by 13 down to 1 MHz.
 */
static struct clk app_timer_clk = {
	.name       = "TIMER_APP",
	.parent	    = &slow_clk,
	.rate       = 1000000,
	.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

#define DEF_LOOKUP(devid, clkref)		\
	{					\
	.dev_id = devid,			\
	.clk = clkref,				\
	}

#define DEF_LOOKUP_CON(devid, conid, clkref)	\
	{					\
	.dev_id = devid,			\
	.con_id = conid,			\
	.clk = clkref,				\
	}

/*
 * Here we only define clocks that are meaningful to
 * look up through clockdevice.
 */
static struct clk_lookup lookups[] = {
	/* Connected directly to the AMBA bus */
	DEF_LOOKUP("amba",      &amba_clk),
	DEF_LOOKUP("cpu",       &cpu_clk),
	DEF_LOOKUP("fsmc-nand", &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_CON("intcon", "apb_pclk", &intcon_clk),
	DEF_LOOKUP("mspro",     &mspro_clk),
	DEF_LOOKUP("pl172",     &emif_clk),
	DEF_LOOKUP_CON("pl172", "apb_pclk", &emif_clk),
	/* FAST bridge clocks */
	DEF_LOOKUP("fast",      &fast_clk),
	DEF_LOOKUP("mmci",      &mmcsd_clk),
	DEF_LOOKUP_CON("mmci", "apb_pclk", &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),
	DEF_LOOKUP_CON("pl022", "apb_pclk", &spi_clk),
#ifdef CONFIG_MACH_U300_BS335
	DEF_LOOKUP("uart1",     &uart1_clk),
	DEF_LOOKUP_CON("uart1", "apb_pclk", &uart1_pclk),
#endif
	/* SLOW bridge clocks */
	DEF_LOOKUP("slow",      &slow_clk),
	DEF_LOOKUP("coh901327_wdog",      &wdog_clk),
	DEF_LOOKUP("uart0",     &uart0_clk),
	DEF_LOOKUP_CON("uart0", "apb_pclk", &uart0_pclk),
	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));
}

#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,
	&uart1_pclk,
#endif
	/* SLOW bridge clocks */
	&slow_clk,
	&wdog_clk,
	&uart0_clk,
	&uart0_pclk,
	&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 || clk->rate != 0)
				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

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);
	clk_enable(&semi_clk);
	clk_enable(&emif_clk);

	return 0;
}