diff options
Diffstat (limited to 'arch/arm/cpu/armv7/u8500')
-rw-r--r-- | arch/arm/cpu/armv7/u8500/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/u8500/clock.c | 34 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/u8500/cpu.c | 192 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/u8500/prcmu.c | 229 |
4 files changed, 456 insertions, 1 deletions
diff --git a/arch/arm/cpu/armv7/u8500/Makefile b/arch/arm/cpu/armv7/u8500/Makefile index 270aa40c88..ce8af96038 100644 --- a/arch/arm/cpu/armv7/u8500/Makefile +++ b/arch/arm/cpu/armv7/u8500/Makefile @@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o -COBJS = timer.o clock.o +COBJS = timer.o clock.o prcmu.o cpu.o SOBJS = lowlevel.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/u8500/clock.c b/arch/arm/cpu/armv7/u8500/clock.c index 9e3b87394d..fcfd61a1f4 100644 --- a/arch/arm/cpu/armv7/u8500/clock.c +++ b/arch/arm/cpu/armv7/u8500/clock.c @@ -54,3 +54,37 @@ void u8500_clock_enable(int periph, int cluster, int kern) if (cluster != -1) writel(1 << cluster, &clkrst->pcken); } + +void db8500_clocks_init(void) +{ + /* + * Enable all clocks. This is u-boot, we can enable it all. There is no + * powersave in u-boot. + */ + + u8500_clock_enable(1, 9, -1); /* GPIO0 */ + u8500_clock_enable(2, 11, -1);/* GPIO1 */ + u8500_clock_enable(3, 8, -1); /* GPIO2 */ + u8500_clock_enable(5, 1, -1); /* GPIO3 */ + u8500_clock_enable(3, 6, 6); /* UART2 */ + u8500_clock_enable(3, 3, 3); /* I2C0 */ + u8500_clock_enable(1, 5, 5); /* SDI0 */ + u8500_clock_enable(2, 4, 2); /* SDI4 */ + u8500_clock_enable(6, 6, -1); /* MTU0 */ + u8500_clock_enable(3, 4, 4); /* SDI2 */ + + /* + * Enabling clocks for all devices which are AMBA devices in the + * kernel. Otherwise they will not get probe()'d because the + * peripheral ID register will not be powered. + */ + + /* XXX: some of these differ between ED/V1 */ + + u8500_clock_enable(1, 1, 1); /* UART1 */ + u8500_clock_enable(1, 0, 0); /* UART0 */ + u8500_clock_enable(3, 2, 2); /* SSP1 */ + u8500_clock_enable(3, 1, 1); /* SSP0 */ + u8500_clock_enable(2, 8, -1); /* SPI0 */ + u8500_clock_enable(2, 5, 3); /* MSP2 */ +} diff --git a/arch/arm/cpu/armv7/u8500/cpu.c b/arch/arm/cpu/armv7/u8500/cpu.c new file mode 100644 index 0000000000..6f95c30673 --- /dev/null +++ b/arch/arm/cpu/armv7/u8500/cpu.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2012 Linaro Limited + * Mathieu Poirier <mathieu.poirier@linaro.org> + * + * Based on original code from Joakim Axelsson at ST-Ericsson + * (C) Copyright 2010 ST-Ericsson + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/prcmu.h> +#include <asm/arch/clock.h> +#include <asm/arch/hardware.h> + +#include <asm/arch/hardware.h> + +#define CPUID_DB8500V1 0x411fc091 +#define CPUID_DB8500V2 0x412fc091 +#define ASICID_DB8500V11 0x008500A1 + +#define CACHE_CONTR_BASE 0xA0412000 +/* Cache controller register offsets + * as found in ARM's technical reference manual + */ +#define CACHE_INVAL_BY_WAY (CACHE_CONTR_BASE + 0x77C) +#define CACHE_LOCKDOWN_BY_D (CACHE_CONTR_BASE + 0X900) +#define CACHE_LOCKDOWN_BY_I (CACHE_CONTR_BASE + 0X904) + +static unsigned int read_asicid(void); + +static inline unsigned int read_cpuid(void) +{ + unsigned int val; + + /* Main ID register (MIDR) */ + asm("mrc p15, 0, %0, c0, c0, 0" + : "=r" (val) + : + : "cc"); + + return val; +} + +static int cpu_is_u8500v11(void) +{ + return read_asicid() == ASICID_DB8500V11; +} + +static int cpu_is_u8500v2(void) +{ + return read_cpuid() == CPUID_DB8500V2; +} + +static unsigned int read_asicid(void) +{ + unsigned int *address; + + if (cpu_is_u8500v2()) + address = (void *) U8500_ASIC_ID_LOC_V2; + else + address = (void *) U8500_ASIC_ID_LOC_ED_V1; + + return readl(address); +} + +void cpu_cache_initialization(void) +{ + unsigned int value; + /* invalidate all cache entries */ + writel(0xFFFF, CACHE_INVAL_BY_WAY); + + /* ways are set to '0' when they are totally + * cleaned and invalidated + */ + do { + value = readl(CACHE_INVAL_BY_WAY); + } while (value & 0xFF); + + /* Invalidate register 9 D and I lockdown */ + writel(0xFF, CACHE_LOCKDOWN_BY_D); + writel(0xFF, CACHE_LOCKDOWN_BY_I); +} + +#ifdef CONFIG_ARCH_CPU_INIT +/* + * SOC specific cpu init + */ +int arch_cpu_init(void) +{ + db8500_prcmu_init(); + db8500_clocks_init(); + + return 0; +} +#endif /* CONFIG_ARCH_CPU_INIT */ + +#ifdef CONFIG_MMC + +int u8500_mmc_power_init(void) +{ + int ret; + int enable, voltage; + int ab8500_revision; + + if (!cpu_is_u8500v11() && !cpu_is_u8500v2()) + return 0; + + /* Get AB8500 revision */ + ret = ab8500_read(AB8500_MISC, AB8500_REV_REG); + if (ret < 0) + goto out; + + ab8500_revision = ret; + + /* + * On v1.1 HREF boards (HREF+), Vaux3 needs to be enabled for the SD + * card to work. This is done by enabling the regulators in the AB8500 + * via PRCMU I2C transactions. + * + * This code is derived from the handling of AB8500_LDO_VAUX3 in + * ab8500_ldo_enable() and ab8500_ldo_disable() in Linux. + * + * Turn off and delay is required to have it work across soft reboots. + */ + + /* Turn off (read-modify-write) */ + ret = ab8500_read(AB8500_REGU_CTRL2, + AB8500_REGU_VRF1VAUX3_REGU_REG); + if (ret < 0) + goto out; + + enable = ret; + + /* Turn off */ + ret = ab8500_write(AB8500_REGU_CTRL2, + AB8500_REGU_VRF1VAUX3_REGU_REG, + enable & ~LDO_VAUX3_ENABLE_MASK); + if (ret < 0) + goto out; + + udelay(10 * 1000); + + /* Set the voltage to 2.91 V or 2.9 V without overriding VRF1 value */ + ret = ab8500_read(AB8500_REGU_CTRL2, + AB8500_REGU_VRF1VAUX3_SEL_REG); + if (ret < 0) + goto out; + + voltage = ret; + + if (ab8500_revision < 0x20) { + voltage &= ~LDO_VAUX3_SEL_MASK; + voltage |= LDO_VAUX3_SEL_2V9; + } else { + voltage &= ~LDO_VAUX3_V2_SEL_MASK; + voltage |= LDO_VAUX3_V2_SEL_2V91; + } + + ret = ab8500_write(AB8500_REGU_CTRL2, + AB8500_REGU_VRF1VAUX3_SEL_REG, voltage); + if (ret < 0) + goto out; + + /* Turn on the supply */ + enable &= ~LDO_VAUX3_ENABLE_MASK; + enable |= LDO_VAUX3_ENABLE_VAL; + + ret = ab8500_write(AB8500_REGU_CTRL2, + AB8500_REGU_VRF1VAUX3_REGU_REG, enable); + +out: + return ret; +} +#endif /* CONFIG_MMC */ diff --git a/arch/arm/cpu/armv7/u8500/prcmu.c b/arch/arm/cpu/armv7/u8500/prcmu.c new file mode 100644 index 0000000000..934428fb89 --- /dev/null +++ b/arch/arm/cpu/armv7/u8500/prcmu.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2009 ST-Ericsson SA + * + * Adapted from the Linux version: + * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +/* + * NOTE: This currently does not support the I2C workaround access method. + */ + +#include <common.h> +#include <config.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/types.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/prcmu.h> + +/* CPU mailbox registers */ +#define PRCMU_I2C_WRITE(slave) \ + (((slave) << 1) | I2CWRITE | (1 << 6)) +#define PRCMU_I2C_READ(slave) \ + (((slave) << 1) | I2CREAD | (1 << 6)) + +#define I2C_MBOX_BIT (1 << 5) + +static int prcmu_is_ready(void) +{ + int ready = readb(PRCM_XP70_CUR_PWR_STATE) == AP_EXECUTE; + if (!ready) + printf("PRCMU firmware not ready\n"); + return ready; +} + +static int wait_for_i2c_mbx_rdy(void) +{ + int timeout = 10000; + + if (readl(PRCM_ARM_IT1_VAL) & I2C_MBOX_BIT) { + printf("prcmu: warning i2c mailbox was not acked\n"); + /* clear mailbox 5 ack irq */ + writel(I2C_MBOX_BIT, PRCM_ARM_IT1_CLEAR); + } + + /* check any already on-going transaction */ + while ((readl(PRCM_MBOX_CPU_VAL) & I2C_MBOX_BIT) && timeout) + timeout--; + + if (timeout == 0) + return -1; + + return 0; +} + +static int wait_for_i2c_req_done(void) +{ + int timeout = 10000; + + /* Set an interrupt to XP70 */ + writel(I2C_MBOX_BIT, PRCM_MBOX_CPU_SET); + + /* wait for mailbox 5 (i2c) ack */ + while (!(readl(PRCM_ARM_IT1_VAL) & I2C_MBOX_BIT) && timeout) + timeout--; + + if (timeout == 0) + return -1; + + return 0; +} + +/** + * prcmu_i2c_read - PRCMU - 4500 communication using PRCMU I2C + * @reg: - db8500 register bank to be accessed + * @slave: - db8500 register to be accessed + * Returns: ACK_MB5 value containing the status + */ +int prcmu_i2c_read(u8 reg, u16 slave) +{ + uint8_t i2c_status; + uint8_t i2c_val; + int ret; + + if (!prcmu_is_ready()) + return -1; + + debug("\nprcmu_4500_i2c_read:bank=%x;reg=%x;\n", + reg, slave); + + ret = wait_for_i2c_mbx_rdy(); + if (ret) { + printf("prcmu_i2c_read: mailbox became not ready\n"); + return ret; + } + + /* prepare the data for mailbox 5 */ + writeb(PRCMU_I2C_READ(reg), PRCM_REQ_MB5_I2COPTYPE_REG); + writeb((1 << 3) | 0x0, PRCM_REQ_MB5_BIT_FIELDS); + writeb(slave, PRCM_REQ_MB5_I2CSLAVE); + writeb(0, PRCM_REQ_MB5_I2CVAL); + + ret = wait_for_i2c_req_done(); + if (ret) { + printf("prcmu_i2c_read: mailbox request timed out\n"); + return ret; + } + + /* retrieve values */ + debug("ack-mb5:transfer status = %x\n", + readb(PRCM_ACK_MB5_STATUS)); + debug("ack-mb5:reg bank = %x\n", readb(PRCM_ACK_MB5) >> 1); + debug("ack-mb5:slave_add = %x\n", + readb(PRCM_ACK_MB5_SLAVE)); + debug("ack-mb5:reg_val = %d\n", readb(PRCM_ACK_MB5_VAL)); + + i2c_status = readb(PRCM_ACK_MB5_STATUS); + i2c_val = readb(PRCM_ACK_MB5_VAL); + /* clear mailbox 5 ack irq */ + writel(I2C_MBOX_BIT, PRCM_ARM_IT1_CLEAR); + + if (i2c_status == I2C_RD_OK) + return i2c_val; + + printf("prcmu_i2c_read:read return status= %d\n", i2c_status); + return -1; +} + +/** + * prcmu_i2c_write - PRCMU-db8500 communication using PRCMU I2C + * @reg: - db8500 register bank to be accessed + * @slave: - db800 register to be written to + * @reg_data: - the data to write + * Returns: ACK_MB5 value containing the status + */ +int prcmu_i2c_write(u8 reg, u16 slave, u8 reg_data) +{ + uint8_t i2c_status; + int ret; + + if (!prcmu_is_ready()) + return -1; + + debug("\nprcmu_4500_i2c_write:bank=%x;reg=%x;\n", + reg, slave); + + ret = wait_for_i2c_mbx_rdy(); + if (ret) { + printf("prcmu_i2c_write: mailbox became not ready\n"); + return ret; + } + + /* prepare the data for mailbox 5 */ + writeb(PRCMU_I2C_WRITE(reg), PRCM_REQ_MB5_I2COPTYPE_REG); + writeb((1 << 3) | 0x0, PRCM_REQ_MB5_BIT_FIELDS); + writeb(slave, PRCM_REQ_MB5_I2CSLAVE); + writeb(reg_data, PRCM_REQ_MB5_I2CVAL); + + ret = wait_for_i2c_req_done(); + if (ret) { + printf("prcmu_i2c_write: mailbox request timed out\n"); + return ret; + } + + /* retrieve values */ + debug("ack-mb5:transfer status = %x\n", + readb(PRCM_ACK_MB5_STATUS)); + debug("ack-mb5:reg bank = %x\n", readb(PRCM_ACK_MB5) >> 1); + debug("ack-mb5:slave_add = %x\n", + readb(PRCM_ACK_MB5_SLAVE)); + debug("ack-mb5:reg_val = %d\n", readb(PRCM_ACK_MB5_VAL)); + + i2c_status = readb(PRCM_ACK_MB5_STATUS); + debug("\ni2c_status = %x\n", i2c_status); + /* clear mailbox 5 ack irq */ + writel(I2C_MBOX_BIT, PRCM_ARM_IT1_CLEAR); + + if (i2c_status == I2C_WR_OK) + return 0; + + printf("%s: i2c_status : 0x%x\n", __func__, i2c_status); + return -1; +} + +void u8500_prcmu_enable(u32 *reg) +{ + writel(readl(reg) | (1 << 8), reg); +} + +void db8500_prcmu_init(void) +{ + /* Enable timers */ + writel(1 << 17, PRCM_TCR); + + u8500_prcmu_enable((u32 *)PRCM_PER1CLK_MGT_REG); + u8500_prcmu_enable((u32 *)PRCM_PER2CLK_MGT_REG); + u8500_prcmu_enable((u32 *)PRCM_PER3CLK_MGT_REG); + /* PER4CLK does not exist */ + u8500_prcmu_enable((u32 *)PRCM_PER5CLK_MGT_REG); + u8500_prcmu_enable((u32 *)PRCM_PER6CLK_MGT_REG); + /* Only exists in ED but is always ok to write to */ + u8500_prcmu_enable((u32 *)PRCM_PER7CLK_MGT_REG); + + u8500_prcmu_enable((u32 *)PRCM_UARTCLK_MGT_REG); + u8500_prcmu_enable((u32 *)PRCM_I2CCLK_MGT_REG); + + u8500_prcmu_enable((u32 *)PRCM_SDMMCCLK_MGT_REG); + + /* Clean up the mailbox interrupts after pre-u-boot code. */ + writel(I2C_MBOX_BIT, PRCM_ARM_IT1_CLEAR); +} |