uboot/arch/arm/mach-sunxi/spl_spi_sunxi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
   4 */
   5
   6#include <common.h>
   7#include <image.h>
   8#include <log.h>
   9#include <spl.h>
  10#include <asm/gpio.h>
  11#include <asm/io.h>
  12#include <linux/bitops.h>
  13#include <linux/delay.h>
  14#include <linux/libfdt.h>
  15
  16#ifdef CONFIG_SPL_OS_BOOT
  17#error CONFIG_SPL_OS_BOOT is not supported yet
  18#endif
  19
  20/*
  21 * This is a very simple U-Boot image loading implementation, trying to
  22 * replicate what the boot ROM is doing when loading the SPL. Because we
  23 * know the exact pins where the SPI Flash is connected and also know
  24 * that the Read Data Bytes (03h) command is supported, the hardware
  25 * configuration is very simple and we don't need the extra flexibility
  26 * of the SPI framework. Moreover, we rely on the default settings of
  27 * the SPI controler hardware registers and only adjust what needs to
  28 * be changed. This is good for the code size and this implementation
  29 * adds less than 400 bytes to the SPL.
  30 *
  31 * There are two variants of the SPI controller in Allwinner SoCs:
  32 * A10/A13/A20 (sun4i variant) and everything else (sun6i variant).
  33 * Both of them are supported.
  34 *
  35 * The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are
  36 * supported at the moment.
  37 */
  38
  39/*****************************************************************************/
  40/* SUN4I variant of the SPI controller                                       */
  41/*****************************************************************************/
  42
  43#define SUN4I_SPI0_CCTL             0x1C
  44#define SUN4I_SPI0_CTL              0x08
  45#define SUN4I_SPI0_RX               0x00
  46#define SUN4I_SPI0_TX               0x04
  47#define SUN4I_SPI0_FIFO_STA         0x28
  48#define SUN4I_SPI0_BC               0x20
  49#define SUN4I_SPI0_TC               0x24
  50
  51#define SUN4I_CTL_ENABLE            BIT(0)
  52#define SUN4I_CTL_MASTER            BIT(1)
  53#define SUN4I_CTL_TF_RST            BIT(8)
  54#define SUN4I_CTL_RF_RST            BIT(9)
  55#define SUN4I_CTL_XCH               BIT(10)
  56
  57/*****************************************************************************/
  58/* SUN6I variant of the SPI controller                                       */
  59/*****************************************************************************/
  60
  61#define SUN6I_SPI0_CCTL             0x24
  62#define SUN6I_SPI0_GCR              0x04
  63#define SUN6I_SPI0_TCR              0x08
  64#define SUN6I_SPI0_FIFO_STA         0x1C
  65#define SUN6I_SPI0_MBC              0x30
  66#define SUN6I_SPI0_MTC              0x34
  67#define SUN6I_SPI0_BCC              0x38
  68#define SUN6I_SPI0_TXD              0x200
  69#define SUN6I_SPI0_RXD              0x300
  70
  71#define SUN6I_CTL_ENABLE            BIT(0)
  72#define SUN6I_CTL_MASTER            BIT(1)
  73#define SUN6I_CTL_SRST              BIT(31)
  74#define SUN6I_TCR_XCH               BIT(31)
  75
  76/*****************************************************************************/
  77
  78#define CCM_AHB_GATING0             (0x01C20000 + 0x60)
  79#define CCM_H6_SPI_BGR_REG          (0x03001000 + 0x96c)
  80#ifdef CONFIG_MACH_SUN50I_H6
  81#define CCM_SPI0_CLK                (0x03001000 + 0x940)
  82#else
  83#define CCM_SPI0_CLK                (0x01C20000 + 0xA0)
  84#endif
  85#define SUN6I_BUS_SOFT_RST_REG0     (0x01C20000 + 0x2C0)
  86
  87#define AHB_RESET_SPI0_SHIFT        20
  88#define AHB_GATE_OFFSET_SPI0        20
  89
  90#define SPI0_CLK_DIV_BY_2           0x1000
  91#define SPI0_CLK_DIV_BY_4           0x1001
  92
  93/*****************************************************************************/
  94
  95/*
  96 * Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting
  97 * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3.
  98 * The H6 uses PC0, PC2, PC3, PC5.
  99 */
 100static void spi0_pinmux_setup(unsigned int pin_function)
 101{
 102        /* All chips use PC0 and PC2. */
 103        sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
 104        sunxi_gpio_set_cfgpin(SUNXI_GPC(2), pin_function);
 105
 106        /* All chips except H6 use PC1, and only H6 uses PC5. */
 107        if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 108                sunxi_gpio_set_cfgpin(SUNXI_GPC(1), pin_function);
 109        else
 110                sunxi_gpio_set_cfgpin(SUNXI_GPC(5), pin_function);
 111
 112        /* Older generations use PC23 for CS, newer ones use PC3. */
 113        if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I) ||
 114            IS_ENABLED(CONFIG_MACH_SUN8I_R40))
 115                sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function);
 116        else
 117                sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function);
 118}
 119
 120static bool is_sun6i_gen_spi(void)
 121{
 122        return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ||
 123               IS_ENABLED(CONFIG_MACH_SUN50I_H6);
 124}
 125
 126static uintptr_t spi0_base_address(void)
 127{
 128        if (IS_ENABLED(CONFIG_MACH_SUN8I_R40))
 129                return 0x01C05000;
 130
 131        if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 132                return 0x05010000;
 133
 134        if (!is_sun6i_gen_spi())
 135                return 0x01C05000;
 136
 137        return 0x01C68000;
 138}
 139
 140/*
 141 * Setup 6 MHz from OSC24M (because the BROM is doing the same).
 142 */
 143static void spi0_enable_clock(void)
 144{
 145        uintptr_t base = spi0_base_address();
 146
 147        /* Deassert SPI0 reset on SUN6I */
 148        if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 149                setbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1);
 150        else if (is_sun6i_gen_spi())
 151                setbits_le32(SUN6I_BUS_SOFT_RST_REG0,
 152                             (1 << AHB_RESET_SPI0_SHIFT));
 153
 154        /* Open the SPI0 gate */
 155        if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 156                setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
 157
 158        /* Divide by 4 */
 159        writel(SPI0_CLK_DIV_BY_4, base + (is_sun6i_gen_spi() ?
 160                                  SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL));
 161        /* 24MHz from OSC24M */
 162        writel((1 << 31), CCM_SPI0_CLK);
 163
 164        if (is_sun6i_gen_spi()) {
 165                /* Enable SPI in the master mode and do a soft reset */
 166                setbits_le32(base + SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
 167                             SUN6I_CTL_ENABLE | SUN6I_CTL_SRST);
 168                /* Wait for completion */
 169                while (readl(base + SUN6I_SPI0_GCR) & SUN6I_CTL_SRST)
 170                        ;
 171        } else {
 172                /* Enable SPI in the master mode and reset FIFO */
 173                setbits_le32(base + SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
 174                                                    SUN4I_CTL_ENABLE |
 175                                                    SUN4I_CTL_TF_RST |
 176                                                    SUN4I_CTL_RF_RST);
 177        }
 178}
 179
 180static void spi0_disable_clock(void)
 181{
 182        uintptr_t base = spi0_base_address();
 183
 184        /* Disable the SPI0 controller */
 185        if (is_sun6i_gen_spi())
 186                clrbits_le32(base + SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
 187                                             SUN6I_CTL_ENABLE);
 188        else
 189                clrbits_le32(base + SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
 190                                             SUN4I_CTL_ENABLE);
 191
 192        /* Disable the SPI0 clock */
 193        writel(0, CCM_SPI0_CLK);
 194
 195        /* Close the SPI0 gate */
 196        if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 197                clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
 198
 199        /* Assert SPI0 reset on SUN6I */
 200        if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 201                clrbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1);
 202        else if (is_sun6i_gen_spi())
 203                clrbits_le32(SUN6I_BUS_SOFT_RST_REG0,
 204                             (1 << AHB_RESET_SPI0_SHIFT));
 205}
 206
 207static void spi0_init(void)
 208{
 209        unsigned int pin_function = SUNXI_GPC_SPI0;
 210
 211        if (IS_ENABLED(CONFIG_MACH_SUN50I) ||
 212            IS_ENABLED(CONFIG_MACH_SUN50I_H6))
 213                pin_function = SUN50I_GPC_SPI0;
 214
 215        spi0_pinmux_setup(pin_function);
 216        spi0_enable_clock();
 217}
 218
 219static void spi0_deinit(void)
 220{
 221        /* New SoCs can disable pins, older could only set them as input */
 222        unsigned int pin_function = SUNXI_GPIO_INPUT;
 223
 224        if (is_sun6i_gen_spi())
 225                pin_function = SUNXI_GPIO_DISABLE;
 226
 227        spi0_disable_clock();
 228        spi0_pinmux_setup(pin_function);
 229}
 230
 231/*****************************************************************************/
 232
 233#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
 234
 235static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
 236                                 ulong spi_ctl_reg,
 237                                 ulong spi_ctl_xch_bitmask,
 238                                 ulong spi_fifo_reg,
 239                                 ulong spi_tx_reg,
 240                                 ulong spi_rx_reg,
 241                                 ulong spi_bc_reg,
 242                                 ulong spi_tc_reg,
 243                                 ulong spi_bcc_reg)
 244{
 245        writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
 246        writel(4, spi_tc_reg);           /* Transfer counter (bytes to send) */
 247        if (spi_bcc_reg)
 248                writel(4, spi_bcc_reg);  /* SUN6I also needs this */
 249
 250        /* Send the Read Data Bytes (03h) command header */
 251        writeb(0x03, spi_tx_reg);
 252        writeb((u8)(addr >> 16), spi_tx_reg);
 253        writeb((u8)(addr >> 8), spi_tx_reg);
 254        writeb((u8)(addr), spi_tx_reg);
 255
 256        /* Start the data transfer */
 257        setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
 258
 259        /* Wait until everything is received in the RX FIFO */
 260        while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
 261                ;
 262
 263        /* Skip 4 bytes */
 264        readl(spi_rx_reg);
 265
 266        /* Read the data */
 267        while (bufsize-- > 0)
 268                *buf++ = readb(spi_rx_reg);
 269
 270        /* tSHSL time is up to 100 ns in various SPI flash datasheets */
 271        udelay(1);
 272}
 273
 274static void spi0_read_data(void *buf, u32 addr, u32 len)
 275{
 276        u8 *buf8 = buf;
 277        u32 chunk_len;
 278        uintptr_t base = spi0_base_address();
 279
 280        while (len > 0) {
 281                chunk_len = len;
 282                if (chunk_len > SPI_READ_MAX_SIZE)
 283                        chunk_len = SPI_READ_MAX_SIZE;
 284
 285                if (is_sun6i_gen_spi()) {
 286                        sunxi_spi0_read_data(buf8, addr, chunk_len,
 287                                             base + SUN6I_SPI0_TCR,
 288                                             SUN6I_TCR_XCH,
 289                                             base + SUN6I_SPI0_FIFO_STA,
 290                                             base + SUN6I_SPI0_TXD,
 291                                             base + SUN6I_SPI0_RXD,
 292                                             base + SUN6I_SPI0_MBC,
 293                                             base + SUN6I_SPI0_MTC,
 294                                             base + SUN6I_SPI0_BCC);
 295                } else {
 296                        sunxi_spi0_read_data(buf8, addr, chunk_len,
 297                                             base + SUN4I_SPI0_CTL,
 298                                             SUN4I_CTL_XCH,
 299                                             base + SUN4I_SPI0_FIFO_STA,
 300                                             base + SUN4I_SPI0_TX,
 301                                             base + SUN4I_SPI0_RX,
 302                                             base + SUN4I_SPI0_BC,
 303                                             base + SUN4I_SPI0_TC,
 304                                             0);
 305                }
 306
 307                len  -= chunk_len;
 308                buf8 += chunk_len;
 309                addr += chunk_len;
 310        }
 311}
 312
 313static ulong spi_load_read(struct spl_load_info *load, ulong sector,
 314                           ulong count, void *buf)
 315{
 316        spi0_read_data(buf, sector, count);
 317
 318        return count;
 319}
 320
 321/*****************************************************************************/
 322
 323static int spl_spi_load_image(struct spl_image_info *spl_image,
 324                              struct spl_boot_device *bootdev)
 325{
 326        int ret = 0;
 327        struct image_header *header;
 328        header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
 329
 330        spi0_init();
 331
 332        spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40);
 333
 334        if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
 335                image_get_magic(header) == FDT_MAGIC) {
 336                struct spl_load_info load;
 337
 338                debug("Found FIT image\n");
 339                load.dev = NULL;
 340                load.priv = NULL;
 341                load.filename = NULL;
 342                load.bl_len = 1;
 343                load.read = spi_load_read;
 344                ret = spl_load_simple_fit(spl_image, &load,
 345                                          CONFIG_SYS_SPI_U_BOOT_OFFS, header);
 346        } else {
 347                ret = spl_parse_image_header(spl_image, header);
 348                if (ret)
 349                        return ret;
 350
 351                spi0_read_data((void *)spl_image->load_addr,
 352                               CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size);
 353        }
 354
 355        spi0_deinit();
 356
 357        return ret;
 358}
 359/* Use priorty 0 to override the default if it happens to be linked in */
 360SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);
 361