diff options
author | Stefan Reinauer <reinauer@chromium.org> | 2013-02-14 16:35:47 -0800 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2013-02-15 02:11:34 +0100 |
commit | 5dbf689b62367f5386441cdc35434f14b39ac17b (patch) | |
tree | 5329f24010efbe3487fb7379e2cc1a577ef8ba0f /src/cpu/samsung/exynos5-common | |
parent | 7512e4593ed535bdd1bc33a91dfb769566146a39 (diff) |
Exynos5: Drop S5P directory and merge files
s5p-common mostly contained duplicate files, drop the whole directory
and merge the few pieces that we are using into exynos5-common.
Change-Id: I5f18e8a6d2379d719ab6bbbf817fe15bda70d17f
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Reviewed-on: http://review.coreboot.org/2405
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Diffstat (limited to 'src/cpu/samsung/exynos5-common')
-rw-r--r-- | src/cpu/samsung/exynos5-common/Makefile.inc | 18 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/clk.h | 5 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/clock.h | 94 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/cpu.h | 93 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/cpu_info.c | 128 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/gpio.c | 501 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/gpio.h | 156 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/i2c.c | 612 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/i2c.h | 43 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/power.h | 42 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/pwm.c | 199 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/pwm.h | 6 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/sromc.c | 49 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/timer.c | 150 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/watchdog.h | 57 | ||||
-rw-r--r-- | src/cpu/samsung/exynos5-common/wdt.c | 59 |
16 files changed, 2201 insertions, 11 deletions
diff --git a/src/cpu/samsung/exynos5-common/Makefile.inc b/src/cpu/samsung/exynos5-common/Makefile.inc index ff86fbdb4e..47abe0f058 100644 --- a/src/cpu/samsung/exynos5-common/Makefile.inc +++ b/src/cpu/samsung/exynos5-common/Makefile.inc @@ -1,3 +1,21 @@ romstage-y += spi.c ramstage-y += spi.c bootblock-y += spi.c + +bootblock-y += gpio.c +bootblock-$(CONFIG_EARLY_CONSOLE) += pwm.c +bootblock-$(CONFIG_EARLY_CONSOLE) += timer.c + +romstage-y += pwm.c # needed by timer.c +romstage-y += gpio.c +romstage-y += timer.c +romstage-y += i2c.c + +#romstage-y += wdt.c +#romstage-y += sromc.c + +ramstage-y += cpu_info.c +ramstage-y += pwm.c # needed by timer.c +ramstage-y += timer.c +ramstage-y += gpio.c +ramstage-y += i2c.c diff --git a/src/cpu/samsung/exynos5-common/clk.h b/src/cpu/samsung/exynos5-common/clk.h index eb3b25336d..9de4f45516 100644 --- a/src/cpu/samsung/exynos5-common/clk.h +++ b/src/cpu/samsung/exynos5-common/clk.h @@ -52,12 +52,11 @@ struct clk_bit_info { s8 prediv_bit; }; -/* FIXME(dhendrix) conflicts with s5p-common/clk.h */ -#if 0 unsigned long get_pll_clk(int pllreg); unsigned long get_arm_clk(void); +unsigned long get_pwm_clk(void); +unsigned long get_uart_clk(int dev_index); void set_mmc_clk(int dev_index, unsigned int div); -#endif /** * get the clk frequency of the required peripherial diff --git a/src/cpu/samsung/exynos5-common/clock.h b/src/cpu/samsung/exynos5-common/clock.h new file mode 100644 index 0000000000..c0cd896b22 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/clock.h @@ -0,0 +1,94 @@ +/* + * (C) Copyright 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@samsung.com> + * Heungjun Kim <riverful.kim@samsung.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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#ifndef __ASM_ARM_ARCH_CLOCK_H_ +#define __ASM_ARM_ARCH_CLOCK_H_ + +#ifndef __ASSEMBLER__ +struct s5pc100_clock { + unsigned int apll_lock; + unsigned int mpll_lock; + unsigned int epll_lock; + unsigned int hpll_lock; + unsigned char res1[0xf0]; + unsigned int apll_con; + unsigned int mpll_con; + unsigned int epll_con; + unsigned int hpll_con; + unsigned char res2[0xf0]; + unsigned int src0; + unsigned int src1; + unsigned int src2; + unsigned int src3; + unsigned char res3[0xf0]; + unsigned int div0; + unsigned int div1; + unsigned int div2; + unsigned int div3; + unsigned int div4; + unsigned char res4[0x1ec]; + unsigned int gate_d00; + unsigned int gate_d01; + unsigned int gate_d02; + unsigned char res5[0x54]; + unsigned int gate_sclk0; + unsigned int gate_sclk1; +}; + +struct s5pc110_clock { + unsigned int apll_lock; + unsigned char res1[0x4]; + unsigned int mpll_lock; + unsigned char res2[0x4]; + unsigned int epll_lock; + unsigned char res3[0xc]; + unsigned int vpll_lock; + unsigned char res4[0xdc]; + unsigned int apll_con; + unsigned char res5[0x4]; + unsigned int mpll_con; + unsigned char res6[0x4]; + unsigned int epll_con; + unsigned char res7[0xc]; + unsigned int vpll_con; + unsigned char res8[0xdc]; + unsigned int src0; + unsigned int src1; + unsigned int src2; + unsigned int src3; + unsigned char res9[0xf0]; + unsigned int div0; + unsigned int div1; + unsigned int div2; + unsigned int div3; + unsigned int div4; + unsigned char res10[0x1ec]; + unsigned int gate_d00; + unsigned int gate_d01; + unsigned int gate_d02; + unsigned char res11[0x54]; + unsigned int gate_sclk0; + unsigned int gate_sclk1; +}; +#endif + +#endif diff --git a/src/cpu/samsung/exynos5-common/cpu.h b/src/cpu/samsung/exynos5-common/cpu.h index e68de8688f..5a18913b2a 100644 --- a/src/cpu/samsung/exynos5-common/cpu.h +++ b/src/cpu/samsung/exynos5-common/cpu.h @@ -1,6 +1,7 @@ /* - * (C) Copyright 2010 Samsung Electronics + * (C) Copyright 2009-2010 Samsung Electronics * Minkyu Kang <mk7.kang@samsung.com> + * Heungjun Kim <riverful.kim@samsung.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -22,7 +23,95 @@ #ifndef _EXYNOS_COMMON_CPU_H #define _EXYNOS_COMMON_CPU_H -#include <cpu/samsung/s5p-common/cpu.h> +#define S5PC1XX_ADDR_BASE 0xE0000000 + +/* S5PC100 */ +#define S5PC100_PRO_ID 0xE0000000 +#define S5PC100_CLOCK_BASE 0xE0100000 +#define S5PC100_GPIO_BASE 0xE0300000 +#define S5PC100_VIC0_BASE 0xE4000000 +#define S5PC100_VIC1_BASE 0xE4100000 +#define S5PC100_VIC2_BASE 0xE4200000 +#define S5PC100_DMC_BASE 0xE6000000 +#define S5PC100_SROMC_BASE 0xE7000000 +#define S5PC100_ONENAND_BASE 0xE7100000 +#define S5PC100_PWMTIMER_BASE 0xEA000000 +#define S5PC100_WATCHDOG_BASE 0xEA200000 +#define S5PC100_UART_BASE 0xEC000000 +#define S5PC100_MMC_BASE 0xED800000 + +/* S5PC110 */ +#define S5PC110_PRO_ID 0xE0000000 +#define S5PC110_CLOCK_BASE 0xE0100000 +#define S5PC110_GPIO_BASE 0xE0200000 +#define S5PC110_PWMTIMER_BASE 0xE2500000 +#define S5PC110_WATCHDOG_BASE 0xE2700000 +#define S5PC110_UART_BASE 0xE2900000 +#define S5PC110_SROMC_BASE 0xE8000000 +#define S5PC110_MMC_BASE 0xEB000000 +#define S5PC110_DMC0_BASE 0xF0000000 +#define S5PC110_DMC1_BASE 0xF1400000 +#define S5PC110_VIC0_BASE 0xF2000000 +#define S5PC110_VIC1_BASE 0xF2100000 +#define S5PC110_VIC2_BASE 0xF2200000 +#define S5PC110_VIC3_BASE 0xF2300000 +#define S5PC110_OTG_BASE 0xEC000000 +#define S5PC110_PHY_BASE 0xEC100000 +#define S5PC110_USB_PHY_CONTROL 0xE010E80C + + +#include <arch/io.h> +/* CPU detection macros */ +extern unsigned int s5p_cpu_id; + +/* FIXME(dhendrix): conflicts with the one in cpu_info.c ... */ +#if 0 +inline void s5p_set_cpu_id(void) +{ + s5p_cpu_id = readl(S5PC100_PRO_ID); + s5p_cpu_id = 0xC000 | ((s5p_cpu_id & 0x00FFF000) >> 12); +} +#endif +inline void s5p_set_cpu_id(void); + +#define IS_SAMSUNG_TYPE(type, id) \ +static inline int cpu_is_##type(void) \ +{ \ + return s5p_cpu_id == id ? 1 : 0; \ +} + +IS_SAMSUNG_TYPE(s5pc100, 0xc100) +IS_SAMSUNG_TYPE(s5pc110, 0xc110) + +/* + * FIXME(dhendrix): collides with SAMSUNG_BASE in exynos header files. We + * don't really care about old S5P processors right now. + */ +#if 0 +#define SAMSUNG_BASE(device, base) \ +static inline unsigned int samsung_get_base_##device(void) \ +{ \ + if (cpu_is_s5pc100()) \ + return S5PC100_##base; \ + else if (cpu_is_s5pc110()) \ + return S5PC110_##base; \ + else \ + return 0; \ +} + +SAMSUNG_BASE(clock, CLOCK_BASE) +SAMSUNG_BASE(gpio, GPIO_BASE) +SAMSUNG_BASE(pro_id, PRO_ID) +SAMSUNG_BASE(mmc, MMC_BASE) +SAMSUNG_BASE(sromc, SROMC_BASE) +SAMSUNG_BASE(timer, PWMTIMER_BASE) +SAMSUNG_BASE(uart, UART_BASE) +SAMSUNG_BASE(watchdog, WATCHDOG_BASE) +#endif + +int s5p_get_cpu_rev(void); +//void s5p_set_cpu_id(void); +int s5p_get_cpu_id(void); #define DEVICE_NOT_AVAILABLE 0 diff --git a/src/cpu/samsung/exynos5-common/cpu_info.c b/src/cpu/samsung/exynos5-common/cpu_info.c new file mode 100644 index 0000000000..dec3c779aa --- /dev/null +++ b/src/cpu/samsung/exynos5-common/cpu_info.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@samsung.com> + * + * 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> +#if 0 +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <asm/arch/dmc.h> +#endif +#include <arch/io.h> + +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5-common/clock.h> +#include <cpu/samsung/exynos5-common/cpu.h> + +#include <cpu/samsung/exynos5250/dmc.h> +#include <cpu/samsung/exynos5-common/cpu.h> /* for EXYNOS_PRO_ID */ + +/* FIXME(dhendrix): consolidate samsung ID code/#defines to a common location */ +#include <cpu/samsung/exynos5250/setup.h> /* cpu_info_init() prototype */ + +/* + * The following CPU infos are initialized in lowlevel_init(). They should be + * put in the .data section. Otherwise, a compile will put them in the .bss + * section since they don't have initial values. The relocation code which + * runs after lowlevel_init() will reset them to zero. + */ +unsigned int s5p_cpu_id __attribute__((section(".data"))); +unsigned int s5p_cpu_rev __attribute__((section(".data"))); + +void cpu_info_init(void) +{ + s5p_set_cpu_id(); +} + +int s5p_get_cpu_id(void) +{ + return s5p_cpu_id; +} + +int s5p_get_cpu_rev(void) +{ + return s5p_cpu_rev; +} + +void s5p_set_cpu_id(void) +{ + s5p_cpu_id = readl(EXYNOS_PRO_ID); + s5p_cpu_id = (0xC000 | ((s5p_cpu_id & 0x00FFF000) >> 12)); + + /* + * 0xC200: EXYNOS4210 EVT0 + * 0xC210: EXYNOS4210 EVT1 + */ + if (s5p_cpu_id == 0xC200) { + s5p_cpu_id |= 0x10; + s5p_cpu_rev = 0; + } else if (s5p_cpu_id == 0xC210) { + s5p_cpu_rev = 1; + } +} + +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) +{ + char buf[32]; + + printf("CPU: S5P%X @ %sMHz\n", + s5p_cpu_id, strmhz(buf, get_arm_clk())); + + return 0; +} +#endif + +#if 0 +void board_show_dram(ulong size) +{ + enum ddr_mode mem_type; + unsigned frequency_mhz; + unsigned arm_freq; + enum mem_manuf mem_manuf; + char buf[32]; + int ret; + + /* Get settings from the fdt */ + ret = clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf); + if (ret) + panic("Invalid DRAM information"); + + puts("DRAM: "); + print_size(size, " "); + printf("%s %s @ %sMHz", + clock_get_mem_manuf_name(mem_manuf), + clock_get_mem_type_name(mem_type), + strmhz(buf, frequency_mhz)); + putc('\n'); +} +#endif + +#ifdef CONFIG_ARCH_CPU_INIT +int arch_cpu_init(void) +{ + cpu_info_init(); + + return 0; +} +#endif diff --git a/src/cpu/samsung/exynos5-common/gpio.c b/src/cpu/samsung/exynos5-common/gpio.c new file mode 100644 index 0000000000..c9c4c26ced --- /dev/null +++ b/src/cpu/samsung/exynos5-common/gpio.c @@ -0,0 +1,501 @@ +/* + * (C) Copyright 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@samsung.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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* FIXME(dhendrix): fix this up so it doesn't require a bunch of #ifdefs... */ +#include <common.h> +#include <gpio.h> +//#include <arch/io.h> +#include <gpio.h> +#include <arch/gpio.h> +#include <console/console.h> +#include <cpu/samsung/exynos5-common/gpio.h> +#include <cpu/samsung/exynos5250/gpio.h> /* FIXME: for gpio_decode_number prototype */ + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +#define DAT_MASK(x) (0x1 << (x)) +#define DAT_SET(x) (0x1 << (x)) + +#define PULL_MASK(x) (0x3 << ((x) << 1)) +#define PULL_MODE(x, v) ((v) << ((x) << 1)) + +#define DRV_MASK(x) (0x3 << ((x) << 1)) +#define DRV_SET(x, m) ((m) << ((x) << 1)) +#define RATE_MASK(x) (0x1 << (x + 16)) +#define RATE_SET(x) (0x1 << (x + 16)) + +struct gpio_info { + unsigned int reg_addr; /* Address of register for this part */ + unsigned int max_gpio; /* Maximum GPIO in this part */ +}; + +#include <cpu/samsung/exynos5250/cpu.h> +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, + { EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 }, + { EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 }, + { EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT }, +}; + +#define HAVE_GENERIC_GPIO + +/* This macro gets gpio pin offset from 0..7 */ +#define GPIO_BIT(x) ((x) & 0x7) + +//#ifdef HAVE_GENERIC_GPIO +static struct s5p_gpio_bank *gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i; + + for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS; + i++, upto = data->max_gpio, data++) { + if (gpio < data->max_gpio) { + struct s5p_gpio_bank *bank; + + bank = (struct s5p_gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + return bank; + } + } + + assert(gpio < GPIO_MAX_PORT); /* ...which it will not be */ + return NULL; +} +//#endif + +/* TODO: Deprecation this interface in favour of asm-generic/gpio.h */ +void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +{ + unsigned int value; + + value = readl(&bank->con); + value &= ~CON_MASK(gpio); + value |= CON_SFR(gpio, cfg); + writel(value, &bank->con); +} + +void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + + s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_OUTPUT); + + value = readl(&bank->dat); + value &= ~DAT_MASK(gpio); + if (en) + value |= DAT_SET(gpio); + writel(value, &bank->dat); +} + +void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) +{ + s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_INPUT); +} + +void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + + value = readl(&bank->dat); + value &= ~DAT_MASK(gpio); + if (en) + value |= DAT_SET(gpio); + writel(value, &bank->dat); +} + +unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) +{ + unsigned int value; + + value = readl(&bank->dat); + return !!(value & DAT_MASK(gpio)); +} + +void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->pull); + value &= ~PULL_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_PULL_DOWN: + case EXYNOS_GPIO_PULL_UP: + value |= PULL_MODE(gpio, mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~DRV_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_DRV_1X: + case EXYNOS_GPIO_DRV_2X: + case EXYNOS_GPIO_DRV_3X: + case EXYNOS_GPIO_DRV_4X: + value |= DRV_SET(gpio, mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~RATE_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_DRV_FAST: + case EXYNOS_GPIO_DRV_SLOW: + value |= RATE_SET(gpio); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +/* Common GPIO API - only available on Exynos5 */ +/* FIXME(dhendrix): If this stuff is really only applicable to exynos5, + move it to a more sensible location. */ +#ifdef HAVE_GENERIC_GPIO + +void gpio_cfg_pin(int gpio, int cfg) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->con); + value &= ~CON_MASK(GPIO_BIT(gpio)); + value |= CON_SFR(GPIO_BIT(gpio), cfg); + writel(value, &bank->con); +} + +static int gpio_get_cfg(int gpio) +{ + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + int shift = GPIO_BIT(gpio) << 2; + + return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift; +} + +void gpio_set_pull(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->pull); + value &= ~PULL_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_PULL_DOWN: + case EXYNOS_GPIO_PULL_UP: + value |= PULL_MODE(GPIO_BIT(gpio), mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void gpio_set_drv(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~DRV_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_DRV_1X: + case EXYNOS_GPIO_DRV_2X: + case EXYNOS_GPIO_DRV_3X: + case EXYNOS_GPIO_DRV_4X: + value |= DRV_SET(GPIO_BIT(gpio), mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void gpio_set_rate(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~RATE_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_DRV_FAST: + case EXYNOS_GPIO_DRV_SLOW: + value |= RATE_SET(GPIO_BIT(gpio)); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + gpio_cfg_pin(gpio, EXYNOS_GPIO_INPUT); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int val; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + gpio_cfg_pin(gpio, EXYNOS_GPIO_OUTPUT); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->dat); + return !!(value & DAT_MASK(GPIO_BIT(gpio))); +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int val; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} +#else + +static int s5p_gpio_get_pin(unsigned gpio) +{ + return gpio % GPIO_PER_BANK; +} + +/* + * If we have the old-style GPIO numbering setup, use these functions + * which don't necessary provide sequentially increasing GPIO numbers. + */ +static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) +{ + int bank = gpio / GPIO_PER_BANK; + bank *= sizeof(struct s5p_gpio_bank); + + return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank); +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + s5p_gpio_direction_input(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + s5p_gpio_direction_output(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); +} + +int gpio_set_value(unsigned gpio, int value) +{ + s5p_gpio_set_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + + return 0; +} + +#endif /* HAVE_GENERIC_GPIO */ + +/* + * Add a delay here to give the lines time to settle + * TODO(sjg): 1us does not always work, 2 is stable, so use 5 to be safe + * Come back to this and sort out what the datasheet says + */ +#define GPIO_DELAY_US 5 + +#ifndef __BOOT_BLOCK__ +/* + * FIXME(dhendrix): These functions use udelay, which has dependencies on + * pwm code and timer code. These aren't necessary for the bootblock and + * bloat the image significantly. + */ +int gpio_read_mvl3(unsigned gpio) +{ + int high, low; + enum mvl3 value; + + if (gpio >= GPIO_MAX_PORT) + return -1; + + gpio_direction_input(gpio); + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP); + udelay(GPIO_DELAY_US); + high = gpio_get_value(gpio); + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_DOWN); + udelay(GPIO_DELAY_US); + low = gpio_get_value(gpio); + + if (high && low) /* external pullup */ + value = LOGIC_1; + else if (!high && !low) /* external pulldown */ + value = LOGIC_0; + else /* floating */ + value = LOGIC_Z; + + /* + * Check if line is externally pulled high and + * configure the internal pullup to match. For + * floating and pulldowns, the GPIO is already + * configured with an internal pulldown from the + * above test. + */ + if (value == LOGIC_1) + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP); + + return value; +} + +int gpio_decode_number(unsigned gpio_list[], int count) +{ + int result = 0; + int multiplier = 1; + int gpio, i, value; + enum mvl3 mvl3; + + for (i = 0; i < count; i++) { + gpio = gpio_list[i]; + + mvl3 = gpio_read_mvl3(gpio); + if (mvl3 == LOGIC_1) + value = 2; + else if (mvl3 == LOGIC_0) + value = 1; + else if (mvl3 == LOGIC_Z) + value = 0; + else + return -1; + + result += value * multiplier; + multiplier *= 3; + } + + return result; +} +#endif /* __BOOT_BLOCK__ */ + +static const char *get_cfg_name(int cfg) +{ + static char name[8]; + + if (cfg == EXYNOS_GPIO_INPUT) + return "input"; + else if (cfg == EXYNOS_GPIO_OUTPUT) + return "output"; + printk(BIOS_INFO, "func %d", cfg); +// sprintf(name, "func %d", cfg); + + return name; +} + +/* + * Display Exynos GPIO information + */ +void gpio_info(void) +{ + unsigned gpio; + + for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) { + int cfg = gpio_get_cfg(gpio); + + printk(BIOS_INFO, "GPIO_%-3d: %s", gpio, get_cfg_name(cfg)); + if (cfg == EXYNOS_GPIO_INPUT || cfg == EXYNOS_GPIO_OUTPUT) + printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio)); + printk(BIOS_INFO, "\n"); + } +} diff --git a/src/cpu/samsung/exynos5-common/gpio.h b/src/cpu/samsung/exynos5-common/gpio.h index d291fae770..0de53f617f 100644 --- a/src/cpu/samsung/exynos5-common/gpio.h +++ b/src/cpu/samsung/exynos5-common/gpio.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2010 Samsung Electronics + * (C) Copyright 2009-2010 Samsung Electronics * Minkyu Kang <mk7.kang@samsung.com> * * This program is free software; you can redistribute it and/or @@ -22,10 +22,9 @@ #define __ASM_ARCH_COMMON_GPIO_H #ifndef __ASSEMBLER__ /* FIXME: not needed (i hope)? */ -#include <cpu/samsung/s5p-common/gpio.h> -/* FIXME: s5p's gpio.h and exynos' gpio.h have a lot of conflicting - definitions */ -#if 0 + +#include <cpu/samsung/exynos5-common/cpu.h> /* FIXME: for S5PC110_GPIO_BASE */ + struct s5p_gpio_bank { unsigned int con; unsigned int dat; @@ -36,6 +35,99 @@ struct s5p_gpio_bank { unsigned char res1[8]; }; +struct s5pc100_gpio { + struct s5p_gpio_bank a0; + struct s5p_gpio_bank a1; + struct s5p_gpio_bank b; + struct s5p_gpio_bank c; + struct s5p_gpio_bank d; + struct s5p_gpio_bank e0; + struct s5p_gpio_bank e1; + struct s5p_gpio_bank f0; + struct s5p_gpio_bank f1; + struct s5p_gpio_bank f2; + struct s5p_gpio_bank f3; + struct s5p_gpio_bank g0; + struct s5p_gpio_bank g1; + struct s5p_gpio_bank g2; + struct s5p_gpio_bank g3; + struct s5p_gpio_bank i; + struct s5p_gpio_bank j0; + struct s5p_gpio_bank j1; + struct s5p_gpio_bank j2; + struct s5p_gpio_bank j3; + struct s5p_gpio_bank j4; + struct s5p_gpio_bank k0; + struct s5p_gpio_bank k1; + struct s5p_gpio_bank k2; + struct s5p_gpio_bank k3; + struct s5p_gpio_bank l0; + struct s5p_gpio_bank l1; + struct s5p_gpio_bank l2; + struct s5p_gpio_bank l3; + struct s5p_gpio_bank l4; + struct s5p_gpio_bank h0; + struct s5p_gpio_bank h1; + struct s5p_gpio_bank h2; + struct s5p_gpio_bank h3; +}; + +struct s5pc110_gpio { + struct s5p_gpio_bank a0; + struct s5p_gpio_bank a1; + struct s5p_gpio_bank b; + struct s5p_gpio_bank c0; + struct s5p_gpio_bank c1; + struct s5p_gpio_bank d0; + struct s5p_gpio_bank d1; + struct s5p_gpio_bank e0; + struct s5p_gpio_bank e1; + struct s5p_gpio_bank f0; + struct s5p_gpio_bank f1; + struct s5p_gpio_bank f2; + struct s5p_gpio_bank f3; + struct s5p_gpio_bank g0; + struct s5p_gpio_bank g1; + struct s5p_gpio_bank g2; + struct s5p_gpio_bank g3; + struct s5p_gpio_bank i; + struct s5p_gpio_bank j0; + struct s5p_gpio_bank j1; + struct s5p_gpio_bank j2; + struct s5p_gpio_bank j3; + struct s5p_gpio_bank j4; + struct s5p_gpio_bank mp0_1; + struct s5p_gpio_bank mp0_2; + struct s5p_gpio_bank mp0_3; + struct s5p_gpio_bank mp0_4; + struct s5p_gpio_bank mp0_5; + struct s5p_gpio_bank mp0_6; + struct s5p_gpio_bank mp0_7; + struct s5p_gpio_bank mp1_0; + struct s5p_gpio_bank mp1_1; + struct s5p_gpio_bank mp1_2; + struct s5p_gpio_bank mp1_3; + struct s5p_gpio_bank mp1_4; + struct s5p_gpio_bank mp1_5; + struct s5p_gpio_bank mp1_6; + struct s5p_gpio_bank mp1_7; + struct s5p_gpio_bank mp1_8; + struct s5p_gpio_bank mp2_0; + struct s5p_gpio_bank mp2_1; + struct s5p_gpio_bank mp2_2; + struct s5p_gpio_bank mp2_3; + struct s5p_gpio_bank mp2_4; + struct s5p_gpio_bank mp2_5; + struct s5p_gpio_bank mp2_6; + struct s5p_gpio_bank mp2_7; + struct s5p_gpio_bank mp2_8; + struct s5p_gpio_bank res1[48]; + struct s5p_gpio_bank h0; + struct s5p_gpio_bank h1; + struct s5p_gpio_bank h2; + struct s5p_gpio_bank h3; +}; + /* functions */ void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg); void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en); @@ -45,13 +137,65 @@ unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio); void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode); void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode); void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode); -#endif /* GPIO pins per bank */ #define GPIO_PER_BANK 8 +static inline unsigned int s5p_gpio_base(int nr) +{ + return S5PC110_GPIO_BASE; +} + +#define s5pc110_gpio_get_nr(bank, pin) \ + ((((((unsigned int)&(((struct s5pc110_gpio *)S5PC110_GPIO_BASE)->bank))\ + - S5PC110_GPIO_BASE) / sizeof(struct s5p_gpio_bank)) \ + * GPIO_PER_BANK) + pin) +#endif + +/* Pin configurations */ +#define GPIO_INPUT 0x0 +#define GPIO_OUTPUT 0x1 +#define GPIO_IRQ 0xf +#define GPIO_FUNC(x) (x) + +/* Pull mode */ +#define GPIO_PULL_NONE 0x0 +#define GPIO_PULL_DOWN 0x1 +#define GPIO_PULL_UP 0x2 + +/* Drive Strength level */ +#define GPIO_DRV_1X 0x0 +#define GPIO_DRV_3X 0x1 +#define GPIO_DRV_2X 0x2 +#define GPIO_DRV_4X 0x3 +#define GPIO_DRV_FAST 0x0 +#define GPIO_DRV_SLOW 0x1 + +#if 0 +struct s5p_gpio_bank { + unsigned int con; + unsigned int dat; + unsigned int pull; + unsigned int drv; + unsigned int pdn_con; + unsigned int pdn_pull; + unsigned char res1[8]; +}; + +/* functions */ +void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg); +void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en); +void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio); +void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en); +unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio); +void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode); +void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode); +void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode); #endif +/* GPIO pins per bank */ +#define GPIO_PER_BANK 8 + /* Pin configurations */ #define EXYNOS_GPIO_INPUT 0x0 #define EXYNOS_GPIO_OUTPUT 0x1 diff --git a/src/cpu/samsung/exynos5-common/i2c.c b/src/cpu/samsung/exynos5-common/i2c.c new file mode 100644 index 0000000000..624d48b7dd --- /dev/null +++ b/src/cpu/samsung/exynos5-common/i2c.c @@ -0,0 +1,612 @@ +/* + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * 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 + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same I2C controller inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include <common.h> +#include <arch/io.h> +#include "clk.h" +#include "cpu/samsung/exynos5-common/clk.h" +#include "cpu/samsung/exynos5250/cpu.h" +#include "gpio.h" +#include "cpu/samsung/exynos5250/gpio.h" +#include "cpu/samsung/exynos5250/pinmux.h" + +//#include <fdtdec.h> +#include "device/i2c.h" +#include "i2c.h" + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +/* The timeouts we live by */ +enum { + I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */ + I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */ + I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */ + I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */ +}; + +/* We should not rely on any particular ordering of these IDs */ +#if 0 +#ifndef CONFIG_OF_CONTROL +static enum periph_id periph_for_dev[EXYNOS_I2C_MAX_CONTROLLERS] = { + PERIPH_ID_I2C0, + PERIPH_ID_I2C1, + PERIPH_ID_I2C2, + PERIPH_ID_I2C3, + PERIPH_ID_I2C4, + PERIPH_ID_I2C5, + PERIPH_ID_I2C6, + PERIPH_ID_I2C7, +}; +#endif +#endif + +static unsigned int g_current_bus __attribute__((section(".data"))); +static struct s3c24x0_i2c *g_early_i2c_config __attribute__((section(".data"))); + +static struct s3c24x0_i2c_bus i2c_bus[EXYNOS_I2C_MAX_CONTROLLERS] + __attribute__((section(".data"))); +static int i2c_busses __attribute__((section(".data"))); + +void i2c_set_early_reg(unsigned int base) +{ + g_early_i2c_config = (struct s3c24x0_i2c *)base; +} + +static struct s3c24x0_i2c_bus *get_bus(int bus_idx) +{ + /* If an early i2c config exists we just use that */ + if (g_early_i2c_config) { + /* FIXME: value not retained from i2c_set_early_reg()? (but then, how + * did if (!i2c) check pass earlier on? Corrupt value? */ + i2c_bus[0].regs = g_early_i2c_config; + return &i2c_bus[0]; + } + + if (bus_idx < i2c_busses) + return &i2c_bus[bus_idx]; + debug("Undefined bus: %d\n", bus_idx); + return NULL; +} + +static inline struct exynos5_gpio_part1 *exynos_get_base_gpio1(void) +{ + return (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE); +} + +static int WaitForXfer(struct s3c24x0_i2c *i2c) +{ + int i; + + i = I2C_XFER_TIMEOUT_MS * 20; + while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) { + if (i == 0) { + debug("%s: i2c xfer timeout\n", __func__); + return I2C_NOK_TOUT; + } + udelay(50); + i--; + } + + return I2C_OK; +} + +static int IsACK(struct s3c24x0_i2c *i2c) +{ + return !(readl(&i2c->iicstat) & I2CSTAT_NACK); +} + +static void ReadWriteByte(struct s3c24x0_i2c *i2c) +{ + uint32_t x; + + x = readl(&i2c->iiccon); + writel(x & ~I2CCON_IRPND, &i2c->iiccon); + /* FIXME(dhendrix): cannot use nested macro (compilation failure) */ +// writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); +} + +static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +{ + ulong freq, pres = 16, div; + + freq = clock_get_periph_rate(PERIPH_ID_I2C0); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(slaveadd, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); +} + +/* TODO: determine if this is necessary to init board using FDT-provided info */ +#if 0 +void board_i2c_init(const void *blob) +{ + /* + * Turn off the early i2c configuration and init the i2c properly, + * this is done here to enable the use of i2c configs from FDT. + */ + i2c_set_early_reg(0); + +#ifdef CONFIG_OF_CONTROL + int node_list[EXYNOS_I2C_MAX_CONTROLLERS]; + int i, count; + + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_S3C2440_I2C, node_list, + EXYNOS_I2C_MAX_CONTROLLERS); + + for (i = 0; i < count; i++) { + struct s3c24x0_i2c_bus *bus; + int node = node_list[i]; + + if (node < 0) + continue; + bus = &i2c_bus[i2c_busses]; + bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + bus->id = (enum periph_id) + fdtdec_get_int(blob, node, "samsung,periph-id", -1); + bus->node = node; + bus->bus_num = i2c_busses++; + } +#else + int i; + + for (i = 0; i < EXYNOS_I2C_MAX_CONTROLLERS; i++) { + uintptr_t reg_addr = samsung_get_base_i2c() + + EXYNOS_I2C_SPACING * i; + + i2c_bus[i].regs = (struct s3c24x0_i2c_bus *)reg_addr; + i2c_bus[i].id = periph_for_dev[i]; + } + i2c_busses = EXYNOS_I2C_MAX_CONTROLLERS; +#endif +} +#endif + +/* + * MULTI BUS I2C support + */ +static void i2c_bus_init(struct s3c24x0_i2c_bus *i2c, unsigned int bus) +{ + exynos_pinmux_config(i2c->id, 0); + i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +} + +#ifdef CONFIG_I2C_MULTI_BUS +int i2c_set_bus_num(unsigned int bus) +{ + struct s3c24x0_i2c_bus *i2c; + + i2c = get_bus(bus); + if (!i2c) + return -1; + g_current_bus = bus; + i2c_bus_init(i2c, g_current_bus); + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + return g_current_bus; +} +#endif + +#ifdef CONFIG_OF_CONTROL +int i2c_get_bus_num_fdt(const void *blob, int node) +{ + enum fdt_compat_id compat; + fdt_addr_t reg; + int i; + + compat = fdtdec_lookup(blob, node); + if (compat != COMPAT_SAMSUNG_S3C2440_I2C) { + debug("%s: Not a supported I2C node\n", __func__); + return -1; + } + + reg = fdtdec_get_addr(blob, node, "reg"); + for (i = 0; i < i2c_busses; i++) + if (reg == (fdt_addr_t)(uintptr_t)i2c_bus[i].regs) + return i; + + debug("%s: Can't find any matched I2C bus\n", __func__); + return -1; +} + +int i2c_reset_port_fdt(const void *blob, int node) +{ + struct s3c24x0_i2c_bus *i2c; + + int bus; + + bus = i2c_get_bus_num_fdt(blob, node); + if (bus < 0) { + printf("could not get bus for node %d\n", node); + return -1; + } + i2c = get_bus(bus); + if (!i2c) { + printf("get_bus() failed for node node %d\n", node); + return -1; + } + + i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} +#endif + +/* + * Verify the whether I2C ACK was received or not + * + * @param i2c pointer to I2C register base + * @param buf array of data + * @param len length of data + * return I2C_OK when transmission done + * I2C_NACK otherwise + */ +static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[], + unsigned char len) +{ + int i, result = I2C_OK; + + if (IsACK(i2c)) { + for (i = 0; (i < len) && (result == I2C_OK); i++) { + writel(buf[i], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + if (result == I2C_OK && !IsACK(i2c)) + result = I2C_NACK; + } + } else { + result = I2C_NACK; + } + + return result; +} + +void i2c_init(int speed, int slaveadd) +{ + struct s3c24x0_i2c_bus *i2c; + struct exynos5_gpio_part1 *gpio; + int i; + uint32_t x; + + /* By default i2c channel 0 is the current bus */ + g_current_bus = 0; + + i2c = get_bus(g_current_bus); + if (!i2c) + return; + + i2c_bus_init(i2c, g_current_bus); + + /* wait for some time to give previous transfer a chance to finish */ + i = I2C_INIT_TIMEOUT_MS * 20; + while ((readl(&i2c->regs->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(50); + i--; + } + + gpio = exynos_get_base_gpio1(); + /* FIXME(dhendrix): cannot use nested macro (compilation failure) */ +// writel((readl(&gpio->b3.con) & ~0x00FF) | 0x0022, &gpio->b3.con); + x = readl(&gpio->b3.con); + writel((x & ~0x00FF) | 0x0022, &gpio->b3.con); + + i2c_ch_init(i2c->regs, speed, slaveadd); +} + +/* + * Send a STOP event and wait for it to have completed + * + * @param mode If it is a master transmitter or receiver + * @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise + */ +static int i2c_send_stop(struct s3c24x0_i2c *i2c, int mode) +{ + int timeout; + + /* Setting the STOP event to fire */ + writel(mode | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + + /* Wait for the STOP to send and the bus to go idle */ + for (timeout = I2C_STOP_TIMEOUT_US; timeout > 0; timeout -= 5) { + if (!(readl(&i2c->iicstat) & I2CSTAT_BSY)) + return I2C_OK; + udelay(5); + } + + return I2C_NOK_TOUT; +} + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i, result, stop_bit_result; + uint32_t x; + + if (data == 0 || data_len == 0) { + /* Don't support data transfer of no length or to address 0 */ + debug("i2c_transfer: bad call\n"); + return I2C_NOK; + } + + /* Check I2C bus idle */ + i = I2C_IDLE_TIMEOUT_MS * 20; + while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(50); + i--; + } + + if (readl(&i2c->iicstat) & I2CSTAT_BSY) { + debug("%s: bus busy\n", __func__); + return I2C_NOK_TOUT; + } + + /* FIXME(dhendrix): cannot use nested macro (compilation failure) */ + //writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); + x = readl(&i2c->iiccon); + writel(x | I2CCON_ACKGEN, &i2c->iiccon); + + if (addr && addr_len) { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + if (WaitForXfer(i2c) == I2C_OK) + result = i2c_send_verify(i2c, addr, addr_len); + else + result = I2C_NACK; + } else + result = I2C_NACK; + + switch (cmd_type) { + case I2C_WRITE: + if (result == I2C_OK) + result = i2c_send_verify(i2c, data, data_len); + else { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + if (WaitForXfer(i2c) == I2C_OK) + result = i2c_send_verify(i2c, data, data_len); + } + + if (result == I2C_OK) + result = WaitForXfer(i2c); + + stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MT); + break; + + case I2C_READ: + { + int was_ok = (result == I2C_OK); + + writel(chip, &i2c->iicds); + /* resend START */ + writel(I2C_MODE_MR | I2C_TXRX_ENA | + I2C_START_STOP, &i2c->iicstat); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + + if (was_ok || IsACK(i2c)) { + i = 0; + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) { + /* FIXME(dhendrix): nested macro */ +#if 0 + writel(readl(&i2c->iiccon) & + ~I2CCON_ACKGEN, + &i2c->iiccon); +#endif + x = readl(&i2c->iiccon) & ~I2CCON_ACKGEN; + writel(x, &i2c->iiccon); + } + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i] = readl(&i2c->iicds); + i++; + } + } else { + result = I2C_NACK; + } + + stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MR); + break; + } + + default: + debug("i2c_transfer: bad call\n"); + result = stop_bit_result = I2C_NOK; + break; + } + + /* + * If the transmission went fine, then only the stop bit was left to + * fail. Otherwise, the real failure we're interested in came before + * that, during the actual transmission. + */ + return (result == I2C_OK) ? stop_bit_result : result; +} + +int i2c_probe(uchar chip) +{ + struct s3c24x0_i2c_bus *i2c; + uchar buf[1]; + int ret; + + i2c = get_bus(g_current_bus); + if (!i2c) + return -1; + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, 0, 0, buf, 1); + + return ret != I2C_OK; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c = get_bus(g_current_bus); + if (!i2c) + return -1; + ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen], + alen, buffer, len); + if (ret) { + debug("I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c = get_bus(g_current_bus); + if (!i2c) + return -1; + + ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen], + alen, buffer, len); + + return ret != 0; +} diff --git a/src/cpu/samsung/exynos5-common/i2c.h b/src/cpu/samsung/exynos5-common/i2c.h new file mode 100644 index 0000000000..eb68af78f0 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/i2c.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * 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 + */ + +#ifndef _S3C24X0_I2C_H +#define _S3C24X0_I2C_H + +/* FIXME: gross hack */ +#include "cpu/samsung/exynos5250/periph.h" + +struct s3c24x0_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +}; + +struct s3c24x0_i2c_bus { + int node; /* device tree node */ + int bus_num; /* i2c bus number */ + struct s3c24x0_i2c *regs; + enum periph_id id; +}; +#endif /* _S3C24X0_I2C_H */ diff --git a/src/cpu/samsung/exynos5-common/power.h b/src/cpu/samsung/exynos5-common/power.h new file mode 100644 index 0000000000..57e2a2ba19 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/power.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Minkyu Kang <mk7.kang@samsung.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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#ifndef __ASM_ARM_ARCH_POWER_H_ +#define __ASM_ARM_ARCH_POWER_H_ + +/* + * Power control + */ +#define S5PC100_OTHERS 0xE0108200 +#define S5PC100_RST_STAT 0xE0108300 +#define S5PC100_SLEEP_WAKEUP (1 << 3) +#define S5PC100_WAKEUP_STAT 0xE0108304 +#define S5PC100_INFORM0 0xE0108400 + +#define S5PC110_RST_STAT 0xE010A000 +#define S5PC110_SLEEP_WAKEUP (1 << 3) +#define S5PC110_WAKEUP_STAT 0xE010C200 +#define S5PC110_OTHERS 0xE010E000 +#define S5PC110_USB_PHY_CON 0xE010E80C +#define S5PC110_INFORM0 0xE010F000 + +#endif diff --git a/src/cpu/samsung/exynos5-common/pwm.c b/src/cpu/samsung/exynos5-common/pwm.c new file mode 100644 index 0000000000..f7c1fb1100 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/pwm.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * + * Donghwa Lee <dh09.lee@samsung.com> + * + * 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 <arch/io.h> +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> +#include <cpu/samsung/exynos5-common/pwm.h> + +int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); + + return 0; +} + +int pwm_check_enabled(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + const unsigned long tcon = readl(&pwm->tcon); + + return tcon & TCON_START(pwm_id); +} + +void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); +} + +static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; + + tin_parent_rate = clock_get_periph_rate(PERIPH_ID_PWM0); + + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + } + + return tin_parent_rate / 16; +} + +#define NS_IN_SEC 1000000000UL + +int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp; + + /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -1; +// return -ERANGE; + + if (duty_ns > period_ns) + return -1; +// return -EINVAL; + + frequency = NS_IN_SEC / period_ns; + + /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency); + + tin_ns = NS_IN_SEC / tin_rate; + tcnt = period_ns / tin_ns; + + /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp; + + /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + } + + tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon); + + tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon); + + return 0; +} + +int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long ticks_per_period; + unsigned int offset, prescaler; + + /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */ + + val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1); + + + if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; + unsigned long timer_rate_hz = clock_get_periph_rate( + PERIPH_ID_PWM0) / ((prescaler + 1) * (1 << div)); + + ticks_per_period = timer_rate_hz / pwm_hz; + } + + /* set count value */ + offset = pwm_id * 3; + + writel(ticks_per_period, &pwm->tcntb0 + offset); + + val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && (pwm_id < 4)) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon); + + pwm_enable(pwm_id); + + return 0; +} diff --git a/src/cpu/samsung/exynos5-common/pwm.h b/src/cpu/samsung/exynos5-common/pwm.h index a4e0acf693..d7aa76fd15 100644 --- a/src/cpu/samsung/exynos5-common/pwm.h +++ b/src/cpu/samsung/exynos5-common/pwm.h @@ -63,6 +63,12 @@ struct s5p_timer { unsigned int tcnto4; unsigned int tintcstat; }; + +int pwm_config(int pwm_id, int duty_ns, int period_ns); +int pwm_check_enabled(int pwm_id); +void pwm_disable(int pwm_id); +int pwm_enable(int pwm_id); +int pwm_init(int pwm_id, int div, int invert); #endif /* __ASSEMBLER__ */ #endif diff --git a/src/cpu/samsung/exynos5-common/sromc.c b/src/cpu/samsung/exynos5-common/sromc.c new file mode 100644 index 0000000000..091e8d18ab --- /dev/null +++ b/src/cpu/samsung/exynos5-common/sromc.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Samsung Electronics + * Naveen Krishna Ch <ch.naveen@samsung.com> + * + * 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/sromc.h> + +/* + * s5p_config_sromc() - select the proper SROMC Bank and configure the + * band width control and bank control registers + * srom_bank - SROM + * srom_bw_conf - SMC Band witdh reg configuration value + * srom_bc_conf - SMC Bank Control reg configuration value + */ +void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf) +{ + u32 tmp; + struct s5p_sromc *srom = + (struct s5p_sromc *)samsung_get_base_sromc(); + + /* Configure SMC_BW register to handle proper SROMC bank */ + tmp = srom->bw; + tmp &= ~(0xF << (srom_bank * 4)); + tmp |= srom_bw_conf; + srom->bw = tmp; + + /* Configure SMC_BC register */ + srom->bc[srom_bank] = srom_bc_conf; +} diff --git a/src/cpu/samsung/exynos5-common/timer.c b/src/cpu/samsung/exynos5-common/timer.c new file mode 100644 index 0000000000..7e918aac65 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/timer.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2009 Samsung Electronics + * Heungjun Kim <riverful.kim@samsung.com> + * Inki Dae <inki.dae@samsung.com> + * Minkyu Kang <mk7.kang@samsung.com> + * + * 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 <arch/io.h> +#include <cpu/samsung/exynos5-common/pwm.h> +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5-common/exynos5-common.h> + +//#include <pwm.h> + +//DECLARE_GLOBAL_DATA_PTR; +static unsigned long long timer_reset_value; +static unsigned long lastinc; + +/* macro to read the 16 bit timer */ +static inline struct s5p_timer *s5p_get_base_timer(void) +{ + return (struct s5p_timer *)samsung_get_base_timer(); +} + +/** + * Read the countdown timer. + * + * This operates at 1MHz and counts downwards. It will wrap about every + * hour (2^32 microseconds). + * + * @return current value of timer + */ +static unsigned long timer_get_us_down(void) +{ + struct s5p_timer *const timer = s5p_get_base_timer(); + + return readl(&timer->tcnto4); +} + +int init_timer(void) +{ + /* Timer may have been enabled in SPL */ + if (!pwm_check_enabled(4)) { + /* PWM Timer 4 */ + pwm_init(4, MUX_DIV_4, 0); + pwm_config(4, 100000, 100000); + pwm_enable(4); + + /* Use this as the current monotonic time in us */ + //gd->timer_reset_value = 0; + timer_reset_value = 0; + + /* Use this as the last timer value we saw */ + //gd->lastinc = timer_get_us_down(); + lastinc = timer_get_us_down(); + } + + return 0; +} + +/* + * timer without interrupts + */ +unsigned long get_timer(unsigned long base) +{ + ulong now = timer_get_us_down(); + + /* + * Increment the time by the amount elapsed since the last read. + * The timer may have wrapped around, but it makes no difference to + * our arithmetic here. + */ +#if 0 + gd->timer_reset_value += gd->lastinc - now; + gd->lastinc = now; + + /* Divide by 1000 to convert from us to ms */ + return gd->timer_reset_value / 1000 - base; +#endif + timer_reset_value += lastinc - now; + lastinc = now; + + /* Divide by 1000 to convert from us to ms */ + return timer_reset_value / 1000 - base; +} + +unsigned long timer_get_us(void) +{ + struct s5p_timer *const timer = s5p_get_base_timer(); + unsigned long now_downward_us = readl(&timer->tcnto4); + + /* + * Note that this timer counts downward. The pre-SPL process (BL1) + * takes about 100ms, so add this in here. + */ + return CONFIG_SPL_TIME_US - now_downward_us; +} + +/* delay x useconds */ +void __udelay(unsigned long usec) +{ + unsigned long count_value; + + count_value = timer_get_us_down(); + while ((int)(count_value - timer_get_us_down()) < (int)usec) + ; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +unsigned long get_tbclk(void) +{ + return CONFIG_SYS_HZ; +} + +unsigned long timer_get_boot_us(void) +{ + return timer_get_us(); +} diff --git a/src/cpu/samsung/exynos5-common/watchdog.h b/src/cpu/samsung/exynos5-common/watchdog.h new file mode 100644 index 0000000000..5b3b651ffa --- /dev/null +++ b/src/cpu/samsung/exynos5-common/watchdog.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Heungjun Kim <riverful.kim@samsung.com> + * + * 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 + */ + +#ifndef __ASM_ARM_ARCH_COMMON_WATCHDOG_H_ +#define __ASM_ARM_ARCH_COMMON_WATCHDOG_H_ + +#define WTCON_RESET_OFFSET 0 +#define WTCON_INTEN_OFFSET 2 +#define WTCON_CLKSEL_OFFSET 3 +#define WTCON_EN_OFFSET 5 +#define WTCON_PRE_OFFSET 8 + +#define WTCON_CLK_16 0x0 +#define WTCON_CLK_32 0x1 +#define WTCON_CLK_64 0x2 +#define WTCON_CLK_128 0x3 + +#define WTCON_CLK(x) ((x & 0x3) << WTCON_CLKSEL_OFFSET) +#define WTCON_PRESCALER(x) ((x) << WTCON_PRE_OFFSET) +#define WTCON_EN (0x1 << WTCON_EN_OFFSET) +#define WTCON_RESET (0x1 << WTCON_RESET_OFFSET) +#define WTCON_INT (0x1 << WTCON_INTEN_OFFSET) + +#ifndef __ASSEMBLER__ +struct s5p_watchdog { + unsigned int wtcon; + unsigned int wtdat; + unsigned int wtcnt; + unsigned int wtclrint; +}; + +/* functions */ +void wdt_stop(void); +void wdt_start(unsigned int timeout); +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/src/cpu/samsung/exynos5-common/wdt.c b/src/cpu/samsung/exynos5-common/wdt.c new file mode 100644 index 0000000000..94acc1e4ba --- /dev/null +++ b/src/cpu/samsung/exynos5-common/wdt.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Minkyu Kang <mk7.kang@samsung.com> + * + * 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/watchdog.h> + +#define PRESCALER_VAL 255 + +void wdt_stop(void) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wtcon = readl(&wdt->wtcon); + wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); + + writel(wtcon, &wdt->wtcon); +} + +void wdt_start(unsigned int timeout) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wdt_stop(); + + wtcon = readl(&wdt->wtcon); + wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128)); + wtcon &= ~WTCON_INT; + wtcon |= WTCON_RESET; + wtcon |= WTCON_PRESCALER(PRESCALER_VAL); + + writel(timeout, &wdt->wtdat); + writel(timeout, &wdt->wtcnt); + writel(wtcon, &wdt->wtcon); +} |