[U-Boot] [PATCH 0/7 V3] EXYNOS5: Enable SPI support

This patch set adds SPI driver for EXYNOS5 and enables same.
This patchset is based on top of latest V7 I2C patches which are merged in u-boot-i2c. url for same: http://comments.gmane.org/gmane.comp.boot-loaders.u-boot/136058
Changes in V2: - Correted the Commit message. Changes in V3: - Removed SPI_SLAVE Flag. - Corrected warning messages.
Rajeshwari Shinde (7): EXYNOS5: Add pinmux support for SPI SPI: Add W25Q32 to Winbond SPI flash table EXYNOS: Add clock for SPI. EXYNOS5: Add base address for SPI. SPI: Add SPI Driver for EXYNOS. EXYNOS5: Enable SPI Enable SPI booting.
arch/arm/cpu/armv7/exynos/clock.c | 124 +++++++ arch/arm/cpu/armv7/exynos/pinmux.c | 51 +++- arch/arm/include/asm/arch-exynos/clk.h | 4 +- arch/arm/include/asm/arch-exynos/cpu.h | 6 + arch/arm/include/asm/arch-exynos/periph.h | 5 + arch/arm/include/asm/arch-exynos/spi.h | 78 +++++ board/samsung/smdk5250/Makefile | 2 +- board/samsung/smdk5250/smdk5250.c | 4 + board/samsung/smdk5250/{mmc_boot.c => spl_boot.c} | 31 ++- drivers/mtd/spi/winbond.c | 8 + drivers/spi/Makefile | 1 + drivers/spi/exynos_spi.c | 368 +++++++++++++++++++++ include/configs/smdk5250.h | 28 ++- 13 files changed, 704 insertions(+), 6 deletions(-) create mode 100644 arch/arm/include/asm/arch-exynos/spi.h rename board/samsung/smdk5250/{mmc_boot.c => spl_boot.c} (66%) create mode 100644 drivers/spi/exynos_spi.c

This patch adds pinmux support for SPI channels
Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None. Changes in V3: - Removed the slave flag for SPI. arch/arm/cpu/armv7/exynos/pinmux.c | 51 ++++++++++++++++++++++++++++- arch/arm/include/asm/arch-exynos/periph.h | 5 +++ 2 files changed, 55 insertions(+), 1 deletions(-)
diff --git a/arch/arm/cpu/armv7/exynos/pinmux.c b/arch/arm/cpu/armv7/exynos/pinmux.c index 7776add..13f75e0 100644 --- a/arch/arm/cpu/armv7/exynos/pinmux.c +++ b/arch/arm/cpu/armv7/exynos/pinmux.c @@ -230,6 +230,49 @@ static void exynos5_i2c_config(int peripheral, int flags) } }
+void exynos5_spi_config(int peripheral) +{ + int cfg = 0, pin = 0, i; + struct s5p_gpio_bank *bank = NULL; + struct exynos5_gpio_part1 *gpio1 = + (struct exynos5_gpio_part1 *) samsung_get_base_gpio_part1(); + struct exynos5_gpio_part2 *gpio2 = + (struct exynos5_gpio_part2 *) samsung_get_base_gpio_part2(); + + switch (peripheral) { + case PERIPH_ID_SPI0: + bank = &gpio1->a2; + cfg = GPIO_FUNC(0x2); + pin = 0; + break; + case PERIPH_ID_SPI1: + bank = &gpio1->a2; + cfg = GPIO_FUNC(0x2); + pin = 4; + break; + case PERIPH_ID_SPI2: + bank = &gpio1->b1; + cfg = GPIO_FUNC(0x5); + pin = 1; + break; + case PERIPH_ID_SPI3: + bank = &gpio2->f1; + cfg = GPIO_FUNC(0x2); + pin = 0; + break; + case PERIPH_ID_SPI4: + for (i = 2; i < 4; i++) + s5p_gpio_cfg_pin(&gpio2->f0, i, GPIO_FUNC(0x4)); + for (i = 4; i < 6; i++) + s5p_gpio_cfg_pin(&gpio2->e0, i, GPIO_FUNC(0x4)); + break; + } + if (peripheral != PERIPH_ID_SPI4) { + for (i = pin; i < pin + 4; i++) + s5p_gpio_cfg_pin(bank, i, cfg); + } +} + static int exynos5_pinmux_config(int peripheral, int flags) { switch (peripheral) { @@ -257,11 +300,17 @@ static int exynos5_pinmux_config(int peripheral, int flags) case PERIPH_ID_I2C7: exynos5_i2c_config(peripheral, flags); break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + exynos5_spi_config(peripheral); + break; default: debug("%s: invalid peripheral %d", __func__, peripheral); return -1; } - return 0; }
diff --git a/arch/arm/include/asm/arch-exynos/periph.h b/arch/arm/include/asm/arch-exynos/periph.h index b861d7d..dafc3f3 100644 --- a/arch/arm/include/asm/arch-exynos/periph.h +++ b/arch/arm/include/asm/arch-exynos/periph.h @@ -43,6 +43,11 @@ enum periph_id { PERIPH_ID_SDMMC2, PERIPH_ID_SDMMC3, PERIPH_ID_SROMC, + PERIPH_ID_SPI0, + PERIPH_ID_SPI1, + PERIPH_ID_SPI2, + PERIPH_ID_SPI3, + PERIPH_ID_SPI4, PERIPH_ID_UART0, PERIPH_ID_UART1, PERIPH_ID_UART2,

SMDK EVT1 has a different Winbond part, added its part details to the SPI flash table
Signed-off-by: Abhilash Kesavan a.kesavan@samsung.com Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - Corrected the commit message. Changes in V3: - None. drivers/mtd/spi/winbond.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index c20faa2..b345efc 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -107,6 +107,14 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .nr_blocks = 256, .name = "W25Q128", }, + { + .id = 0x5014, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 128, + .name = "W25Q80", + }, };
static int winbond_erase(struct spi_flash *flash, u32 offset, size_t len)

On Wednesday 01 August 2012 05:33:27 Rajeshwari Shinde wrote:
SMDK EVT1 has a different Winbond part, added its part details to the SPI flash table
you'll need to rebase this now onto current mainline -mike

This patch adds api to calculate and set the clock for SPI channels
Signed-off-by: Simon Glass sjg@chromium.org Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None Changes in V3: - Corrected Warning messages. arch/arm/cpu/armv7/exynos/clock.c | 124 ++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-exynos/clk.h | 4 +- 2 files changed, 127 insertions(+), 1 deletions(-)
diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c index de3db8e..2aa511e 100644 --- a/arch/arm/cpu/armv7/exynos/clock.c +++ b/arch/arm/cpu/armv7/exynos/clock.c @@ -628,6 +628,122 @@ static unsigned long exynos5_get_i2c_clk(void) return aclk_66; }
+/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + + debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + assert(best_fine_scalar != NULL); + assert(main_scaler_bits <= fine_scalar_bits); + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = max(min(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + + debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +static int exynos5_spi_set_clock_rate(enum periph_id periph_id, + unsigned int rate) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + int main; + unsigned int fine; + unsigned shift, pre_shift; + unsigned mask = 0xff; + u32 *reg; + + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { + debug("%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + main = main - 1; + fine = fine - 1; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 0; + pre_shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 16; + pre_shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 0; + pre_shift = 8; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 0; + pre_shift = 4; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 12; + pre_shift = 16; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + clrsetbits_le32(reg, mask << shift, (main & mask) << shift); + clrsetbits_le32(reg, mask << pre_shift, (fine & mask) << pre_shift); + + return 0; +} + unsigned long get_pll_clk(int pllreg) { if (cpu_is_exynos5()) @@ -697,3 +813,11 @@ void set_mipi_clk(void) if (cpu_is_exynos4()) exynos4_set_mipi_clk(); } + +int spi_set_clock_rate(enum periph_id periph_id, unsigned int rate) +{ + if (cpu_is_exynos5()) + return exynos5_spi_set_clock_rate(periph_id, rate); + else + return 0; +} diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h index 5529025..4e51402 100644 --- a/arch/arm/include/asm/arch-exynos/clk.h +++ b/arch/arm/include/asm/arch-exynos/clk.h @@ -22,6 +22,8 @@ #ifndef __ASM_ARM_ARCH_CLK_H_ #define __ASM_ARM_ARCH_CLK_H_
+#include <asm/arch/pinmux.h> + #define APLL 0 #define MPLL 1 #define EPLL 2 @@ -38,5 +40,5 @@ void set_mmc_clk(int dev_index, unsigned int div); unsigned long get_lcd_clk(void); void set_lcd_clk(void); void set_mipi_clk(void); - +int spi_set_clock_rate(enum periph_id periph_id, unsigned int rate); #endif

Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None Changes in V3: - None. arch/arm/include/asm/arch-exynos/cpu.h | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/arm/include/asm/arch-exynos/cpu.h b/arch/arm/include/asm/arch-exynos/cpu.h index 0e6ea87..89c2dd3 100644 --- a/arch/arm/include/asm/arch-exynos/cpu.h +++ b/arch/arm/include/asm/arch-exynos/cpu.h @@ -51,11 +51,13 @@ #define EXYNOS4_UART_BASE 0x13800000 #define EXYNOS4_I2C_BASE 0x13860000 #define EXYNOS4_ADC_BASE 0x13910000 +#define EXYNOS4_SPI_BASE 0x13920000 #define EXYNOS4_PWMTIMER_BASE 0x139D0000 #define EXYNOS4_MODEM_BASE 0x13A00000 #define EXYNOS4_USBPHY_CONTROL 0x10020704
#define EXYNOS4_GPIO_PART4_BASE DEVICE_NOT_AVAILABLE +#define EXYNOS4_SPI_ISP_BASE DEVICE_NOT_AVAILABLE
/* EXYNOS5 */ #define EXYNOS5_I2C_SPACING 0x10000 @@ -80,7 +82,9 @@ #define EXYNOS5_SROMC_BASE 0x12250000 #define EXYNOS5_UART_BASE 0x12C00000 #define EXYNOS5_I2C_BASE 0x12C60000 +#define EXYNOS5_SPI_BASE 0x12D20000 #define EXYNOS5_PWMTIMER_BASE 0x12DD0000 +#define EXYNOS5_SPI_ISP_BASE 0x131A0000 #define EXYNOS5_GPIO_PART2_BASE 0x13400000 #define EXYNOS5_FIMD_BASE 0x14400000
@@ -170,6 +174,8 @@ SAMSUNG_BASE(usb_ehci, USB_HOST_EHCI_BASE) SAMSUNG_BASE(usb_otg, USBOTG_BASE) SAMSUNG_BASE(watchdog, WATCHDOG_BASE) SAMSUNG_BASE(power, POWER_BASE) +SAMSUNG_BASE(spi, SPI_BASE) +SAMSUNG_BASE(spi_isp, SPI_ISP_BASE) #endif
#endif /* _EXYNOS4_CPU_H */

This patch adds SPI driver for EXYNOS.
Signed-off-by: Simon Glass sjg@chromium.org Signed-off-by: Padmavathi Venna padma.v@samsung.com Signed-off-by: Gabe Black gabeblack@google.com Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None. Changes in V3: - Removed SPI_SLAVE flag. arch/arm/include/asm/arch-exynos/spi.h | 78 +++++++ drivers/spi/Makefile | 1 + drivers/spi/exynos_spi.c | 368 ++++++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-exynos/spi.h create mode 100644 drivers/spi/exynos_spi.c
diff --git a/arch/arm/include/asm/arch-exynos/spi.h b/arch/arm/include/asm/arch-exynos/spi.h new file mode 100644 index 0000000..7cab1e9 --- /dev/null +++ b/arch/arm/include/asm/arch-exynos/spi.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Padmavathi Venna padma.v@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_ARCH_EXYNOS_COMMON_SPI_H_ +#define __ASM_ARCH_EXYNOS_COMMON_SPI_H_ + +#ifndef __ASSEMBLY__ + +/* SPI peripheral register map; padded to 64KB */ +struct exynos_spi { + unsigned int ch_cfg; /* 0x00 */ + unsigned char reserved0[4]; + unsigned int mode_cfg; /* 0x08 */ + unsigned int cs_reg; /* 0x0c */ + unsigned char reserved1[4]; + unsigned int spi_sts; /* 0x14 */ + unsigned int tx_data; /* 0x18 */ + unsigned int rx_data; /* 0x1c */ + unsigned int pkt_cnt; /* 0x20 */ + unsigned char reserved2[4]; + unsigned char reserved3[4]; + unsigned int fb_clk; /* 0x2c */ + unsigned char padding[0xffd0]; +}; + +#define EXYNOS_SPI_MAX_FREQ 50000000 + +#define SPI_TIMEOUT_MS 10 + +/* SPI_CHCFG */ +#define SPI_CH_HS_EN (1 << 6) +#define SPI_CH_RST (1 << 5) +#define SPI_SLAVE_MODE (1 << 4) +#define SPI_CH_CPOL_L (1 << 3) +#define SPI_CH_CPHA_B (1 << 2) +#define SPI_RX_CH_ON (1 << 1) +#define SPI_TX_CH_ON (1 << 0) + +/* SPI_MODECFG */ +#define SPI_MODE_CH_WIDTH_WORD (0x2 << 29) +#define SPI_MODE_BUS_WIDTH_WORD (0x2 << 17) + +/* SPI_CSREG */ +#define SPI_SLAVE_SIG_INACT (1 << 0) + +/* SPI_STS */ +#define SPI_ST_TX_DONE (1 << 25) +#define SPI_FIFO_LVL_MASK 0x1ff +#define SPI_TX_LVL_OFFSET 6 +#define SPI_RX_LVL_OFFSET 15 + +/* Feedback Delay */ +#define SPI_CLK_BYPASS (0 << 0) +#define SPI_FB_DELAY_90 (1 << 0) +#define SPI_FB_DELAY_180 (2 << 0) +#define SPI_FB_DELAY_270 (3 << 0) + +/* Packet Count */ +#define SPI_PACKET_CNT_EN (1 << 16) + +#endif /* __ASSEMBLY__ */ +#endif diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c20f1f2..f15adf0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o COBJS-$(CONFIG_CF_SPI) += cf_spi.o COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o +COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c new file mode 100644 index 0000000..a633f95 --- /dev/null +++ b/drivers/spi/exynos_spi.c @@ -0,0 +1,368 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Padmavathi Venna padma.v@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 + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pinmux.h> +#include <asm/arch-exynos/spi.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Information about each SPI controller */ +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ +}; + +/* A list of spi buses that we know about */ +static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS]; +static unsigned int bus_count; + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; +}; + +static struct spi_bus *spi_get_bus(unsigned dev_index) +{ + if (dev_index < bus_count) + return &spi_bus[dev_index]; + debug("%s: invalid bus %d", __func__, dev_index); + + return NULL; +} + +static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave) +{ + return container_of(slave, struct exynos_spi_slave, slave); +} + +/** + * Setup the driver private data + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @param max_hz Required spi frequency + * @param mode Required spi mode (clk polarity, clk phase and + * master or slave) + * @return new device or NULL + */ +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct exynos_spi_slave *spi_slave; + struct spi_bus *bus; + + if (!spi_cs_is_valid(busnum, cs)) { + debug("%s: Invalid bus/chip select %d, %d\n", __func__, + busnum, cs); + return NULL; + } + + spi_slave = malloc(sizeof(*spi_slave)); + if (!spi_slave) { + debug("%s: Could not allocate spi_slave\n", __func__); + return NULL; + } + + bus = &spi_bus[busnum]; + spi_slave->slave.bus = busnum; + spi_slave->slave.cs = cs; + spi_slave->regs = bus->regs; + spi_slave->mode = mode; + spi_slave->periph_id = bus->periph_id; + if (bus->periph_id == PERIPH_ID_SPI0) + spi_slave->fifo_size = 256; + else + spi_slave->fifo_size = 64; + + spi_slave->freq = bus->frequency; + if (max_hz) + spi_slave->freq = min(max_hz, spi_slave->freq); + + return &spi_slave->slave; +} + +/** + * Free spi controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_free_slave(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + free(spi_slave); +} + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_flush_fifo(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + +/** + * Initialize the spi base registers, set the required clock frequency and + * initialize the gpios + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @return zero on success else a negative value + */ +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = spi_set_clock_rate(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + +/** + * Reset the spi H/W and flush the tx and rx fifos + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_release_bus(struct spi_slave *slave) +{ + spi_flush_fifo(slave); +} + +static void spi_get_fifo_levels(struct exynos_spi *regs, + int *rx_lvl, int *tx_lvl) +{ + uint32_t spi_sts = readl(®s->spi_sts); + + *rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; + *tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; +} + +/** + * If there's something to transfer, do a software reset and set a + * transaction size. + * + * @param regs SPI peripheral registers + * @param count Number of bytes to transfer + */ +static void spi_request_bytes(struct exynos_spi *regs, int count) +{ + assert(count && count < (1 << 16)); + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); +} + +static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, + void **dinp, void const **doutp) +{ + struct exynos_spi *regs = spi_slave->regs; + uchar *rxp = *dinp; + const uchar *txp = *doutp; + int rx_lvl, tx_lvl; + uint out_bytes, in_bytes; + + out_bytes = in_bytes = todo; + + /* + * If there's something to send, do a software reset and set a + * transaction size. + */ + spi_request_bytes(regs, todo); + + /* + * Bytes are transmitted/received in pairs. Wait to receive all the + * data because then transmission will be done as well. + */ + while (in_bytes) { + int temp; + + /* Keep the fifos full/empty. */ + spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); + if (tx_lvl < spi_slave->fifo_size && out_bytes) { + temp = txp ? *txp++ : 0xff; + writel(temp, ®s->tx_data); + out_bytes--; + } + if (rx_lvl > 0 && in_bytes) { + temp = readl(®s->rx_data); + if (rxp) + *rxp++ = temp; + in_bytes--; + } + } + *dinp = rxp; + *doutp = txp; +} + +/** + * Transfer and receive data + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @param bitlen No of bits to tranfer or receive + * @param dout Pointer to transfer buffer + * @param din Pointer to receive buffer + * @param flags Flags for transfer begin and end + * @return zero on success else a negative value + */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + int upto, todo; + int bytelen; + + /* spi core configured to do 8 bit transfers */ + if (bitlen % 8) { + debug("Non byte aligned SPI transfer.\n"); + return -1; + } + + /* Start the transaction, if necessary. */ + if ((flags & SPI_XFER_BEGIN)) + spi_cs_activate(slave); + + /* Exynos SPI limits each transfer to 65535 bytes */ + bytelen = bitlen / 8; + for (upto = 0; upto < bytelen; upto += todo) { + todo = min(bytelen - upto, (1 << 16) - 1); + spi_rx_tx(spi_slave, todo, &din, &dout); + } + + /* Stop the transaction, if necessary. */ + if ((flags & SPI_XFER_END)) + spi_cs_deactivate(slave); + + return 0; +} + +/** + * Validates the bus and chip select numbers + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @return one on success else zero + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return spi_get_bus(bus) && cs == 0; +} + +/** + * Activate the CS by driving it LOW + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); +} + +/** + * Deactivate the CS by driving it HIGH + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Deactivate CS, bus %d\n", spi_slave->slave.bus); +} + +static inline struct exynos_spi *get_spi_base(int dev_index) +{ + if (dev_index < 3) + return (struct exynos_spi *)samsung_get_base_spi() + dev_index; + else + return (struct exynos_spi *)samsung_get_base_spi_isp() + + (dev_index - 3); +} + +/* Sadly there is no error return from this function */ +void spi_init(void) +{ + int i; + struct spi_bus *bus; + for (i = 0; i < EXYNOS5_SPI_NUM_CONTROLLERS; i++) { + bus = &spi_bus[i]; + bus->regs = get_spi_base(i); + bus->periph_id = PERIPH_ID_SPI0 + i; + if (i == 1) + bus->frequency = 2500000; + else + bus->frequency = 500000; + bus->inited = 1; + bus_count++; + } +}

On Wednesday 01 August 2012 05:33:30 Rajeshwari Shinde wrote:
--- /dev/null +++ b/drivers/spi/exynos_spi.c
+DECLARE_GLOBAL_DATA_PTR;
i don't think you use this, so delete
+void spi_flush_fifo(struct spi_slave *slave)
static
+void spi_init(void) +{ ...
- for (i = 0; i < EXYNOS5_SPI_NUM_CONTROLLERS; i++) {
...
bus_count++;
why bother with a static bus_count ? just use the existing define. -mike

This patch enables SPI driver for EXYNOS5.
Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None. Changes in V3: - Corrected the warning message. board/samsung/smdk5250/smdk5250.c | 4 ++++ include/configs/smdk5250.h | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletions(-)
diff --git a/board/samsung/smdk5250/smdk5250.c b/board/samsung/smdk5250/smdk5250.c index a5816e4..069c9e8 100644 --- a/board/samsung/smdk5250/smdk5250.c +++ b/board/samsung/smdk5250/smdk5250.c @@ -24,6 +24,7 @@ #include <asm/io.h> #include <i2c.h> #include <netdev.h> +#include <spi.h> #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> #include <asm/arch/mmc.h> @@ -63,6 +64,9 @@ static int smc9115_pre_init(void) int board_init(void) { gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL); +#ifdef CONFIG_EXYNOS_SPI + spi_init(); +#endif return 0; }
diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index 27dab76..29b7ac6 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -167,7 +167,7 @@ #undef CONFIG_CMD_IMLS #define CONFIG_IDENT_STRING " for SMDK5250"
-#define CONFIG_ENV_IS_IN_MMC +/*#define CONFIG_ENV_IS_IN_MMC*/ #define CONFIG_SYS_MMC_ENV_DEV 0
#define CONFIG_SECURE_BL1_ONLY @@ -216,6 +216,27 @@ #define CONFIG_ENV_SROM_BANK 1 #endif /*CONFIG_CMD_NET*/
+/* SPI */ +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_SPI_FLASH + +#ifdef CONFIG_SPI_FLASH +#define CONFIG_EXYNOS_SPI +#define CONFIG_CMD_SF +#define CONFIG_CMD_SPI +#define CONFIG_SPI_FLASH_WINBOND +#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0 +#define CONFIG_SF_DEFAULT_SPEED 50000000 +#define EXYNOS5_SPI_NUM_CONTROLLERS 5 +#endif + +#ifdef CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_ENV_SPI_MODE SPI_MODE_0 +#define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_SPI_BUS 1 +#define CONFIG_ENV_SPI_MAX_HZ 50000000 +#endif + /* Enable devicetree support */ #define CONFIG_OF_LIBFDT

This patch enables SPI Booting for EXYNOS5
Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com --- Changes in V2: - None. Changes in V3: - None. board/samsung/smdk5250/Makefile | 2 +- board/samsung/smdk5250/{mmc_boot.c => spl_boot.c} | 31 +++++++++++++++++++- include/configs/smdk5250.h | 5 +++ 3 files changed, 35 insertions(+), 3 deletions(-) rename board/samsung/smdk5250/{mmc_boot.c => spl_boot.c} (66%)
diff --git a/board/samsung/smdk5250/Makefile b/board/samsung/smdk5250/Makefile index 1474fa8..47c6a5a 100644 --- a/board/samsung/smdk5250/Makefile +++ b/board/samsung/smdk5250/Makefile @@ -36,7 +36,7 @@ COBJS += smdk5250.o endif
ifdef CONFIG_SPL_BUILD -COBJS += mmc_boot.o +COBJS += spl_boot.o endif
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/board/samsung/smdk5250/mmc_boot.c b/board/samsung/smdk5250/spl_boot.c similarity index 66% rename from board/samsung/smdk5250/mmc_boot.c rename to board/samsung/smdk5250/spl_boot.c index 449a919..d8f3c1e 100644 --- a/board/samsung/smdk5250/mmc_boot.c +++ b/board/samsung/smdk5250/spl_boot.c @@ -23,6 +23,16 @@ #include<common.h> #include<config.h>
+enum boot_mode { + BOOT_MODE_MMC = 4, + BOOT_MODE_SERIAL = 20, + /* Boot based on Operating Mode pin settings */ + BOOT_MODE_OM = 32, + BOOT_MODE_USB, /* Boot using USB download */ +}; + + typedef u32 (*spi_copy_func_t)(u32 offset, u32 nblock, u32 dst); + /* * Copy U-boot from mmc to RAM: * COPY_BL2_FNPTR_ADDR: Address in iRAM, which Contains @@ -30,9 +40,26 @@ */ void copy_uboot_to_ram(void) { - u32 (*copy_bl2)(u32, u32, u32) = (void *) *(u32 *)COPY_BL2_FNPTR_ADDR; + spi_copy_func_t spi_copy; + enum boot_mode bootmode; + u32 (*copy_bl2)(u32, u32, u32); + + bootmode = readl(EXYNOS5_POWER_BASE) & OM_STAT;
- copy_bl2(BL2_START_OFFSET, BL2_SIZE_BLOC_COUNT, CONFIG_SYS_TEXT_BASE); + switch (bootmode) { + case BOOT_MODE_SERIAL: + spi_copy = *(spi_copy_func_t *)EXYNOS_COPY_SPI_FNPTR_ADDR; + spi_copy(SPI_FLASH_UBOOT_POS, CONFIG_BL2_SIZE, + CONFIG_SYS_TEXT_BASE); + break; + case BOOT_MODE_MMC: + copy_bl2 = (void *) *(u32 *)COPY_BL2_FNPTR_ADDR; + copy_bl2(BL2_START_OFFSET, BL2_SIZE_BLOC_COUNT, + CONFIG_SYS_TEXT_BASE); + break; + default: + break; + } }
void board_init_f(unsigned long bootflag) diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index 29b7ac6..4b9093c 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -192,6 +192,11 @@ /* U-boot copy size from boot Media to DRAM.*/ #define BL2_START_OFFSET (CONFIG_BL2_OFFSET/512) #define BL2_SIZE_BLOC_COUNT (CONFIG_BL2_SIZE/512) + +#define OM_STAT (0x1f << 1) +#define EXYNOS_COPY_SPI_FNPTR_ADDR 0x02020058 +#define SPI_FLASH_UBOOT_POS (CONFIG_SEC_FW_SIZE + CONFIG_BL1_SIZE) + #define CONFIG_DOS_PARTITION
#define CONFIG_IRAM_STACK 0x02050000
participants (2)
-
Mike Frysinger
-
Rajeshwari Shinde