diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2009-06-14 11:01:44 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-06-14 11:01:44 +0100 |
commit | b7c11ec9f1c9f4272f032215ddb0b65d64b22ecc (patch) | |
tree | c65b81ef9ae5076d2137034accfa2610f45a13de /arch/arm/mach-u300 | |
parent | 06099961002a813324d057a91695af7c72939da6 (diff) | |
parent | 7666c17e2b0986a079da46122d8658544416c2cf (diff) |
Merge branch 'u300' into devel
Conflicts:
arch/arm/Makefile
Updates:
arch/arm/mach-u300/core.c
arch/arm/mach-u300/timer.c
Diffstat (limited to 'arch/arm/mach-u300')
28 files changed, 5318 insertions, 0 deletions
diff --git a/arch/arm/mach-u300/Kconfig b/arch/arm/mach-u300/Kconfig new file mode 100644 index 00000000000..337b9aabce4 --- /dev/null +++ b/arch/arm/mach-u300/Kconfig @@ -0,0 +1,105 @@ +if ARCH_U300 + +menu "ST-Ericsson AB U300/U330/U335/U365 Platform" + +comment "ST-Ericsson Mobile Platform Products" + +config MACH_U300 + bool "U300" + +comment "ST-Ericsson U300/U330/U335/U365 Feature Selections" + +choice + prompt "U300/U330/U335/U365 system type" + default MACH_U300_BS2X + ---help--- + You need to select the target system, i.e. the + U300/U330/U335/U365 board that you want to compile your kernel + for. + +config MACH_U300_BS2X + bool "S26/S26/B25/B26 Test Products" + depends on MACH_U300 + help + Select this if you're developing on the + S26/S25 test products. (Also works on + B26/B25 big boards.) + +config MACH_U300_BS330 + bool "S330/B330 Test Products" + depends on MACH_U300 + help + Select this if you're developing on the + S330/B330 test products. + +config MACH_U300_BS335 + bool "S335/B335 Test Products" + depends on MACH_U300 + help + Select this if you're developing on the + S335/B335 test products. + +config MACH_U300_BS365 + bool "S365/B365 Test Products" + depends on MACH_U300 + help + Select this if you're developing on the + S365/B365 test products. + +endchoice + +choice + prompt "Memory configuration" + default MACH_U300_SINGLE_RAM + ---help--- + You have to config the kernel according to the physical memory + configuration. + +config MACH_U300_SINGLE_RAM + bool "Single RAM" + help + Select this if you want support for Single RAM phones. + +config MACH_U300_DUAL_RAM + bool "Dual RAM" + help + Select this if you want support for Dual RAM phones. + This is two RAM memorys on different EMIFs. +endchoice + +config U300_DEBUG + bool "Debug support for U300" + depends on PM + help + Debug support for U300 in sysfs, procfs etc. + +config MACH_U300_SEMI_IS_SHARED + bool "The SEMI is used by both the access and application side" + depends on MACH_U300 + help + This makes it possible to use the SEMI (Shared External + Memory Interface) from both from access and application + side. + +comment "All the settings below must match the bootloader's settings" + +config MACH_U300_ACCESS_MEM_SIZE + int "Access CPU memory allocation" + range 7 25 + depends on MACH_U300_SINGLE_RAM + default 13 + help + How much memory in MiB that the Access side CPU has allocated + +config MACH_U300_2MB_ALIGNMENT_FIX + bool "2MiB alignment fix" + depends on MACH_U300_SINGLE_RAM + default y + help + If yes and the Access side CPU has allocated an odd size in + MiB, this fix gives you one MiB extra that would otherwise be + lost due to Linux 2 MiB alignment policy. + +endmenu + +endif diff --git a/arch/arm/mach-u300/Makefile b/arch/arm/mach-u300/Makefile new file mode 100644 index 00000000000..24950e0df4b --- /dev/null +++ b/arch/arm/mach-u300/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the linux kernel, U300 machine. +# + +obj-y := core.o clock.o timer.o gpio.o padmux.o +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_ARCH_U300) += u300.o +obj-$(CONFIG_MMC) += mmc.o diff --git a/arch/arm/mach-u300/Makefile.boot b/arch/arm/mach-u300/Makefile.boot new file mode 100644 index 00000000000..6fbfc6ea2d3 --- /dev/null +++ b/arch/arm/mach-u300/Makefile.boot @@ -0,0 +1,15 @@ +# Note: the following conditions must always be true: +# ZRELADDR == virt_to_phys(TEXTADDR) +# PARAMS_PHYS must be within 4MB of ZRELADDR +# INITRD_PHYS must be in RAM + +ifdef CONFIG_MACH_U300_SINGLE_RAM + zreladdr-y := 0x28E08000 + params_phys-y := 0x28E00100 +else + zreladdr-y := 0x48008000 + params_phys-y := 0x48000100 +endif + +# This isn't used. +#initrd_phys-y := 0x29800000 diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c new file mode 100644 index 00000000000..5cd04d6751b --- /dev/null +++ b/arch/arm/mach-u300/clock.c @@ -0,0 +1,1487 @@ +/* + * + * 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 <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, +}; + +/* + * 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, +}; + +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, +}; + +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, +}; + +#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, +}; + +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, +}; +#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_SY |