Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/linux-sunxi/sunxi-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiarhei Siamashka <siarhei.siamashka@gmail.com>2016-05-12 01:54:04 +0300
committerSiarhei Siamashka <siarhei.siamashka@gmail.com>2016-06-03 06:04:44 +0300
commit958143e77bb3f7c4c70326dd6f400fe4b719e407 (patch)
tree45c67ea5f263dc9eddd703c443dd135153d55a7a /uart0-helloworld-sdboot.c
parentce9cf33606492076b81e1157ba9fc54b56379335 (diff)
Add new uart0-helloworld-sdboot.sunxi bootable test image
This is a universal bootable image, which just prints a hello message on UART0. For example, it is useful as a test payload when debugging the SPI boot functionality: https://linux-sunxi.org/Bootable_SPI_flash Changes in v2: - A workaround for https://patchwork.ozlabs.org/patch/622173 - Also print the type of the used bootable media Changes in v3: - Bernhard Nortmann's workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63803 More details in https://github.com/linux-sunxi/sunxi-tools/pull/44 Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
Diffstat (limited to 'uart0-helloworld-sdboot.c')
-rw-r--r--uart0-helloworld-sdboot.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/uart0-helloworld-sdboot.c b/uart0-helloworld-sdboot.c
new file mode 100644
index 0000000..e0f4e5b
--- /dev/null
+++ b/uart0-helloworld-sdboot.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Partially based on the uart code from ar100-info
+ *
+ * (C) Copyright 2013 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
+ *
+ * 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
+ */
+
+/*
+ * Partially based on the sunxi gpio code from U-Boot
+ *
+ * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
+ *
+ * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
+ *
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+typedef unsigned int u32;
+
+#define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v))
+#define readl(addr) (*((volatile unsigned long *)(addr)))
+#define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v))
+
+#define SUNXI_UART0_BASE 0x01C28000
+#define SUNXI_PIO_BASE 0x01C20800
+#define AW_CCM_BASE 0x01c20000
+#define AW_SRAMCTRL_BASE 0x01c00000
+
+/*****************************************************************************
+ * GPIO code, borrowed from U-Boot *
+ *****************************************************************************/
+
+#define SUNXI_GPIO_A 0
+#define SUNXI_GPIO_B 1
+#define SUNXI_GPIO_C 2
+#define SUNXI_GPIO_D 3
+#define SUNXI_GPIO_E 4
+#define SUNXI_GPIO_F 5
+#define SUNXI_GPIO_G 6
+#define SUNXI_GPIO_H 7
+#define SUNXI_GPIO_I 8
+
+struct sunxi_gpio {
+ u32 cfg[4];
+ u32 dat;
+ u32 drv[2];
+ u32 pull[2];
+};
+
+struct sunxi_gpio_reg {
+ struct sunxi_gpio gpio_bank[10];
+};
+
+#define GPIO_BANK(pin) ((pin) >> 5)
+#define GPIO_NUM(pin) ((pin) & 0x1F)
+
+#define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3)
+#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2)
+
+#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
+#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
+/* GPIO bank sizes */
+#define SUNXI_GPIO_A_NR (32)
+#define SUNXI_GPIO_B_NR (32)
+#define SUNXI_GPIO_C_NR (32)
+#define SUNXI_GPIO_D_NR (32)
+#define SUNXI_GPIO_E_NR (32)
+#define SUNXI_GPIO_F_NR (32)
+#define SUNXI_GPIO_G_NR (32)
+#define SUNXI_GPIO_H_NR (32)
+#define SUNXI_GPIO_I_NR (32)
+
+#define SUNXI_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + 0)
+
+enum sunxi_gpio_number {
+ SUNXI_GPIO_A_START = 0,
+ SUNXI_GPIO_B_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_A),
+ SUNXI_GPIO_C_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_B),
+ SUNXI_GPIO_D_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_C),
+ SUNXI_GPIO_E_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_D),
+ SUNXI_GPIO_F_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_E),
+ SUNXI_GPIO_G_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_F),
+ SUNXI_GPIO_H_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_G),
+ SUNXI_GPIO_I_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_H),
+};
+
+/* SUNXI GPIO number definitions */
+#define SUNXI_GPA(_nr) (SUNXI_GPIO_A_START + (_nr))
+#define SUNXI_GPB(_nr) (SUNXI_GPIO_B_START + (_nr))
+#define SUNXI_GPC(_nr) (SUNXI_GPIO_C_START + (_nr))
+#define SUNXI_GPD(_nr) (SUNXI_GPIO_D_START + (_nr))
+#define SUNXI_GPE(_nr) (SUNXI_GPIO_E_START + (_nr))
+#define SUNXI_GPF(_nr) (SUNXI_GPIO_F_START + (_nr))
+#define SUNXI_GPG(_nr) (SUNXI_GPIO_G_START + (_nr))
+#define SUNXI_GPH(_nr) (SUNXI_GPIO_H_START + (_nr))
+#define SUNXI_GPI(_nr) (SUNXI_GPIO_I_START + (_nr))
+
+/* GPIO pin function config */
+#define SUNXI_GPIO_INPUT (0)
+#define SUNXI_GPIO_OUTPUT (1)
+#define SUN4I_GPB_UART0 (2)
+#define SUN5I_GPB_UART0 (2)
+#define SUN6I_GPH_UART0 (2)
+#define SUN8I_H3_GPA_UART0 (2)
+#define SUN50I_A64_GPB_UART0 (4)
+#define SUNXI_GPF_UART0 (4)
+
+/* GPIO pin pull-up/down config */
+#define SUNXI_GPIO_PULL_DISABLE (0)
+#define SUNXI_GPIO_PULL_UP (1)
+#define SUNXI_GPIO_PULL_DOWN (2)
+
+int sunxi_gpio_set_cfgpin(u32 pin, u32 val)
+{
+ u32 cfg;
+ u32 bank = GPIO_BANK(pin);
+ u32 index = GPIO_CFG_INDEX(pin);
+ u32 offset = GPIO_CFG_OFFSET(pin);
+ struct sunxi_gpio *pio =
+ &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
+ cfg = readl(&pio->cfg[0] + index);
+ cfg &= ~(0xf << offset);
+ cfg |= val << offset;
+ writel(cfg, &pio->cfg[0] + index);
+ return 0;
+}
+
+int sunxi_gpio_set_pull(u32 pin, u32 val)
+{
+ u32 cfg;
+ u32 bank = GPIO_BANK(pin);
+ u32 index = GPIO_PULL_INDEX(pin);
+ u32 offset = GPIO_PULL_OFFSET(pin);
+ struct sunxi_gpio *pio =
+ &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
+ cfg = readl(&pio->pull[0] + index);
+ cfg &= ~(0x3 << offset);
+ cfg |= val << offset;
+ writel(cfg, &pio->pull[0] + index);
+ return 0;
+}
+
+int sunxi_gpio_output(u32 pin, u32 val)
+{
+ u32 dat;
+ u32 bank = GPIO_BANK(pin);
+ u32 num = GPIO_NUM(pin);
+ struct sunxi_gpio *pio =
+ &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
+ dat = readl(&pio->dat);
+ if(val)
+ dat |= 1 << num;
+ else
+ dat &= ~(1 << num);
+ writel(dat, &pio->dat);
+ return 0;
+}
+
+int sunxi_gpio_input(u32 pin)
+{
+ u32 dat;
+ u32 bank = GPIO_BANK(pin);
+ u32 num = GPIO_NUM(pin);
+ struct sunxi_gpio *pio =
+ &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
+ dat = readl(&pio->dat);
+ dat >>= num;
+ return (dat & 0x1);
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
+ return sunxi_gpio_input(gpio);
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
+ return sunxi_gpio_output(gpio, value);
+}
+
+/*****************************************************************************
+ * Nearly all the Allwinner SoCs are using the same VER_REG register for *
+ * runtime SoC type identification. For additional details see: *
+ * *
+ * https://linux-sunxi.org/SRAM_Controller_Register_Guide *
+ * *
+ * Allwinner A80 is an oddball and has a non-standard address of the VER_REG *
+ * *
+ * Allwinner A10s and A13 are using the same SoC type id, but they can be *
+ * differentiated using a certain part of the SID register. *
+ *****************************************************************************/
+
+#define VER_REG (AW_SRAMCTRL_BASE + 0x24)
+#define SUN4I_SID_BASE 0x01C23800
+
+static u32 soc_id;
+
+void soc_detection_init(void)
+{
+ u32 midr;
+ asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr));
+
+ if (((midr >> 4) & 0xFFF) == 0xC0F) {
+ soc_id = 0x1639; /* ARM Cortex-A15, so likely Allwinner A80 */
+ } else {
+ set_wbit(VER_REG, 1 << 15);
+ soc_id = readl(VER_REG) >> 16;
+ }
+}
+
+int soc_is_a10(void)
+{
+ return soc_id == 0x1623;
+}
+
+int soc_is_a10s(void)
+{
+ return (soc_id == 0x1625) &&
+ (((readl(SUN4I_SID_BASE + 0x08) >> 12) & 0xf) == 7);
+}
+
+int soc_is_a13(void)
+{
+ return (soc_id == 0x1625) &&
+ !(((readl(SUN4I_SID_BASE + 0x08) >> 12) & 0xf) == 7);
+}
+
+int soc_is_a20(void)
+{
+ return soc_id == 0x1651;
+}
+
+int soc_is_a31(void)
+{
+ return soc_id == 0x1633;
+}
+
+int soc_is_a80(void)
+{
+ return soc_id == 0x1639;
+}
+
+int soc_is_a64(void)
+{
+ return soc_id == 0x1689;
+}
+
+int soc_is_h3(void)
+{
+ return soc_id == 0x1680;
+}
+
+/*****************************************************************************
+ * UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs *
+ * have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and *
+ * APB1 names, but newer SoCs just have renamed them into APB1 and APB2). *
+ * The constants below are using the new APB numbering convention. *
+ * Also the newer SoCs have introduced the APB2_RESET register, but writing *
+ * to it effectively goes nowhere on older SoCs and is harmless. *
+ *****************************************************************************/
+
+#define CONFIG_CONS_INDEX 1
+#define APB2_CFG (AW_CCM_BASE + 0x058)
+#define APB2_GATE (AW_CCM_BASE + 0x06C)
+#define APB2_RESET (AW_CCM_BASE + 0x2D8)
+#define APB2_GATE_UART_SHIFT (16)
+#define APB2_RESET_UART_SHIFT (16)
+
+void clock_init_uart(void)
+{
+ /* Open the clock gate for UART0 */
+ set_wbit(APB2_GATE, 1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
+ /* Deassert UART0 reset (only needed on A31/A64/H3) */
+ set_wbit(APB2_RESET, 1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
+}
+
+/*****************************************************************************
+ * UART0 pins muxing is different for different SoC variants. *
+ * Allwinner A13 is a bit special, because there are no dedicated UART0 pins *
+ * and they are shared with MMC0. *
+ *****************************************************************************/
+
+void gpio_init(void)
+{
+ if (soc_is_a10() || soc_is_a20()) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP);
+ } else if (soc_is_a10s()) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP);
+ } else if (soc_is_a13()) {
+ /* Disable PB19/PB20 as UART0 to avoid conflict */
+ gpio_direction_input(SUNXI_GPB(19));
+ gpio_direction_input(SUNXI_GPB(20));
+ /* Use SD breakout board to access UART0 on MMC0 pins */
+ sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPF(4), SUNXI_GPIO_PULL_UP);
+ } else if (soc_is_a31()) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP);
+ } else if (soc_is_a64()) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
+ } else if (soc_is_h3()) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
+ } else {
+ /* Unknown SoC */
+ while (1) {}
+ }
+}
+
+/*****************************************************************************/
+
+#define UART0_RBR (SUNXI_UART0_BASE + 0x0) /* receive buffer register */
+#define UART0_THR (SUNXI_UART0_BASE + 0x0) /* transmit holding register */
+#define UART0_DLL (SUNXI_UART0_BASE + 0x0) /* divisor latch low register */
+
+#define UART0_DLH (SUNXI_UART0_BASE + 0x4) /* divisor latch high register */
+#define UART0_IER (SUNXI_UART0_BASE + 0x4) /* interrupt enable reigster */
+
+#define UART0_IIR (SUNXI_UART0_BASE + 0x8) /* interrupt identity register */
+#define UART0_FCR (SUNXI_UART0_BASE + 0x8) /* fifo control register */
+
+#define UART0_LCR (SUNXI_UART0_BASE + 0xc) /* line control register */
+
+#define UART0_LSR (SUNXI_UART0_BASE + 0x14) /* line status register */
+
+#define BAUD_115200 (0xD) /* 24 * 1000 * 1000 / 16 / 115200 = 13 */
+#define NO_PARITY (0)
+#define ONE_STOP_BIT (0)
+#define DAT_LEN_8_BITS (3)
+#define LC_8_N_1 (NO_PARITY << 3 | ONE_STOP_BIT << 2 | DAT_LEN_8_BITS)
+
+void uart0_init(void)
+{
+ clock_init_uart();
+
+ /* select dll dlh */
+ writel(0x80, UART0_LCR);
+ /* set baudrate */
+ writel(0, UART0_DLH);
+ writel(BAUD_115200, UART0_DLL);
+ /* set line control */
+ writel(LC_8_N_1, UART0_LCR);
+}
+
+void uart0_putc(char c)
+{
+ while (!(readl(UART0_LSR) & (1 << 6))) {}
+ writel(c, UART0_THR);
+}
+
+void uart0_puts(const char *s)
+{
+ while (*s) {
+ if (*s == '\n')
+ uart0_putc('\r');
+ uart0_putc(*s++);
+ }
+}
+
+/*****************************************************************************/
+
+/* A workaround for https://patchwork.ozlabs.org/patch/622173 */
+void __attribute__((section(".start"))) __attribute__((naked)) start(void)
+{
+ asm volatile("b main \n"
+ ".long 0xffffffff \n"
+ ".long 0xffffffff \n"
+ ".long 0xffffffff \n");
+}
+
+enum { BOOT_DEVICE_UNK, BOOT_DEVICE_FEL, BOOT_DEVICE_MMC0, BOOT_DEVICE_SPI };
+
+int get_boot_device(void)
+{
+ u32 *spl_signature = (void *)0x4;
+ if (soc_is_a64() || soc_is_a80())
+ spl_signature = (void *)0x10004;
+
+ /* Check the eGON.BT0 magic in the SPL header */
+ if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E)
+ return BOOT_DEVICE_FEL;
+
+ u32 boot_dev = spl_signature[9] & 0xFF; /* offset into SPL = 0x28 */
+ if (boot_dev == 0)
+ return BOOT_DEVICE_MMC0;
+ if (boot_dev == 3)
+ return BOOT_DEVICE_SPI;
+
+ return BOOT_DEVICE_UNK;
+}
+
+int main(void)
+{
+ soc_detection_init();
+ gpio_init();
+ uart0_init();
+
+ uart0_puts("\nHello from ");
+ if (soc_is_a10())
+ uart0_puts("Allwinner A10!\n");
+ else if (soc_is_a10s())
+ uart0_puts("Allwinner A10s!\n");
+ else if (soc_is_a13())
+ uart0_puts("Allwinner A13!\n");
+ else if (soc_is_a20())
+ uart0_puts("Allwinner A20!\n");
+ else if (soc_is_a31())
+ uart0_puts("Allwinner A31/A31s!\n");
+ else if (soc_is_a64())
+ uart0_puts("Allwinner A64!\n");
+ else if (soc_is_h3())
+ uart0_puts("Allwinner H3!\n");
+ else
+ uart0_puts("unknown Allwinner SoC!\n");
+
+ switch (get_boot_device()) {
+ case BOOT_DEVICE_FEL:
+ uart0_puts("Returning back to FEL.\n");
+ return 0;
+ case BOOT_DEVICE_MMC0:
+ uart0_puts("Booted from MMC0, entering an infinite loop.\n");
+ while (1) {}
+ case BOOT_DEVICE_SPI:
+ uart0_puts("Booted from SPI0, entering an infinite loop.\n");
+ while (1) {}
+ default:
+ uart0_puts("Booted from unknown media, entering an infinite loop.\n");
+ while (1) {}
+ };
+
+ return 0;
+}