From 19e99fb4ff73f648f2b316d0ddd8ee3c01496bd4 Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Tue, 7 Jun 2016 14:28:34 +0300 Subject: sunxi: Support booting from SPI flash Allwinner devices support SPI flash as one of the possible bootable media type. The SPI flash chip needs to be connected to SPI0 pins (port C) to make this work. More information is available at: https://linux-sunxi.org/Bootable_SPI_flash This patch adds the initial support for booting from SPI flash. The existing SPI frameworks are not used in order to reduce the SPL code size. Right now the SPL size grows by ~370 bytes when CONFIG_SPL_SPI_SUNXI option is enabled. While there are no popular Allwinner devices with SPI flash at the moment, testing can be done using a SPI flash module (it can be bought for ~2$ on ebay) and jumper wires with the boards, which expose relevant pins on the expansion header. The SPI flash chips themselves are very cheap (some prices are even listed as low as 4 cents) and should not cost much if somebody decides to design a development board with an SPI flash chip soldered on the PCB. Another nice feature of the SPI flash is that it can be safely accessed in a device-independent way (since we know that the boot ROM is already probing these pins during the boot time). And if, for example, Olimex boards opted to use SPI flash instead of EEPROM, then they would have been able to have U-Boot installed in the SPI flash now and boot the rest of the system from the SATA hard drive. Hopefully we may see new interesting Allwinner based development boards in the future, now that the software support for the SPI flash is in a better shape :-) Testing can be done by enabling the CONFIG_SPL_SPI_SUNXI option in a board defconfig, then building U-Boot and finally flashing the resulting u-boot-sunxi-with-spl.bin binary over USB OTG with a help of the sunxi-fel tool: sunxi-fel spiflash-write 0 u-boot-sunxi-with-spl.bin The device needs to be switched into FEL (USB recovery) mode first. The most suitable boards for testing are Orange Pi PC and Pine64. Because these boards are cheap, have no built-in NAND/eMMC and expose SPI0 pins on the Raspberry Pi compatible expansion header. The A13-OLinuXino-Micro board also can be used. Signed-off-by: Siarhei Siamashka Reviewed-by: Simon Glass Signed-off-by: Hans de Goede diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 1ace548..bff7d14 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -141,6 +141,7 @@ enum sunxi_gpio_number { /* GPIO pin function config */ #define SUNXI_GPIO_INPUT 0 #define SUNXI_GPIO_OUTPUT 1 +#define SUNXI_GPIO_DISABLE 7 #define SUNXI_GPA_EMAC 2 #define SUN6I_GPA_GMAC 2 @@ -162,8 +163,10 @@ enum sunxi_gpio_number { #define SUN50I_GPB_UART0 4 #define SUNXI_GPC_NAND 2 +#define SUNXI_GPC_SPI0 3 #define SUNXI_GPC_SDC2 3 #define SUN6I_GPC_SDC3 4 +#define SUN50I_GPC_SPI0 4 #define SUN8I_GPD_SDC1 3 #define SUNXI_GPD_LCD0 2 diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 66e028e..3f5116b 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -223,6 +223,11 @@ u32 spl_boot_device(void) if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */ return BOOT_DEVICE_BOARD; +#ifdef CONFIG_SPL_SPI_SUNXI + if (readb(SPL_ADDR + 0x28) == SUNXI_BOOTED_FROM_SPI) + return BOOT_DEVICE_SPI; +#endif + /* The BROM will try to boot from mmc0 first, so try that first. */ #ifdef CONFIG_MMC mmc_initialize(gd->bd); diff --git a/common/spl/spl.c b/common/spl/spl.c index 840910a..0039716 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -270,7 +270,7 @@ struct boot_device_name boot_name_table[] = { #ifdef CONFIG_SPL_YMODEM_SUPPORT { BOOT_DEVICE_UART, "UART" }, #endif -#ifdef CONFIG_SPL_SPI_SUPPORT +#if defined(CONFIG_SPL_SPI_SUPPORT) || defined(CONFIG_SPL_SPI_FLASH_SUPPORT) { BOOT_DEVICE_SPI, "SPI" }, #endif #ifdef CONFIG_SPL_ETH_SUPPORT @@ -346,7 +346,7 @@ static int spl_load_image(u32 boot_device) case BOOT_DEVICE_UART: return spl_ymodem_load_image(); #endif -#ifdef CONFIG_SPL_SPI_SUPPORT +#if defined(CONFIG_SPL_SPI_SUPPORT) || defined(CONFIG_SPL_SPI_FLASH_SUPPORT) case BOOT_DEVICE_SPI: return spl_spi_load_image(); #endif diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 3f7433c..1f23c8e 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -128,4 +128,16 @@ config SPI_FLASH_MTD If unsure, say N +if SPL + +config SPL_SPI_SUNXI + bool "Support for SPI Flash on Allwinner SoCs in SPL" + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I_H3 || MACH_SUN50I + ---help--- + Enable support for SPI Flash. This option allows SPL to read from + sunxi SPI Flash. It uses the same method as the boot ROM, so does + not need any extra configuration. + +endif + endmenu # menu "SPI Flash Support" diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index c665836..6f47a66 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o +obj-$(CONFIG_SPL_SPI_SUNXI) += sunxi_spi_spl.o endif obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o sf_params.o sf.o diff --git a/drivers/mtd/spi/sunxi_spi_spl.c b/drivers/mtd/spi/sunxi_spi_spl.c new file mode 100644 index 0000000..e3ded5b --- /dev/null +++ b/drivers/mtd/spi/sunxi_spi_spl.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2016 Siarhei Siamashka + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SPL_OS_BOOT +#error CONFIG_SPL_OS_BOOT is not supported yet +#endif + +/* + * This is a very simple U-Boot image loading implementation, trying to + * replicate what the boot ROM is doing when loading the SPL. Because we + * know the exact pins where the SPI Flash is connected and also know + * that the Read Data Bytes (03h) command is supported, the hardware + * configuration is very simple and we don't need the extra flexibility + * of the SPI framework. Moreover, we rely on the default settings of + * the SPI controler hardware registers and only adjust what needs to + * be changed. This is good for the code size and this implementation + * adds less than 400 bytes to the SPL. + * + * There are two variants of the SPI controller in Allwinner SoCs: + * A10/A13/A20 (sun4i variant) and everything else (sun6i variant). + * Both of them are supported. + * + * The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are + * supported at the moment. + */ + +/*****************************************************************************/ +/* SUN4I variant of the SPI controller */ +/*****************************************************************************/ + +#define SUN4I_SPI0_CCTL (0x01C05000 + 0x1C) +#define SUN4I_SPI0_CTL (0x01C05000 + 0x08) +#define SUN4I_SPI0_RX (0x01C05000 + 0x00) +#define SUN4I_SPI0_TX (0x01C05000 + 0x04) +#define SUN4I_SPI0_FIFO_STA (0x01C05000 + 0x28) +#define SUN4I_SPI0_BC (0x01C05000 + 0x20) +#define SUN4I_SPI0_TC (0x01C05000 + 0x24) + +#define SUN4I_CTL_ENABLE BIT(0) +#define SUN4I_CTL_MASTER BIT(1) +#define SUN4I_CTL_TF_RST BIT(8) +#define SUN4I_CTL_RF_RST BIT(9) +#define SUN4I_CTL_XCH BIT(10) + +/*****************************************************************************/ +/* SUN6I variant of the SPI controller */ +/*****************************************************************************/ + +#define SUN6I_SPI0_CCTL (0x01C68000 + 0x24) +#define SUN6I_SPI0_GCR (0x01C68000 + 0x04) +#define SUN6I_SPI0_TCR (0x01C68000 + 0x08) +#define SUN6I_SPI0_FIFO_STA (0x01C68000 + 0x1C) +#define SUN6I_SPI0_MBC (0x01C68000 + 0x30) +#define SUN6I_SPI0_MTC (0x01C68000 + 0x34) +#define SUN6I_SPI0_BCC (0x01C68000 + 0x38) +#define SUN6I_SPI0_TXD (0x01C68000 + 0x200) +#define SUN6I_SPI0_RXD (0x01C68000 + 0x300) + +#define SUN6I_CTL_ENABLE BIT(0) +#define SUN6I_CTL_MASTER BIT(1) +#define SUN6I_CTL_SRST BIT(31) +#define SUN6I_TCR_XCH BIT(31) + +/*****************************************************************************/ + +#define CCM_AHB_GATING0 (0x01C20000 + 0x60) +#define CCM_SPI0_CLK (0x01C20000 + 0xA0) +#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0) + +#define AHB_RESET_SPI0_SHIFT 20 +#define AHB_GATE_OFFSET_SPI0 20 + +#define SPI0_CLK_DIV_BY_2 0x1000 +#define SPI0_CLK_DIV_BY_4 0x1001 + +/*****************************************************************************/ + +/* + * Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting + * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3. + */ +static void spi0_pinmux_setup(unsigned int pin_function) +{ + unsigned int pin; + + for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++) + sunxi_gpio_set_cfgpin(pin, pin_function); + + if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I)) + sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function); + else + sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function); +} + +/* + * Setup 6 MHz from OSC24M (because the BROM is doing the same). + */ +static void spi0_enable_clock(void) +{ + /* Deassert SPI0 reset on SUN6I */ + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) + setbits_le32(SUN6I_BUS_SOFT_RST_REG0, + (1 << AHB_RESET_SPI0_SHIFT)); + + /* Open the SPI0 gate */ + setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); + + /* Divide by 4 */ + writel(SPI0_CLK_DIV_BY_4, IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ? + SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL); + /* 24MHz from OSC24M */ + writel((1 << 31), CCM_SPI0_CLK); + + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) { + /* Enable SPI in the master mode and do a soft reset */ + setbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | + SUN6I_CTL_ENABLE | + SUN6I_CTL_SRST); + /* Wait for completion */ + while (readl(SUN6I_SPI0_GCR) & SUN6I_CTL_SRST) + ; + } else { + /* Enable SPI in the master mode and reset FIFO */ + setbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | + SUN4I_CTL_ENABLE | + SUN4I_CTL_TF_RST | + SUN4I_CTL_RF_RST); + } +} + +static void spi0_disable_clock(void) +{ + /* Disable the SPI0 controller */ + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) + clrbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | + SUN6I_CTL_ENABLE); + else + clrbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | + SUN4I_CTL_ENABLE); + + /* Disable the SPI0 clock */ + writel(0, CCM_SPI0_CLK); + + /* Close the SPI0 gate */ + clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); + + /* Assert SPI0 reset on SUN6I */ + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) + clrbits_le32(SUN6I_BUS_SOFT_RST_REG0, + (1 << AHB_RESET_SPI0_SHIFT)); +} + +static int spi0_init(void) +{ + unsigned int pin_function = SUNXI_GPC_SPI0; + if (IS_ENABLED(CONFIG_MACH_SUN50I)) + pin_function = SUN50I_GPC_SPI0; + + spi0_pinmux_setup(pin_function); + spi0_enable_clock(); +} + +static void spi0_deinit(void) +{ + /* New SoCs can disable pins, older could only set them as input */ + unsigned int pin_function = SUNXI_GPIO_INPUT; + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) + pin_function = SUNXI_GPIO_DISABLE; + + spi0_disable_clock(); + spi0_pinmux_setup(pin_function); +} + +/*****************************************************************************/ + +#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */ + +static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize, + u32 spi_ctl_reg, + u32 spi_ctl_xch_bitmask, + u32 spi_fifo_reg, + u32 spi_tx_reg, + u32 spi_rx_reg, + u32 spi_bc_reg, + u32 spi_tc_reg, + u32 spi_bcc_reg) +{ + writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */ + writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */ + if (spi_bcc_reg) + writel(4, spi_bcc_reg); /* SUN6I also needs this */ + + /* Send the Read Data Bytes (03h) command header */ + writeb(0x03, spi_tx_reg); + writeb((u8)(addr >> 16), spi_tx_reg); + writeb((u8)(addr >> 8), spi_tx_reg); + writeb((u8)(addr), spi_tx_reg); + + /* Start the data transfer */ + setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask); + + /* Wait until everything is received in the RX FIFO */ + while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize) + ; + + /* Skip 4 bytes */ + readl(spi_rx_reg); + + /* Read the data */ + while (bufsize-- > 0) + *buf++ = readb(spi_rx_reg); + + /* tSHSL time is up to 100 ns in various SPI flash datasheets */ + udelay(1); +} + +static void spi0_read_data(void *buf, u32 addr, u32 len) +{ + u8 *buf8 = buf; + u32 chunk_len; + + while (len > 0) { + chunk_len = len; + if (chunk_len > SPI_READ_MAX_SIZE) + chunk_len = SPI_READ_MAX_SIZE; + + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) { + sunxi_spi0_read_data(buf8, addr, chunk_len, + SUN6I_SPI0_TCR, + SUN6I_TCR_XCH, + SUN6I_SPI0_FIFO_STA, + SUN6I_SPI0_TXD, + SUN6I_SPI0_RXD, + SUN6I_SPI0_MBC, + SUN6I_SPI0_MTC, + SUN6I_SPI0_BCC); + } else { + sunxi_spi0_read_data(buf8, addr, chunk_len, + SUN4I_SPI0_CTL, + SUN4I_CTL_XCH, + SUN4I_SPI0_FIFO_STA, + SUN4I_SPI0_TX, + SUN4I_SPI0_RX, + SUN4I_SPI0_BC, + SUN4I_SPI0_TC, + 0); + } + + len -= chunk_len; + buf8 += chunk_len; + addr += chunk_len; + } +} + +/*****************************************************************************/ + +int spl_spi_load_image(void) +{ + int err; + struct image_header *header; + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + + spi0_init(); + + spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40); + err = spl_parse_image_header(header); + if (err) + return err; + + spi0_read_data((void *)spl_image.load_addr, CONFIG_SYS_SPI_U_BOOT_OFFS, + spl_image.size); + + spi0_deinit(); + return 0; +} diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 94275a7..6358901 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -137,6 +137,11 @@ #define CONFIG_SPL_NAND_SUPPORT 1 #endif +#ifdef CONFIG_SPL_SPI_SUNXI +#define CONFIG_SPL_SPI_FLASH_SUPPORT 1 +#define CONFIG_SYS_SPI_U_BOOT_OFFS 0x8000 +#endif + /* mmc config */ #ifdef CONFIG_MMC #define CONFIG_GENERIC_MMC -- cgit v0.10.2 From 320e0570e67efbd093d7750655a758c66e9d5528 Mon Sep 17 00:00:00 2001 From: Bernhard Nortmann Date: Thu, 9 Jun 2016 07:37:35 +0200 Subject: sunxi: FEL - Add the ability to recognize and auto-import uEnv-style data The patch converts one of the "reserved" fields in the sunxi SPL header to a fel_uEnv_length entry. When booting over USB ("FEL mode"), this enables the sunxi-fel utility to pass the string length of uEnv.txt compatible data; at the same time requesting that this data be imported into the U-Boot environment. If parse_spl_header() in the sunxi board.c encounters a non-zero value in this header field, it will therefore call himport_r() to merge the string (lines) passed via FEL into the default settings. Environment vars can be changed this way even before U-Boot will attempt to autoboot - specifically, this also allows overriding "bootcmd". With fel_script_addr set and a zero fel_uEnv_length, U-Boot is safe to assume that data in .scr format (a mkimage-type script) was passed at fel_script_addr, and will handle it using the existing mechanism ("bootcmd_fel"). Signed-off-by: Bernhard Nortmann Acked-by: Siarhei Siamashka Signed-off-by: Hans de Goede diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h index ec73379..5d7ab55 100644 --- a/arch/arm/include/asm/arch-sunxi/spl.h +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -51,7 +51,14 @@ struct boot_file_head { uint8_t spl_signature[4]; }; uint32_t fel_script_address; - uint32_t reserved1[3]; + /* + * If the fel_uEnv_length member below is set to a non-zero value, + * it specifies the size (byte count) of data at fel_script_address. + * At the same time this indicates that the data is in uEnv.txt + * compatible format, ready to be imported via "env import -t". + */ + uint32_t fel_uEnv_length; + uint32_t reserved1[2]; uint32_t boot_media; /* written here by the boot ROM */ uint32_t reserved2[5]; /* padding, align to 64 bytes */ }; diff --git a/board/sunxi/board.c b/board/sunxi/board.c index c8bf316..78dfda5 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -573,6 +573,7 @@ void get_board_serial(struct tag_serialnr *serialnr) #if !defined(CONFIG_SPL_BUILD) #include +#include /* * Check the SPL header for the "sunxi" variant. If found: parse values @@ -582,17 +583,29 @@ void get_board_serial(struct tag_serialnr *serialnr) static void parse_spl_header(const uint32_t spl_addr) { struct boot_file_head *spl = (void *)(ulong)spl_addr; - if (memcmp(spl->spl_signature, SPL_SIGNATURE, 3) == 0) { - uint8_t spl_header_version = spl->spl_signature[3]; - if (spl_header_version == SPL_HEADER_VERSION) { - if (spl->fel_script_address) - setenv_hex("fel_scriptaddr", - spl->fel_script_address); - return; - } + if (memcmp(spl->spl_signature, SPL_SIGNATURE, 3) != 0) + return; /* signature mismatch, no usable header */ + + uint8_t spl_header_version = spl->spl_signature[3]; + if (spl_header_version != SPL_HEADER_VERSION) { printf("sunxi SPL version mismatch: expected %u, got %u\n", SPL_HEADER_VERSION, spl_header_version); + return; + } + if (!spl->fel_script_address) + return; + + if (spl->fel_uEnv_length != 0) { + /* + * data is expected in uEnv.txt compatible format, so "env + * import -t" the string(s) at fel_script_address right away. + */ + himport_r(&env_htab, (char *)spl->fel_script_address, + spl->fel_uEnv_length, '\n', H_NOCLEAR, 0, 0, NULL); + return; } + /* otherwise assume .scr format (mkimage-type script) */ + setenv_hex("fel_scriptaddr", spl->fel_script_address); } #endif -- cgit v0.10.2 From 26c0c15786039fb437925c08205702169462e343 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Fri, 8 Jul 2016 12:40:14 +0200 Subject: sunxi: mmc: increase status register polling rate for data transfers With a recent bunch of SD3.0 cards in our A20-based board we experienced data transfer rates of about 250 KiB/s instead of 10 MiB/s with previous cards from the same vendor (both 4 GB/class 10). By increasing status register polling rate from 1 kHz to 1 MHz we were able to reach the original transfer rates again. With the old cards we now even reach about 16 MiB/s. Signed-off-by: Tobias Doerffel Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index ce2dc4a..3be9a90 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -269,18 +269,18 @@ static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data) unsigned i; unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); unsigned byte_cnt = data->blocksize * data->blocks; - unsigned timeout_msecs = byte_cnt >> 8; - if (timeout_msecs < 2000) - timeout_msecs = 2000; + unsigned timeout_usecs = (byte_cnt >> 8) * 1000; + if (timeout_usecs < 2000000) + timeout_usecs = 2000000; /* Always read / write data through the CPU */ setbits_le32(&mmchost->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB); for (i = 0; i < (byte_cnt >> 2); i++) { while (readl(&mmchost->reg->status) & status_bit) { - if (!timeout_msecs--) + if (!timeout_usecs--) return -1; - udelay(1000); + udelay(1); } if (reading) -- cgit v0.10.2 From a29710c525ff43c3031b879e2beac306a09c6944 Mon Sep 17 00:00:00 2001 From: Amit Singh Tomar Date: Wed, 6 Jul 2016 17:59:44 +0530 Subject: net: Add EMAC driver for H3/A83T/A64 SoCs. This patch add EMAC driver support for H3/A83T/A64 SoCs. Tested on Pine64(A64-External PHY) and Orangepipc(H3-Internal PHY). BIG Thanks to Andre for providing some of the DT code. Signed-off-by: Amit Singh Tomar Acked-by: Hans de Goede Signed-off-by: Hans de Goede diff --git a/arch/arm/dts/sun50i-a64-pine64-plus.dts b/arch/arm/dts/sun50i-a64-pine64-plus.dts index 549dc15..389c609 100644 --- a/arch/arm/dts/sun50i-a64-pine64-plus.dts +++ b/arch/arm/dts/sun50i-a64-pine64-plus.dts @@ -57,3 +57,16 @@ reg = <0x40000000 0x40000000>; }; }; + +&emac { + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>; + phy-mode = "rgmii"; + phy = <&phy1>; + status = "okay"; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; + diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi index 1bd436f..7d0dc76 100644 --- a/arch/arm/dts/sun50i-a64.dtsi +++ b/arch/arm/dts/sun50i-a64.dtsi @@ -506,6 +506,25 @@ allwinner,drive = ; allwinner,pull = ; }; + + rmii_pins: rmii_pins { + allwinner,pins = "PD10", "PD11", "PD13", "PD14", + "PD17", "PD18", "PD19", "PD20", + "PD22", "PD23"; + allwinner,function = "emac"; + allwinner,drive = ; + allwinner,pull = ; + }; + + rgmii_pins: rgmii_pins { + allwinner,pins = "PD8", "PD9", "PD10", "PD11", + "PD12", "PD13", "PD15", + "PD16", "PD17", "PD18", "PD19", + "PD20", "PD21", "PD22", "PD23"; + allwinner,function = "emac"; + allwinner,drive = ; + allwinner,pull = ; + }; }; ahb_rst: reset@1c202c0 { @@ -620,5 +639,19 @@ #address-cells = <1>; #size-cells = <0>; }; + + emac: ethernet@01c30000 { + compatible = "allwinner,sun50i-a64-emac"; + reg = <0x01c30000 0x2000>, <0x01c00030 0x4>; + reg-names = "emac", "syscon"; + interrupts = ; + resets = <&ahb_rst 17>; + reset-names = "ahb"; + clocks = <&bus_gates 17>; + clock-names = "ahb"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; }; }; diff --git a/arch/arm/dts/sun8i-h3-orangepi-2.dts b/arch/arm/dts/sun8i-h3-orangepi-2.dts index f93f5d1..d3f8f55 100644 --- a/arch/arm/dts/sun8i-h3-orangepi-2.dts +++ b/arch/arm/dts/sun8i-h3-orangepi-2.dts @@ -184,3 +184,16 @@ usb1_vbus-supply = <®_usb1_vbus>; status = "okay"; }; + +&emac { + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>; + phy-mode = "rgmii"; + phy = <&phy1>; + status = "okay"; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; + diff --git a/arch/arm/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/dts/sun8i-h3-orangepi-pc.dts index 30ccca0..0a74a91 100644 --- a/arch/arm/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/dts/sun8i-h3-orangepi-pc.dts @@ -173,3 +173,15 @@ /* USB VBUS is always on */ status = "okay"; }; + +&emac { + phy = <&phy1>; + phy-mode = "mii"; + allwinner,use-internal-phy; + allwinner,leds-active-low; + status = "okay"; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi index c2f63c5..6ca5e34 100644 --- a/arch/arm/dts/sun8i-h3.dtsi +++ b/arch/arm/dts/sun8i-h3.dtsi @@ -501,6 +501,17 @@ interrupt-controller; #interrupt-cells = <3>; + rgmii_pins: rgmii_pins { + allwinner,pins = "PD0", "PD1", "PD2", "PD3", + "PD4", "PD5", "PD7", + "PD8", "PD9", "PD10", + "PD12", "PD13", "PD15", + "PD16", "PD17"; + allwinner,function = "emac"; + allwinner,drive = ; + allwinner,pull = ; + }; + uart0_pins_a: uart0@0 { allwinner,pins = "PA4", "PA5"; allwinner,function = "uart0"; @@ -616,6 +627,20 @@ status = "disabled"; }; + emac: ethernet@01c30000 { + compatible = "allwinner,sun8i-h3-emac"; + reg = <0x01c30000 0x2000>, <0x01c00030 0x4>; + reg-names = "emac", "syscon"; + interrupts = ; + resets = <&ahb_rst 17>, <&ahb_rst 66>; + reset-names = "ahb", "ephy"; + clocks = <&bus_gates 17>, <&bus_gates 128>; + clock-names = "ahb", "ephy"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + gic: interrupt-controller@01c81000 { compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; reg = <0x01c81000 0x1000>, diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index c2e72f5..d4dff1e 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -40,7 +40,8 @@ struct sunxi_ccm_reg { u32 ahb_gate1; /* 0x64 ahb module clock gating 1 */ u32 apb1_gate; /* 0x68 apb1 module clock gating */ u32 apb2_gate; /* 0x6c apb2 module clock gating */ - u32 reserved9[4]; + u32 bus_gate4; /* 0x70 gate 4 module clock gating */ + u8 res3[0xc]; u32 nand0_clk_cfg; /* 0x80 nand0 clock control */ u32 nand1_clk_cfg; /* 0x84 nand1 clock control */ u32 sd0_clk_cfg; /* 0x88 sd0 clock control */ @@ -387,6 +388,7 @@ struct sunxi_ccm_reg { #define AHB_RESET_OFFSET_LCD0 4 /* ahb_reset2 offsets */ +#define AHB_RESET_OFFSET_EPHY 2 #define AHB_RESET_OFFSET_LVDS 0 /* apb2 reset */ diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h index c5e9d88..cd009d7 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h @@ -87,7 +87,8 @@ #define SUNXI_KEYPAD_BASE 0x01c23000 #define SUNXI_TZPC_BASE 0x01c23400 -#if defined(CONFIG_MACH_SUN8I_A83T) || defined(CONFIG_MACH_SUN8I_H3) +#if defined(CONFIG_MACH_SUN8I_A83T) || defined(CONFIG_MACH_SUN8I_H3) || \ +defined(CONFIG_MACH_SUN50I) /* SID address space starts at 0x01c1400, but e-fuse is at offset 0x200 */ #define SUNXI_SID_BASE 0x01c14200 #else diff --git a/configs/orangepi_pc_defconfig b/configs/orangepi_pc_defconfig index 7eaa795..294f7ab 100644 --- a/configs/orangepi_pc_defconfig +++ b/configs/orangepi_pc_defconfig @@ -13,3 +13,4 @@ CONFIG_SPL=y # CONFIG_CMD_FPGA is not set CONFIG_SY8106A_POWER=y CONFIG_USB_EHCI_HCD=y +CONFIG_SUN8I_EMAC=y diff --git a/configs/pine64_plus_defconfig b/configs/pine64_plus_defconfig index 0bf79bf..5c97de1 100644 --- a/configs/pine64_plus_defconfig +++ b/configs/pine64_plus_defconfig @@ -10,3 +10,4 @@ CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-pine64-plus" # CONFIG_CMD_FLASH is not set # CONFIG_CMD_FPGA is not set CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK=y +CONFIG_SUN8I_EMAC=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c1cb689..88d8e83 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -152,6 +152,15 @@ config RTL8169 This driver supports Realtek 8169 series gigabit ethernet family of PCI/PCIe chipsets/adapters. +config SUN8I_EMAC + bool "Allwinner Sun8i Ethernet MAC support" + depends on DM_ETH + select PHYLIB + help + This driver supports the Allwinner based SUN8I/SUN50I Ethernet MAC. + It can be found in H3/A64/A83T based SoCs and compatible with both + External and Internal PHY's. + config XILINX_AXIEMAC depends on DM_ETH && (MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP) select PHYLIB diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5702592..a448526 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_E1000) += e1000.o obj-$(CONFIG_E1000_SPI) += e1000_spi.o obj-$(CONFIG_EEPRO100) += eepro100.o obj-$(CONFIG_SUNXI_EMAC) += sunxi_emac.o +obj-$(CONFIG_SUN8I_EMAC) += sun8i_emac.o obj-$(CONFIG_ENC28J60) += enc28j60.o obj-$(CONFIG_EP93XX) += ep93xx_eth.o obj-$(CONFIG_ETHOC) += ethoc.o diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c new file mode 100644 index 0000000..4bed50d --- /dev/null +++ b/drivers/net/sun8i_emac.c @@ -0,0 +1,789 @@ +/* + * (C) Copyright 2016 + * Author: Amit Singh Tomar, amittomer25@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ethernet driver for H3/A64/A83T based SoC's + * + * It is derived from the work done by + * LABBE Corentin & Chen-Yu Tsai for Linux, THANKS! + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCTL_EMAC_TX_CLK_SRC_MII BIT(0) +#define SCTL_EMAC_EPIT_MII BIT(2) +#define SCTL_EMAC_CLK_SEL BIT(18) /* 25 Mhz */ + +#define MDIO_CMD_MII_BUSY BIT(0) +#define MDIO_CMD_MII_WRITE BIT(1) + +#define MDIO_CMD_MII_PHY_REG_ADDR_MASK 0x000001f0 +#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 +#define MDIO_CMD_MII_PHY_ADDR_MASK 0x0001f000 +#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 + +#define CONFIG_TX_DESCR_NUM 32 +#define CONFIG_RX_DESCR_NUM 32 +#define CONFIG_ETH_BUFSIZE 2024 + +#define TX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_TX_DESCR_NUM) +#define RX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_RX_DESCR_NUM) + +#define H3_EPHY_DEFAULT_VALUE 0x58000 +#define H3_EPHY_DEFAULT_MASK GENMASK(31, 15) +#define H3_EPHY_ADDR_SHIFT 20 +#define REG_PHY_ADDR_MASK GENMASK(4, 0) +#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ +#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ +#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ + +#define SC_RMII_EN BIT(13) +#define SC_EPIT BIT(2) /* 1: RGMII, 0: MII */ +#define SC_ETCS_MASK GENMASK(1, 0) +#define SC_ETCS_EXT_GMII 0x1 +#define SC_ETCS_INT_GMII 0x2 + +#define CONFIG_MDIO_TIMEOUT (3 * CONFIG_SYS_HZ) + +#define AHB_GATE_OFFSET_EPHY 0 + +#if defined(CONFIG_MACH_SUN8I_H3) +#define SUN8I_GPD8_GMAC 2 +#else +#define SUN8I_GPD8_GMAC 4 +#endif + +/* H3/A64 EMAC Register's offset */ +#define EMAC_CTL0 0x00 +#define EMAC_CTL1 0x04 +#define EMAC_INT_STA 0x08 +#define EMAC_INT_EN 0x0c +#define EMAC_TX_CTL0 0x10 +#define EMAC_TX_CTL1 0x14 +#define EMAC_TX_FLOW_CTL 0x1c +#define EMAC_TX_DMA_DESC 0x20 +#define EMAC_RX_CTL0 0x24 +#define EMAC_RX_CTL1 0x28 +#define EMAC_RX_DMA_DESC 0x34 +#define EMAC_MII_CMD 0x48 +#define EMAC_MII_DATA 0x4c +#define EMAC_ADDR0_HIGH 0x50 +#define EMAC_ADDR0_LOW 0x54 +#define EMAC_TX_DMA_STA 0xb0 +#define EMAC_TX_CUR_DESC 0xb4 +#define EMAC_TX_CUR_BUF 0xb8 +#define EMAC_RX_DMA_STA 0xc0 +#define EMAC_RX_CUR_DESC 0xc4 + +DECLARE_GLOBAL_DATA_PTR; + +enum emac_variant { + A83T_EMAC = 1, + H3_EMAC, + A64_EMAC, +}; + +struct emac_dma_desc { + u32 status; + u32 st; + u32 buf_addr; + u32 next; +} __aligned(ARCH_DMA_MINALIGN); + +struct emac_eth_dev { + struct emac_dma_desc rx_chain[CONFIG_TX_DESCR_NUM]; + struct emac_dma_desc tx_chain[CONFIG_RX_DESCR_NUM]; + char rxbuffer[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN); + char txbuffer[TX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN); + + u32 interface; + u32 phyaddr; + u32 link; + u32 speed; + u32 duplex; + u32 phy_configured; + u32 tx_currdescnum; + u32 rx_currdescnum; + u32 addr; + u32 tx_slot; + bool use_internal_phy; + + enum emac_variant variant; + void *mac_reg; + phys_addr_t sysctl_reg; + struct phy_device *phydev; + struct mii_dev *bus; +}; + +static int sun8i_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct emac_eth_dev *priv = bus->priv; + ulong start; + u32 miiaddr = 0; + int timeout = CONFIG_MDIO_TIMEOUT; + + miiaddr &= ~MDIO_CMD_MII_WRITE; + miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; + miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_REG_ADDR_MASK; + + miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; + + miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_ADDR_MASK; + + miiaddr |= MDIO_CMD_MII_BUSY; + + writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(priv->mac_reg + EMAC_MII_CMD) & MDIO_CMD_MII_BUSY)) + return readl(priv->mac_reg + EMAC_MII_DATA); + udelay(10); + }; + + return -1; +} + +static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct emac_eth_dev *priv = bus->priv; + ulong start; + u32 miiaddr = 0; + int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; + + miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; + miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_REG_ADDR_MASK; + + miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; + miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_ADDR_MASK; + + miiaddr |= MDIO_CMD_MII_WRITE; + miiaddr |= MDIO_CMD_MII_BUSY; + + writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + writel(val, priv->mac_reg + EMAC_MII_DATA); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(priv->mac_reg + EMAC_MII_CMD) & + MDIO_CMD_MII_BUSY)) { + ret = 0; + break; + } + udelay(10); + }; + + return ret; +} + +static int _sun8i_write_hwaddr(struct emac_eth_dev *priv, u8 *mac_id) +{ + u32 macid_lo, macid_hi; + + macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + + (mac_id[3] << 24); + macid_hi = mac_id[4] + (mac_id[5] << 8); + + writel(macid_hi, priv->mac_reg + EMAC_ADDR0_HIGH); + writel(macid_lo, priv->mac_reg + EMAC_ADDR0_LOW); + + return 0; +} + +static void sun8i_adjust_link(struct emac_eth_dev *priv, + struct phy_device *phydev) +{ + u32 v; + + v = readl(priv->mac_reg + EMAC_CTL0); + + if (phydev->duplex) + v |= BIT(0); + else + v &= ~BIT(0); + + v &= ~0x0C; + + switch (phydev->speed) { + case 1000: + break; + case 100: + v |= BIT(2); + v |= BIT(3); + break; + case 10: + v |= BIT(3); + break; + } + writel(v, priv->mac_reg + EMAC_CTL0); +} + +static int sun8i_emac_set_syscon_ephy(struct emac_eth_dev *priv, u32 *reg) +{ + if (priv->use_internal_phy) { + /* H3 based SoC's that has an Internal 100MBit PHY + * needs to be configured and powered up before use + */ + *reg &= ~H3_EPHY_DEFAULT_MASK; + *reg |= H3_EPHY_DEFAULT_VALUE; + *reg |= priv->phyaddr << H3_EPHY_ADDR_SHIFT; + *reg &= ~H3_EPHY_SHUTDOWN; + *reg |= H3_EPHY_SELECT; + } else + /* This is to select External Gigabit PHY on + * the boards with H3 SoC. + */ + *reg &= ~H3_EPHY_SELECT; + + return 0; +} + +static int sun8i_emac_set_syscon(struct emac_eth_dev *priv) +{ + int ret; + u32 reg; + + reg = readl(priv->sysctl_reg); + + if (priv->variant == H3_EMAC) { + ret = sun8i_emac_set_syscon_ephy(priv, ®); + if (ret) + return ret; + } + + reg &= ~(SC_ETCS_MASK | SC_EPIT); + if (priv->variant == H3_EMAC || priv->variant == A64_EMAC) + reg &= ~SC_RMII_EN; + + switch (priv->interface) { + case PHY_INTERFACE_MODE_MII: + /* default */ + break; + case PHY_INTERFACE_MODE_RGMII: + reg |= SC_EPIT | SC_ETCS_INT_GMII; + break; + case PHY_INTERFACE_MODE_RMII: + if (priv->variant == H3_EMAC || + priv->variant == A64_EMAC) { + reg |= SC_RMII_EN | SC_ETCS_EXT_GMII; + break; + } + /* RMII not supported on A83T */ + default: + debug("%s: Invalid PHY interface\n", __func__); + return -EINVAL; + } + + writel(reg, priv->sysctl_reg); + + return 0; +} + +static int sun8i_phy_init(struct emac_eth_dev *priv, void *dev) +{ + struct phy_device *phydev; + + phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); + if (!phydev) + return -ENODEV; + + phy_connect_dev(phydev, dev); + + priv->phydev = phydev; + phy_config(priv->phydev); + + return 0; +} + +static void rx_descs_init(struct emac_eth_dev *priv) +{ + struct emac_dma_desc *desc_table_p = &priv->rx_chain[0]; + char *rxbuffs = &priv->rxbuffer[0]; + struct emac_dma_desc *desc_p; + u32 idx; + + /* flush Rx buffers */ + flush_dcache_range((uintptr_t)rxbuffs, (ulong)rxbuffs + + RX_TOTAL_BUFSIZE); + + for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] + ; + desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; + desc_p->st |= CONFIG_ETH_BUFSIZE; + desc_p->status = BIT(31); + } + + /* Correcting the last pointer of the chain */ + desc_p->next = (uintptr_t)&desc_table_p[0]; + + flush_dcache_range((uintptr_t)priv->rx_chain, + (uintptr_t)priv->rx_chain + + sizeof(priv->rx_chain)); + + writel((uintptr_t)&desc_table_p[0], (priv->mac_reg + EMAC_RX_DMA_DESC)); + priv->rx_currdescnum = 0; +} + +static void tx_descs_init(struct emac_eth_dev *priv) +{ + struct emac_dma_desc *desc_table_p = &priv->tx_chain[0]; + char *txbuffs = &priv->txbuffer[0]; + struct emac_dma_desc *desc_p; + u32 idx; + + for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] + ; + desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; + desc_p->status = (1 << 31); + desc_p->st = 0; + } + + /* Correcting the last pointer of the chain */ + desc_p->next = (uintptr_t)&desc_table_p[0]; + + /* Flush all Tx buffer descriptors */ + flush_dcache_range((uintptr_t)priv->tx_chain, + (uintptr_t)priv->tx_chain + + sizeof(priv->tx_chain)); + + writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC); + priv->tx_currdescnum = 0; +} + +static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) +{ + u32 reg, v; + int timeout = 100; + + reg = readl((priv->mac_reg + EMAC_CTL1)); + + if (!(reg & 0x1)) { + /* Soft reset MAC */ + setbits_le32((priv->mac_reg + EMAC_CTL1), 0x1); + do { + reg = readl(priv->mac_reg + EMAC_CTL1); + } while ((reg & 0x01) != 0 && (--timeout)); + if (!timeout) { + printf("%s: Timeout\n", __func__); + return -1; + } + } + + /* Rewrite mac address after reset */ + _sun8i_write_hwaddr(priv, enetaddr); + + v = readl(priv->mac_reg + EMAC_TX_CTL1); + /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ + v |= BIT(1); + writel(v, priv->mac_reg + EMAC_TX_CTL1); + + v = readl(priv->mac_reg + EMAC_RX_CTL1); + /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a + * complete frame has been written to RX DMA FIFO + */ + v |= BIT(1); + writel(v, priv->mac_reg + EMAC_RX_CTL1); + + /* DMA */ + writel(8 << 24, priv->mac_reg + EMAC_CTL1); + + /* Initialize rx/tx descriptors */ + rx_descs_init(priv); + tx_descs_init(priv); + + /* PHY Start Up */ + genphy_parse_link(priv->phydev); + + sun8i_adjust_link(priv, priv->phydev); + + /* Start RX DMA */ + v = readl(priv->mac_reg + EMAC_RX_CTL1); + v |= BIT(30); + writel(v, priv->mac_reg + EMAC_RX_CTL1); + /* Start TX DMA */ + v = readl(priv->mac_reg + EMAC_TX_CTL1); + v |= BIT(30); + writel(v, priv->mac_reg + EMAC_TX_CTL1); + + /* Enable RX/TX */ + setbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); + setbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + + return 0; +} + +static int parse_phy_pins(struct udevice *dev) +{ + int offset; + const char *pin_name; + int drive, pull, i; + + offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset, + "pinctrl-0"); + if (offset < 0) { + printf("WARNING: emac: cannot find pinctrl-0 node\n"); + return offset; + } + + drive = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0, + "allwinner,drive", 4); + pull = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0, + "allwinner,pull", 0); + for (i = 0; ; i++) { + int pin; + + if (fdt_get_string_index(gd->fdt_blob, offset, + "allwinner,pins", i, &pin_name)) + break; + if (pin_name[0] != 'P') + continue; + pin = (pin_name[1] - 'A') << 5; + if (pin >= 26 << 5) + continue; + pin += simple_strtol(&pin_name[2], NULL, 10); + + sunxi_gpio_set_cfgpin(pin, SUN8I_GPD8_GMAC); + sunxi_gpio_set_drv(pin, drive); + sunxi_gpio_set_pull(pin, pull); + } + + if (!i) { + printf("WARNING: emac: cannot find allwinner,pins property\n"); + return -2; + } + + return 0; +} + +static int _sun8i_eth_recv(struct emac_eth_dev *priv, uchar **packetp) +{ + u32 status, desc_num = priv->rx_currdescnum; + struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; + int length = -EAGAIN; + int good_packet = 1; + uintptr_t desc_start = (uintptr_t)desc_p; + uintptr_t desc_end = desc_start + + roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); + + ulong data_start = (uintptr_t)desc_p->buf_addr; + ulong data_end; + + /* Invalidate entire buffer descriptor */ + invalidate_dcache_range(desc_start, desc_end); + + status = desc_p->status; + + /* Check for DMA own bit */ + if (!(status & BIT(31))) { + length = (desc_p->status >> 16) & 0x3FFF; + + if (length < 0x40) { + good_packet = 0; + debug("RX: Bad Packet (runt)\n"); + } + + data_end = data_start + length; + /* Invalidate received data */ + invalidate_dcache_range(rounddown(data_start, + ARCH_DMA_MINALIGN), + roundup(data_end, + ARCH_DMA_MINALIGN)); + if (good_packet) { + if (length > CONFIG_ETH_BUFSIZE) { + printf("Received packet is too big (len=%d)\n", + length); + return -EMSGSIZE; + } + *packetp = (uchar *)(ulong)desc_p->buf_addr; + return length; + } + } + + return length; +} + +static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, + int len) +{ + u32 v, desc_num = priv->tx_currdescnum; + struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num]; + uintptr_t desc_start = (uintptr_t)desc_p; + uintptr_t desc_end = desc_start + + roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); + + uintptr_t data_start = (uintptr_t)desc_p->buf_addr; + uintptr_t data_end = data_start + + roundup(len, ARCH_DMA_MINALIGN); + + /* Invalidate entire buffer descriptor */ + invalidate_dcache_range(desc_start, desc_end); + + desc_p->st = len; + /* Mandatory undocumented bit */ + desc_p->st |= BIT(24); + + memcpy((void *)data_start, packet, len); + + /* Flush data to be sent */ + flush_dcache_range(data_start, data_end); + + /* frame end */ + desc_p->st |= BIT(30); + desc_p->st |= BIT(31); + + /*frame begin */ + desc_p->st |= BIT(29); + desc_p->status = BIT(31); + + /*Descriptors st and status field has changed, so FLUSH it */ + flush_dcache_range(desc_start, desc_end); + + /* Move to next Descriptor and wrap around */ + if (++desc_num >= CONFIG_TX_DESCR_NUM) + desc_num = 0; + priv->tx_currdescnum = desc_num; + + /* Start the DMA */ + v = readl(priv->mac_reg + EMAC_TX_CTL1); + v |= BIT(31);/* mandatory */ + v |= BIT(30);/* mandatory */ + writel(v, priv->mac_reg + EMAC_TX_CTL1); + + return 0; +} + +static int sun8i_eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct emac_eth_dev *priv = dev_get_priv(dev); + + return _sun8i_write_hwaddr(priv, pdata->enetaddr); +} + +static void sun8i_emac_board_setup(struct emac_eth_dev *priv) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (priv->use_internal_phy) { + /* Set clock gating for ephy */ + setbits_le32(&ccm->bus_gate4, BIT(AHB_GATE_OFFSET_EPHY)); + + /* Set Tx clock source as MII with rate 25 MZ */ + setbits_le32(priv->sysctl_reg, SCTL_EMAC_TX_CLK_SRC_MII | + SCTL_EMAC_EPIT_MII | SCTL_EMAC_CLK_SEL); + /* Deassert EPHY */ + setbits_le32(&ccm->ahb_reset2_cfg, BIT(AHB_RESET_OFFSET_EPHY)); + } + + /* Set clock gating for emac */ + setbits_le32(&ccm->ahb_gate0, BIT(AHB_GATE_OFFSET_GMAC)); + + /* Set EMAC clock */ + setbits_le32(&ccm->axi_gate, (BIT(1) | BIT(0))); + + /* De-assert EMAC */ + setbits_le32(&ccm->ahb_reset0_cfg, BIT(AHB_RESET_OFFSET_GMAC)); +} + +static int sun8i_mdio_init(const char *name, struct emac_eth_dev *priv) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + debug("Failed to allocate MDIO bus\n"); + return -ENOMEM; + } + + bus->read = sun8i_mdio_read; + bus->write = sun8i_mdio_write; + snprintf(bus->name, sizeof(bus->name), name); + bus->priv = (void *)priv; + + return mdio_register(bus); +} + +static int sun8i_emac_eth_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + + return _sun8i_emac_eth_init(dev->priv, pdata->enetaddr); +} + +static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) +{ + struct emac_eth_dev *priv = dev_get_priv(dev); + + return _sun8i_emac_eth_send(priv, packet, length); +} + +static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct emac_eth_dev *priv = dev_get_priv(dev); + + return _sun8i_eth_recv(priv, packetp); +} + +static int _sun8i_free_pkt(struct emac_eth_dev *priv) +{ + u32 desc_num = priv->rx_currdescnum; + struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; + uintptr_t desc_start = (uintptr_t)desc_p; + uintptr_t desc_end = desc_start + + roundup(sizeof(u32), ARCH_DMA_MINALIGN); + + /* Make the current descriptor valid again */ + desc_p->status |= BIT(31); + + /* Flush Status field of descriptor */ + flush_dcache_range(desc_start, desc_end); + + /* Move to next desc and wrap-around condition. */ + if (++desc_num >= CONFIG_RX_DESCR_NUM) + desc_num = 0; + priv->rx_currdescnum = desc_num; + + return 0; +} + +static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, + int length) +{ + struct emac_eth_dev *priv = dev_get_priv(dev); + + return _sun8i_free_pkt(priv); +} + +static void sun8i_emac_eth_stop(struct udevice *dev) +{ + struct emac_eth_dev *priv = dev_get_priv(dev); + + /* Stop Rx/Tx transmitter */ + clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); + clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + + /* Stop TX DMA */ + clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, BIT(30)); + + phy_shutdown(priv->phydev); +} + +static int sun8i_emac_eth_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct emac_eth_dev *priv = dev_get_priv(dev); + + priv->mac_reg = (void *)pdata->iobase; + + sun8i_emac_board_setup(priv); + + sun8i_mdio_init(dev->name, priv); + priv->bus = miiphy_get_dev_by_name(dev->name); + + sun8i_emac_set_syscon(priv); + + return sun8i_phy_init(priv, dev); +} + +static const struct eth_ops sun8i_emac_eth_ops = { + .start = sun8i_emac_eth_start, + .write_hwaddr = sun8i_eth_write_hwaddr, + .send = sun8i_emac_eth_send, + .recv = sun8i_emac_eth_recv, + .free_pkt = sun8i_eth_free_pkt, + .stop = sun8i_emac_eth_stop, +}; + +static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct emac_eth_dev *priv = dev_get_priv(dev); + const char *phy_mode; + int offset = 0; + + pdata->iobase = dev_get_addr_name(dev, "emac"); + priv->sysctl_reg = dev_get_addr_name(dev, "syscon"); + + pdata->phy_interface = -1; + priv->phyaddr = -1; + priv->use_internal_phy = false; + + offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset, + "phy"); + if (offset > 0) + priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", + -1); + + phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL); + + if (phy_mode) + pdata->phy_interface = phy_get_interface_by_name(phy_mode); + printf("phy interface%d\n", pdata->phy_interface); + + if (pdata->phy_interface == -1) { + debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); + return -EINVAL; + } + + priv->variant = dev_get_driver_data(dev); + + if (!priv->variant) { + printf("%s: Missing variant '%s'\n", __func__, + (char *)priv->variant); + return -EINVAL; + } + + if (priv->variant == H3_EMAC) { + if (fdt_getprop(gd->fdt_blob, dev->of_offset, + "allwinner,use-internal-phy", NULL)) + priv->use_internal_phy = true; + } + + priv->interface = pdata->phy_interface; + + if (!priv->use_internal_phy) + parse_phy_pins(dev); + + return 0; +} + +static const struct udevice_id sun8i_emac_eth_ids[] = { + {.compatible = "allwinner,sun8i-h3-emac", .data = (uintptr_t)H3_EMAC }, + {.compatible = "allwinner,sun50i-a64-emac", + .data = (uintptr_t)A64_EMAC }, + {.compatible = "allwinner,sun8i-a83t-emac", + .data = (uintptr_t)A83T_EMAC }, + { } +}; + +U_BOOT_DRIVER(eth_sun8i_emac) = { + .name = "eth_sun8i_emac", + .id = UCLASS_ETH, + .of_match = sun8i_emac_eth_ids, + .ofdata_to_platdata = sun8i_emac_eth_ofdata_to_platdata, + .probe = sun8i_emac_eth_probe, + .ops = &sun8i_emac_eth_ops, + .priv_auto_alloc_size = sizeof(struct emac_eth_dev), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; -- cgit v0.10.2 From f221961e963fcfa5ceab6455c24d6532877ca923 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 26 Jun 2016 13:34:42 +0200 Subject: sunxi: Add support for multiple ethadrr-esses Currently we fill ethaddr with a fixed unique address based on the SoCs serial (from the sid) to make sure that boards which use the integrated emac / gmac get a fixed mac rather then a random one. On some boards the wifi does not come with a fixed mac either, so we need to also set eth1addr. This commit changes the ethaddr setting code to check for ethernet%d aliases (as fdt_fixup_ethernet does) and set an ethaddr variable for all present aliases. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 78dfda5..5e48756 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -26,6 +26,7 @@ #endif #include #include +#include #include #include #include @@ -609,36 +610,42 @@ static void parse_spl_header(const uint32_t spl_addr) } #endif -#ifdef CONFIG_MISC_INIT_R -int misc_init_r(void) +/* + * Note this function gets called multiple times. + * It must not make any changes to env variables which already exist. + */ +static void setup_environment(const void *fdt) { char serial_string[17] = { 0 }; unsigned int sid[4]; uint8_t mac_addr[6]; - int ret; - -#if !defined(CONFIG_SPL_BUILD) - setenv("fel_booted", NULL); - setenv("fel_scriptaddr", NULL); - /* determine if we are running in FEL mode */ - if (!is_boot0_magic(SPL_ADDR + 4)) { /* eGON.BT0 */ - setenv("fel_booted", "1"); - parse_spl_header(SPL_ADDR); - } -#endif + char ethaddr[16]; + int i, ret; ret = sunxi_get_sid(sid); if (ret == 0 && sid[0] != 0 && sid[3] != 0) { - if (!getenv("ethaddr")) { + for (i = 0; i < 4; i++) { + sprintf(ethaddr, "ethernet%d", i); + if (!fdt_get_alias(fdt, ethaddr)) + continue; + + if (i == 0) + strcpy(ethaddr, "ethaddr"); + else + sprintf(ethaddr, "eth%daddr", i); + + if (getenv(ethaddr)) + continue; + /* Non OUI / registered MAC address */ - mac_addr[0] = 0x02; + mac_addr[0] = (i << 4) | 0x02; mac_addr[1] = (sid[0] >> 0) & 0xff; mac_addr[2] = (sid[3] >> 24) & 0xff; mac_addr[3] = (sid[3] >> 16) & 0xff; mac_addr[4] = (sid[3] >> 8) & 0xff; mac_addr[5] = (sid[3] >> 0) & 0xff; - eth_setenv_enetaddr("ethaddr", mac_addr); + eth_setenv_enetaddr(ethaddr, mac_addr); } if (!getenv("serial#")) { @@ -648,6 +655,24 @@ int misc_init_r(void) setenv("serial#", serial_string); } } +} + +#ifdef CONFIG_MISC_INIT_R +int misc_init_r(void) +{ + __maybe_unused int ret; + +#if !defined(CONFIG_SPL_BUILD) + setenv("fel_booted", NULL); + setenv("fel_scriptaddr", NULL); + /* determine if we are running in FEL mode */ + if (!is_boot0_magic(SPL_ADDR + 4)) { /* eGON.BT0 */ + setenv("fel_booted", "1"); + parse_spl_header(SPL_ADDR); + } +#endif + + setup_environment(gd->fdt_blob); #ifndef CONFIG_MACH_SUN9I ret = sunxi_usb_phy_probe(); @@ -664,6 +689,12 @@ int ft_board_setup(void *blob, bd_t *bd) { int __maybe_unused r; + /* + * Call setup_environment again in case the boot fdt has + * ethernet aliases the u-boot copy does not have. + */ + setup_environment(blob); + #ifdef CONFIG_VIDEO_DT_SIMPLEFB r = sunxi_simplefb_setup(blob); if (r) -- cgit v0.10.2 From 4a8c7c1f45a25687e08ff98ab27ba664c8fd7b74 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jul 2016 09:56:56 +0200 Subject: sunxi: Remove some unnecessary #ifdefs We always define CONFIG_MISC_INIT_R on sunxi and misc_init_r is never called in the spl, so the linker will optimize it and parse_spl_header(), of which it is the only caller, away. On the tests I've done (Orange Pi PC build) the SPL actually becomes 8 bytes smaller with this patch. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 5e48756..320958a 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -20,12 +20,14 @@ #include #include #include +#include #include #ifndef CONFIG_ARM64 #include #endif #include #include +#include #include #include #include @@ -572,10 +574,6 @@ void get_board_serial(struct tag_serialnr *serialnr) } #endif -#if !defined(CONFIG_SPL_BUILD) -#include -#include - /* * Check the SPL header for the "sunxi" variant. If found: parse values * that might have been passed by the loader ("fel" utility), and update @@ -608,7 +606,6 @@ static void parse_spl_header(const uint32_t spl_addr) /* otherwise assume .scr format (mkimage-type script) */ setenv_hex("fel_scriptaddr", spl->fel_script_address); } -#endif /* * Note this function gets called multiple times. @@ -657,12 +654,10 @@ static void setup_environment(const void *fdt) } } -#ifdef CONFIG_MISC_INIT_R int misc_init_r(void) { __maybe_unused int ret; -#if !defined(CONFIG_SPL_BUILD) setenv("fel_booted", NULL); setenv("fel_scriptaddr", NULL); /* determine if we are running in FEL mode */ @@ -670,7 +665,6 @@ int misc_init_r(void) setenv("fel_booted", "1"); parse_spl_header(SPL_ADDR); } -#endif setup_environment(gd->fdt_blob); @@ -683,7 +677,6 @@ int misc_init_r(void) return 0; } -#endif int ft_board_setup(void *blob, bd_t *bd) { -- cgit v0.10.2 From ef36d9ae1646e5aee7c1425ee507d275699a072e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jul 2016 15:31:47 +0200 Subject: sunxi: Use BROM stored boot_media value to determine our boot-source Now that we know that the BROM stores a value indicating the boot-source at the beginning of SRAM, use that instead of trying to recreate the BROM's boot probing. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/arch/arm/include/asm/arch-sunxi/mmc.h b/arch/arm/include/asm/arch-sunxi/mmc.h index 3da360b..cb52e64 100644 --- a/arch/arm/include/asm/arch-sunxi/mmc.h +++ b/arch/arm/include/asm/arch-sunxi/mmc.h @@ -127,5 +127,4 @@ struct sunxi_mmc { #define SUNXI_MMC_COMMON_RESET (1 << 18) struct mmc *sunxi_mmc_init(int sdc_no); -int sunxi_mmc_has_egon_boot_signature(struct mmc *mmc); #endif /* _SUNXI_MMC_H */ diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 3f5116b..06a1986 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -203,7 +203,8 @@ DECLARE_GLOBAL_DATA_PTR; */ u32 spl_boot_device(void) { - __maybe_unused struct mmc *mmc0, *mmc1; + int boot_source; + /* * When booting from the SD card or NAND memory, the "eGON.BT0" * signature is expected to be found in memory at the address 0x0004 @@ -223,32 +224,19 @@ u32 spl_boot_device(void) if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */ return BOOT_DEVICE_BOARD; -#ifdef CONFIG_SPL_SPI_SUNXI - if (readb(SPL_ADDR + 0x28) == SUNXI_BOOTED_FROM_SPI) - return BOOT_DEVICE_SPI; -#endif - - /* The BROM will try to boot from mmc0 first, so try that first. */ -#ifdef CONFIG_MMC - mmc_initialize(gd->bd); - mmc0 = find_mmc_device(0); - if (sunxi_mmc_has_egon_boot_signature(mmc0)) + boot_source = readb(SPL_ADDR + 0x28); + switch (boot_source) { + case SUNXI_BOOTED_FROM_MMC0: return BOOT_DEVICE_MMC1; -#endif - - /* Fallback to booting NAND if enabled. */ - if (IS_ENABLED(CONFIG_SPL_NAND_SUPPORT)) + case SUNXI_BOOTED_FROM_NAND: return BOOT_DEVICE_NAND; - -#ifdef CONFIG_MMC - if (CONFIG_MMC_SUNXI_SLOT_EXTRA == 2) { - mmc1 = find_mmc_device(1); - if (sunxi_mmc_has_egon_boot_signature(mmc1)) - return BOOT_DEVICE_MMC2; + case SUNXI_BOOTED_FROM_MMC2: + return BOOT_DEVICE_MMC2; + case SUNXI_BOOTED_FROM_SPI: + return BOOT_DEVICE_SPI; } -#endif - panic("Could not determine boot source\n"); + panic("Unknown boot source %d\n", boot_source); return -1; /* Never reached */ } diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 320958a..f6e28b0 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -369,8 +369,7 @@ int board_mmc_init(bd_t *bis) * are searched there first. Note we only do this for u-boot proper, * not for the SPL, see spl_boot_device(). */ - if (!sunxi_mmc_has_egon_boot_signature(mmc0) && - sunxi_mmc_has_egon_boot_signature(mmc1)) { + if (readb(SPL_ADDR + 0x28) == SUNXI_BOOTED_FROM_MMC2) { /* Booting from emmc / mmc2, swap */ mmc0->block_dev.devnum = 1; mmc1->block_dev.devnum = 0; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 3be9a90..5d8abdc 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -445,23 +445,6 @@ static int sunxi_mmc_getcd(struct mmc *mmc) return !gpio_get_value(cd_pin); } -int sunxi_mmc_has_egon_boot_signature(struct mmc *mmc) -{ - char *buf = malloc(512); - int valid_signature = 0; - - if (buf == NULL) - panic("Failed to allocate memory\n"); - - if (mmc_getcd(mmc) && mmc_init(mmc) == 0 && - mmc->block_dev.block_read(&mmc->block_dev, 16, 1, buf) == 1 && - strncmp(&buf[4], "eGON.BT0", 8) == 0) - valid_signature = 1; - - free(buf); - return valid_signature; -} - static const struct mmc_ops sunxi_mmc_ops = { .send_cmd = sunxi_mmc_send_cmd, .set_ios = sunxi_mmc_set_ios, -- cgit v0.10.2 From 74fcc927f65d782e4bab769a85acaa553acfe389 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jul 2016 15:15:12 +0200 Subject: sunxi: orangepi_pc: Add support for eMMC found on the Orange Pi PC Plus The Plus variant of the Orange Pi PC has an eMMC, add support for this. Note we are using the same u-boot defconfig / dts for both the regular Orange Pi PC as well as the Orange Pi PC Plus. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/configs/orangepi_pc_defconfig b/configs/orangepi_pc_defconfig index 294f7ab..43ec927 100644 --- a/configs/orangepi_pc_defconfig +++ b/configs/orangepi_pc_defconfig @@ -4,6 +4,8 @@ CONFIG_MACH_SUN8I_H3=y CONFIG_DRAM_CLK=624 CONFIG_DRAM_ZQ=3881979 CONFIG_DRAM_ODT_EN=y +CONFIG_MMC0_CD_PIN="PF6" +CONFIG_MMC_SUNXI_SLOT_EXTRA=2 # CONFIG_VIDEO is not set CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-pc" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set -- cgit v0.10.2 From fa8a485d06188e38abc9c46fbd1be916fe16fb7a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jul 2016 17:31:14 +0200 Subject: sunxi: Sync sun8i-h3-orangepi-plus.dts with upstream This enables extra USB controllers which enable use of the 3rd USB port on the new Orange Pi Plus 2E variant. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/arch/arm/dts/sun8i-h3-orangepi-plus.dts b/arch/arm/dts/sun8i-h3-orangepi-plus.dts index 900ec4f..28f74f6 100644 --- a/arch/arm/dts/sun8i-h3-orangepi-plus.dts +++ b/arch/arm/dts/sun8i-h3-orangepi-plus.dts @@ -40,26 +40,13 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -/dts-v1/; -#include "sun8i-h3.dtsi" -#include "sunxi-common-regulators.dtsi" - -#include -#include -#include +/* The Orange Pi Plus is an extended version of the Orange Pi 2 */ +#include "sun8i-h3-orangepi-2.dts" / { - model = "Xunlong Orange Pi Plus"; + model = "Xunlong Orange Pi Plus / Plus 2 / Plus 2E"; compatible = "xunlong,orangepi-plus", "allwinner,sun8i-h3"; - aliases { - serial0 = &uart0; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - reg_usb3_vbus: usb3-vbus { compatible = "regulator-fixed"; pinctrl-names = "default"; @@ -71,75 +58,42 @@ enable-active-high; gpio = <&pio 6 11 GPIO_ACTIVE_HIGH>; }; +}; - leds { - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <&leds_opc>; - - status_led { - label = "status:red:user"; - gpios = <&pio 0 15 GPIO_ACTIVE_HIGH>; - }; - }; - - r_leds { - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <&leds_r_opc>; - - tx { - label = "pwr:green:user"; - gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; - default-state = "on"; - }; - }; - - r_gpio_keys { - compatible = "gpio-keys"; - input-name = "sw4"; - - pinctrl-names = "default"; - pinctrl-0 = <&sw_r_opc>; +&ehci2 { + status = "okay"; +}; - sw4@0 { - label = "sw4"; - linux,code = ; - gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; - }; - }; +&ehci3 { + status = "okay"; }; -&pio { - leds_opc: led_pins@0 { - allwinner,pins = "PA15"; - allwinner,function = "gpio_out"; - allwinner,drive = ; - allwinner,pull = ; - }; +&mmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_8bit_pins>; + vmmc-supply = <®_vcc3v3>; + bus-width = <8>; + non-removable; + cap-mmc-hw-reset; + status = "okay"; }; -&r_pio { - leds_r_opc: led_pins@0 { - allwinner,pins = "PL10"; - allwinner,function = "gpio_out"; - allwinner,drive = ; - allwinner,pull = ; - }; +&mmc2_8bit_pins { + /* Increase drive strength for DDR modes */ + allwinner,drive = ; + /* eMMC is missing pull-ups */ + allwinner,pull = ; +}; - sw_r_opc: key_pins@0 { - allwinner,pins = "PL03"; - allwinner,function = "gpio_in"; - allwinner,drive = ; - allwinner,pull = ; - }; +&ohci1 { + status = "okay"; }; -&ehci1 { +&ohci2 { status = "okay"; }; -&ehci3 { +&ohci3 { status = "okay"; }; @@ -152,33 +106,6 @@ }; }; -&mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; - vmmc-supply = <®_vcc3v3>; - bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; - status = "okay"; -}; - -®_usb1_vbus { - gpio = <&pio 6 13 GPIO_ACTIVE_HIGH>; - status = "okay"; -}; - -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins_a>; - status = "okay"; -}; - -&usb1_vbus_pin_a { - allwinner,pins = "PG13"; -}; - &usbphy { - usb1_vbus-supply = <®_usb1_vbus>; usb3_vbus-supply = <®_usb3_vbus>; - status = "okay"; }; diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi index 6ca5e34..72c0920 100644 --- a/arch/arm/dts/sun8i-h3.dtsi +++ b/arch/arm/dts/sun8i-h3.dtsi @@ -541,6 +541,16 @@ allwinner,drive = ; allwinner,pull = ; }; + + mmc2_8bit_pins: mmc2_8bit { + allwinner,pins = "PC5", "PC6", "PC8", + "PC9", "PC10", "PC11", + "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "mmc2"; + allwinner,drive = ; + allwinner,pull = ; + }; }; ahb_rst: reset@01c202c0 { -- cgit v0.10.2 From 66ab5286736e47cc4fdec9ceab4cc1b2f24ed066 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jul 2016 22:20:00 +0200 Subject: sunxi: Add defconfig and dts file for the Orange Pi Lite SBC The Orange Pi Lite SBC is a small H3 based SBC, with 512MB RAM, micro-sd slot, HDMI out, 2 USB-A connectors, 1 micro-USB connector, sdio attached rtl8189ftv wifi and an ir receiver. The dts file is identical to the one submitted to the upstream kernel. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index ef573ec..acecb7c 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -242,6 +242,7 @@ dtb-$(CONFIG_MACH_SUN8I_A83T) += \ sun8i-a83t-sinovoip-bpi-m3.dtb dtb-$(CONFIG_MACH_SUN8I_H3) += \ sun8i-h3-orangepi-2.dtb \ + sun8i-h3-orangepi-lite.dtb \ sun8i-h3-orangepi-one.dtb \ sun8i-h3-orangepi-pc.dtb \ sun8i-h3-orangepi-plus.dtb diff --git a/arch/arm/dts/sun8i-h3-orangepi-lite.dts b/arch/arm/dts/sun8i-h3-orangepi-lite.dts new file mode 100644 index 0000000..ac71749 --- /dev/null +++ b/arch/arm/dts/sun8i-h3-orangepi-lite.dts @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 Hans de Goede + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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 file 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. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "sun8i-h3.dtsi" +#include "sunxi-common-regulators.dtsi" + +#include +#include +#include + +/ { + model = "Xunlong Orange Pi Lite"; + compatible = "xunlong,orangepi-lite", "allwinner,sun8i-h3"; + + aliases { + /* The H3 emac is not used so the wifi is ethernet0 */ + ethernet1 = &rtl8189ftv; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&leds_opc>, <&leds_r_opc>; + + pwr_led { + label = "orangepi:green:pwr"; + gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + status_led { + label = "orangepi:red:status"; + gpios = <&pio 0 15 GPIO_ACTIVE_HIGH>; + }; + }; + + r_gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&sw_r_opc>; + + sw4 { + label = "sw4"; + linux,code = ; + gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&ehci1 { + status = "okay"; +}; + +&ehci2 { + status = "okay"; +}; + +&ir { + pinctrl-names = "default"; + pinctrl-0 = <&ir_pins_a>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + cd-inverted; + status = "okay"; +}; + +&mmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins_a>; + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; + non-removable; + status = "okay"; + + /* + * Explicitly define the sdio device, so that we can add an ethernet + * alias for it (which e.g. makes u-boot set a mac-address). + */ + rtl8189ftv: sdio_wifi@1 { + reg = <1>; + }; +}; + +&ohci1 { + status = "okay"; +}; + +&ohci2 { + status = "okay"; +}; + +&pio { + leds_opc: led_pins@0 { + allwinner,pins = "PA15"; + allwinner,function = "gpio_out"; + allwinner,drive = ; + allwinner,pull = ; + }; +}; + +&r_pio { + leds_r_opc: led_pins@0 { + allwinner,pins = "PL10"; + allwinner,function = "gpio_out"; + allwinner,drive = ; + allwinner,pull = ; + }; + + sw_r_opc: key_pins@0 { + allwinner,pins = "PL3"; + allwinner,function = "gpio_in"; + allwinner,drive = ; + allwinner,pull = ; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; +}; + +&usbphy { + /* USB VBUS is always on */ + status = "okay"; +}; diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS index d2dfebe..0dc84f6 100644 --- a/board/sunxi/MAINTAINERS +++ b/board/sunxi/MAINTAINERS @@ -56,6 +56,7 @@ F: configs/ga10h_v1_1_defconfig F: configs/gt90h_v4_defconfig F: configs/inet86dz_defconfig F: configs/orangepi_2_defconfig +F: configs/orangepi_lite_defconfig F: configs/orangepi_one_defconfig F: configs/orangepi_pc_defconfig F: configs/orangepi_plus_defconfig diff --git a/configs/orangepi_lite_defconfig b/configs/orangepi_lite_defconfig new file mode 100644 index 0000000..417e4f6 --- /dev/null +++ b/configs/orangepi_lite_defconfig @@ -0,0 +1,15 @@ +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_MACH_SUN8I_H3=y +CONFIG_DRAM_CLK=672 +CONFIG_DRAM_ZQ=3881979 +CONFIG_DRAM_ODT_EN=y +CONFIG_MMC0_CD_PIN="PF6" +# CONFIG_VIDEO is not set +CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-lite" +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set +CONFIG_SPL=y +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +CONFIG_USB_EHCI_HCD=y -- cgit v0.10.2 From b52813239c10d857bd262dc850232ccccdbaa69e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:31 +0800 Subject: ARM: PSCI: Split out common stack setup code from psci_arch_init Every platform has the same stack setup code in assembly as part of psci_arch_init. Move this out into a common separate function, psci_stack_setup, for all platforms. This will allow us to move the remaining parts of psci_arch_init into C code, or drop it entirely. Also provide a stub no-op psci_arch_init for platforms that don't need their own specific setup code. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/ls102xa/psci.S b/arch/arm/cpu/armv7/ls102xa/psci.S index cf5cd48..86116e1f 100644 --- a/arch/arm/cpu/armv7/ls102xa/psci.S +++ b/arch/arm/cpu/armv7/ls102xa/psci.S @@ -111,16 +111,6 @@ psci_cpu_off: 1: wfi b 1b -.globl psci_arch_init -psci_arch_init: - mov r6, lr - - bl psci_get_cpu_id - bl psci_get_cpu_stack_top - mov sp, r0 - - bx r6 - .globl psci_text_end psci_text_end: .popsection diff --git a/arch/arm/cpu/armv7/mx7/psci.S b/arch/arm/cpu/armv7/mx7/psci.S index 34c6ab3..12cca7c 100644 --- a/arch/arm/cpu/armv7/mx7/psci.S +++ b/arch/arm/cpu/armv7/mx7/psci.S @@ -9,22 +9,6 @@ .arch_extension sec - @ r1 = target CPU - @ r2 = target PC - -.globl psci_arch_init -psci_arch_init: - mov r6, lr - - bl psci_get_cpu_id - bl psci_get_cpu_stack_top - mov sp, r0 - - bx r6 - - @ r1 = target CPU - @ r2 = target PC - .globl psci_cpu_on psci_cpu_on: push {lr} diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index b7563ed..95ce938 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -49,8 +49,13 @@ _secure_monitor: mcr p15, 0, r5, c12, c0, 1 isb - @ Obtain a secure stack, and configure the PSCI backend + @ Obtain a secure stack + bl psci_stack_setup + + @ Configure the PSCI backend + push {r0, r1, r2, ip} bl psci_arch_init + pop {r0, r1, r2, ip} #endif #ifdef CONFIG_ARM_ERRATA_773022 diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S index ab40837..46fcf77 100644 --- a/arch/arm/cpu/armv7/psci.S +++ b/arch/arm/cpu/armv7/psci.S @@ -211,6 +211,24 @@ ENTRY(psci_get_cpu_stack_top) bx lr ENDPROC(psci_get_cpu_stack_top) +@ {r0, r1, r2, ip} from _do_nonsec_entry(kernel_entry, 0, machid, r2) in +@ arch/arm/lib/bootm.c:boot_jump_linux() must remain unchanged across +@ this function. +ENTRY(psci_stack_setup) + mov r6, lr + mov r7, r0 + bl psci_get_cpu_id @ CPU ID => r0 + bl psci_get_cpu_stack_top @ stack top => r0 + mov sp, r0 + mov r0, r7 + bx r6 +ENDPROC(psci_stack_setup) + +ENTRY(psci_arch_init) + mov pc, lr +ENDPROC(psci_arch_init) +.weak psci_arch_init + ENTRY(psci_cpu_entry) bl psci_enable_smp diff --git a/arch/arm/cpu/armv7/sunxi/psci_head.S b/arch/arm/cpu/armv7/sunxi/psci_head.S index 8fa823d..e51db04 100644 --- a/arch/arm/cpu/armv7/sunxi/psci_head.S +++ b/arch/arm/cpu/armv7/sunxi/psci_head.S @@ -44,22 +44,8 @@ #define GICD_BASE (SUNXI_GIC400_BASE + 0x1000) #define GICC_BASE (SUNXI_GIC400_BASE + 0x2000) -@ {r0, r1, r2, ip} from _do_nonsec_entry(kernel_entry, 0, machid, r2) in -@ arch/arm/lib/bootm.c:boot_jump_linux() must remain unchanged across -@ this function. ENTRY(psci_arch_init) - mov r6, lr - mov r7, r0 - bl psci_get_cpu_id @ CPU ID => r0 - bl psci_get_cpu_stack_top @ stack top => r0 - sub r0, r0, #4 @ Save space for target PC - mov sp, r0 - mov r0, r7 - mov lr, r6 - - push {r0, r1, r2, ip, lr} - bl sunxi_gic_init - pop {r0, r1, r2, ip, pc} + b sunxi_gic_init ENDPROC(psci_arch_init) ENTRY(psci_text_end) diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S index b836da1..75068f3 100644 --- a/arch/arm/mach-tegra/psci.S +++ b/arch/arm/mach-tegra/psci.S @@ -61,9 +61,6 @@ ENTRY(psci_arch_init) ldrne r7, [r5] mcrne p15, 0, r7, c14, c0, 0 @ write CNTFRQ to CPU1..3 - bl psci_get_cpu_stack_top @ stack top => r0 - mov sp, r0 - bx r6 ENDPROC(psci_arch_init) -- cgit v0.10.2 From 94a389b257039511784b91a263913c845c8146e4 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:32 +0800 Subject: sunxi: Move remaining PSCI assembly code to C This patch finishes the rewrite of sunxi specific PSCI parts into C code. The assembly-only stack setup code has been factored out into a common function for ARMv7. The GIC setup code can be renamed as psci_arch_init. And we can use an empty stub function for psci_text_end. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index c208510..b35b9df 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_MACH_SUN8I_H3) += tzpc.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMV7_PSCI) += psci.o -obj-$(CONFIG_ARMV7_PSCI) += psci_head.o endif ifdef CONFIG_SPL_BUILD diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index a118e9d..cd0d944 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -250,7 +250,7 @@ void __secure psci_cpu_off(void) wfi(); } -void __secure sunxi_gic_init(void) +void __secure psci_arch_init(void) { u32 reg; @@ -271,3 +271,8 @@ void __secure sunxi_gic_init(void) reg &= ~BIT(0); /* Secure mode */ cp15_write_scr(reg); } + +/* dummy entry for end of psci text */ +void __secure psci_text_end(void) +{ +} diff --git a/arch/arm/cpu/armv7/sunxi/psci_head.S b/arch/arm/cpu/armv7/sunxi/psci_head.S deleted file mode 100644 index e51db04..0000000 --- a/arch/arm/cpu/armv7/sunxi/psci_head.S +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2013 - ARM Ltd - * Author: Marc Zyngier - * - * Based on code by Carl van Schaik . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 . - */ - -#include -#include - -#include -#include -#include -#include -#include - -/* - * Memory layout: - * - * SECURE_RAM to text_end : - * ._secure_text section - * text_end to ALIGN_PAGE(text_end): - * nothing - * ALIGN_PAGE(text_end) to ALIGN_PAGE(text_end) + 0x1000) - * 1kB of stack per CPU (4 CPUs max). - */ - - .pushsection ._secure.text, "ax" - - .arch_extension sec - -#define GICD_BASE (SUNXI_GIC400_BASE + 0x1000) -#define GICC_BASE (SUNXI_GIC400_BASE + 0x2000) - -ENTRY(psci_arch_init) - b sunxi_gic_init -ENDPROC(psci_arch_init) - -ENTRY(psci_text_end) - .popsection -- cgit v0.10.2 From 1e77ce0e8b77c1e4ed516b5f8a46ef6d06921432 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:33 +0800 Subject: sunxi: Add missing CONFIG_ARMV7_PSCI_NR_CPUS for sun7i sun7i has 2 CPUs. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index 0dd2902..1c89fdf 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -21,6 +21,7 @@ #define CONFIG_SUNXI_USB_PHYS 3 #define CONFIG_ARMV7_PSCI 1 +#define CONFIG_ARMV7_PSCI_NR_CPUS 2 #define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE /* -- cgit v0.10.2 From a1274cc94a20a0fc6715522d021ab84969b6bf91 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:34 +0800 Subject: ARM: Page align secure section only when it is executed in situ Targets that define CONFIG_ARMV7_SECURE_BASE will copy the secure section to another address before execution. Since the secure section in the u-boot image is only storage, there's no reason to page align it and increase the binary image size. Page align the secure section only when CONFIG_ARMV7_SECURE_BASE is not defined. And instead of just aligning the __secure_start symbol, align the whole .__secure_start section. This also makes the section empty, so we need to add KEEP() to the input entry to prevent the section from being garbage collected. Also use ld constant "COMMONPAGESIZE" instead of hardcoded page size. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index 1769b6e..ba17778 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -48,16 +48,20 @@ SECTIONS #ifdef CONFIG_ARMV7_NONSEC + /* Align the secure section only if we're going to use it in situ */ + .__secure_start : +#ifndef CONFIG_ARMV7_SECURE_BASE + ALIGN(CONSTANT(COMMONPAGESIZE)) +#endif + { + KEEP(*(.__secure_start)) + } + #ifndef CONFIG_ARMV7_SECURE_BASE #define CONFIG_ARMV7_SECURE_BASE #define __ARMV7_PSCI_STACK_IN_RAM #endif - .__secure_start : { - . = ALIGN(0x1000); - *(.__secure_start) - } - .secure_text CONFIG_ARMV7_SECURE_BASE : AT(ADDR(.__secure_start) + SIZEOF(.__secure_start)) { -- cgit v0.10.2 From dbf38aabd9f4d4fd4d9bd4eeeba88e0e47dcb27c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 Jul 2016 21:45:05 +0800 Subject: ARM: PSCI: Add missing CONFIG_ARMV7_PSCI_NR_CPUS for PSCI enabled platforms The original PSCI implementation assumed CONFIG_ARMV7_PSCI_NR_CPUS=4. Add this to platforms that have not defined it, using CONFIG_MAX_CPUS if it is defined, or the actual number of cores for the given platform. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/include/configs/jetson-tk1.h b/include/configs/jetson-tk1.h index 953c088..2b172a5 100644 --- a/include/configs/jetson-tk1.h +++ b/include/configs/jetson-tk1.h @@ -61,6 +61,7 @@ #include "tegra-common-post.h" #define CONFIG_ARMV7_PSCI 1 +#define CONFIG_ARMV7_PSCI_NR_CPUS 4 /* Reserve top 1M for secure RAM */ #define CONFIG_ARMV7_SECURE_BASE 0xfff00000 #define CONFIG_ARMV7_SECURE_RESERVE_SIZE 0x00100000 diff --git a/include/configs/ls1021aqds.h b/include/configs/ls1021aqds.h index db684d2..eb444eb 100644 --- a/include/configs/ls1021aqds.h +++ b/include/configs/ls1021aqds.h @@ -10,6 +10,7 @@ #define CONFIG_LS102XA #define CONFIG_ARMV7_PSCI +#define CONFIG_ARMV7_PSCI_NR_CPUS CONFIG_MAX_CPUS #define CONFIG_SYS_FSL_CLK diff --git a/include/configs/ls1021atwr.h b/include/configs/ls1021atwr.h index 0fb28ef..616aebb 100644 --- a/include/configs/ls1021atwr.h +++ b/include/configs/ls1021atwr.h @@ -10,6 +10,7 @@ #define CONFIG_LS102XA #define CONFIG_ARMV7_PSCI +#define CONFIG_ARMV7_PSCI_NR_CPUS CONFIG_MAX_CPUS #define CONFIG_SYS_FSL_CLK -- cgit v0.10.2 From 980d6a55119f757ade4abed88bf4b2b7494c68e6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:36 +0800 Subject: ARM: Add an empty secure stack section Until now we've been using memory beyond psci_text_end as stack space for the secure monitor or PSCI implementation, even if space was not allocated for it. This was partially fixed in ("ARM: allocate extra space for PSCI stack in secure section during link phase"). However, calculating stack space from psci_text_end in one place, while allocating the space in another is error prone. This patch adds a separate empty secure stack section, with space for CONFIG_ARMV7_PSCI_NR_CPUS stacks, each 1 KB. There's also __secure_stack_start and __secure_stack_end symbols. The linker script handles calculating the correct VMAs for the stack section. For platforms that relocate/copy the secure monitor before using it, the space is not allocated in the executable, saving space. For platforms that do not define CONFIG_ARMV7_PSCI_NR_CPUS, a whole page of stack space for 4 CPUs is allocated, matching the previous behavior. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index ba17778..002706a 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -8,6 +8,7 @@ */ #include +#include OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) @@ -68,18 +69,31 @@ SECTIONS *(._secure.text) } - . = LOADADDR(.__secure_start) + - SIZEOF(.__secure_start) + - SIZEOF(.secure_text); - + .secure_stack ALIGN(ADDR(.secure_text) + SIZEOF(.secure_text), + CONSTANT(COMMONPAGESIZE)) (NOLOAD) : #ifdef __ARMV7_PSCI_STACK_IN_RAM - /* Align to page boundary and skip 2 pages */ - . = (. & ~ 0xfff) + 0x2000; -#undef __ARMV7_PSCI_STACK_IN_RAM + AT(ADDR(.secure_stack)) +#else + AT(LOADADDR(.secure_text) + SIZEOF(.secure_text)) +#endif + { + KEEP(*(.__secure_stack_start)) + + /* Skip addreses for stack */ + . = . + CONFIG_ARMV7_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE; + + /* Align end of stack section to page boundary */ + . = ALIGN(CONSTANT(COMMONPAGESIZE)); + + KEEP(*(.__secure_stack_end)) + } + +#ifndef __ARMV7_PSCI_STACK_IN_RAM + /* Reset VMA but don't allocate space if we have secure SRAM */ + . = LOADADDR(.secure_stack); #endif - __secure_end_lma = .; - .__secure_end : AT(__secure_end_lma) { + .__secure_end : AT(ADDR(.__secure_end)) { *(.__secure_end) LONG(0x1d1071c); /* Must output something to reset LMA */ } diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h index 423fc70..a20702e 100644 --- a/arch/arm/include/asm/armv7.h +++ b/arch/arm/include/asm/armv7.h @@ -126,6 +126,8 @@ void _smp_pen(void); extern char __secure_start[]; extern char __secure_end[]; +extern char __secure_stack_start[]; +extern char __secure_stack_end[]; #endif /* CONFIG_ARMV7_NONSEC */ diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h index bc5edda..dab5769 100644 --- a/arch/arm/include/asm/psci.h +++ b/arch/arm/include/asm/psci.h @@ -47,6 +47,10 @@ #define ARM_PSCI_0_2_FN_SYSTEM_OFF ARM_PSCI_0_2_FN(8) #define ARM_PSCI_0_2_FN_SYSTEM_RESET ARM_PSCI_0_2_FN(9) +/* 1KB stack per core */ +#define ARM_PSCI_STACK_SHIFT 10 +#define ARM_PSCI_STACK_SIZE (1 << ARM_PSCI_STACK_SHIFT) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c index 6a94522..952e8ae 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -27,6 +27,8 @@ char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start"))); char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end"))); char __secure_start[0] __attribute__((section(".__secure_start"))); char __secure_end[0] __attribute__((section(".__secure_end"))); +char __secure_stack_start[0] __attribute__((section(".__secure_stack_start"))); +char __secure_stack_end[0] __attribute__((section(".__secure_stack_end"))); char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start"))); char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop"))); char __efi_runtime_rel_start[0] __attribute__((section(".__efi_runtime_rel_start"))); -- cgit v0.10.2 From 8c0ef7fad676827125ad91060361d05ab4b16f44 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:37 +0800 Subject: ARM: PSCI: Allocate PSCI stack in secure stack section Now that we have a secure stack section that guarantees usable memory, allocate the PSCI stacks in that section. Also add a diagram detailing how the stacks are placed in memory. Reserved space for the target PC remains unchanged. This should be moved to global variables within a secure data section in the future. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S index 46fcf77..bfc4475 100644 --- a/arch/arm/cpu/armv7/psci.S +++ b/arch/arm/cpu/armv7/psci.S @@ -196,18 +196,28 @@ ENTRY(psci_cpu_off_common) bx lr ENDPROC(psci_cpu_off_common) -@ expects CPU ID in r0 and returns stack top in r0 +@ The stacks are allocated in reverse order, i.e. +@ the stack for CPU0 has the highest memory address. +@ +@ -------------------- __secure_stack_end +@ | CPU0 target PC | +@ |------------------| +@ | | +@ | CPU0 stack | +@ | | +@ |------------------| __secure_stack_end - 1KB +@ | . | +@ | . | +@ | . | +@ | . | +@ -------------------- __secure_stack_start +@ +@ This expects CPU ID in r0 and returns stack top in r0 ENTRY(psci_get_cpu_stack_top) - mov r3, #0x400 @ 1kB of stack per CPU - mul r0, r0, r3 - - ldr r3, =psci_text_end @ end of monitor text - add r3, r3, #0x2000 @ Skip two pages - lsr r3, r3, #12 @ Align to start of page - lsl r3, r3, #12 - sub r3, r3, #4 @ reserve 1 word for target PC - sub r0, r3, r0 @ here's our stack! - + @ stack top = __secure_stack_end - (cpuid << ARM_PSCI_STACK_SHIFT) + ldr r3, =__secure_stack_end + sub r0, r3, r0, LSL #ARM_PSCI_STACK_SHIFT + sub r0, r0, #4 @ Save space for target PC bx lr ENDPROC(psci_get_cpu_stack_top) -- cgit v0.10.2 From 28f90357323bc65e7da01c458222b84dc42988bb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:38 +0800 Subject: ARM: PSCI: Remove unused psci_text_end symbol psci_text_end was used to calculate the PSCI stack address following the secure monitor text. Now that we have an explicit secure stack section, this is no longer used. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/ls102xa/psci.S b/arch/arm/cpu/armv7/ls102xa/psci.S index 86116e1f..ba043ef 100644 --- a/arch/arm/cpu/armv7/ls102xa/psci.S +++ b/arch/arm/cpu/armv7/ls102xa/psci.S @@ -111,6 +111,4 @@ psci_cpu_off: 1: wfi b 1b - .globl psci_text_end -psci_text_end: .popsection diff --git a/arch/arm/cpu/armv7/mx7/psci.S b/arch/arm/cpu/armv7/mx7/psci.S index 12cca7c..d9e9fbf 100644 --- a/arch/arm/cpu/armv7/mx7/psci.S +++ b/arch/arm/cpu/armv7/mx7/psci.S @@ -33,6 +33,4 @@ psci_cpu_off: 1: wfi b 1b - .globl psci_text_end -psci_text_end: .popsection diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index cd0d944..c7d97fe 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -271,8 +271,3 @@ void __secure psci_arch_init(void) reg &= ~BIT(0); /* Secure mode */ cp15_write_scr(reg); } - -/* dummy entry for end of psci text */ -void __secure psci_text_end(void) -{ -} diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S index 75068f3..85d5b6b 100644 --- a/arch/arm/mach-tegra/psci.S +++ b/arch/arm/mach-tegra/psci.S @@ -106,6 +106,4 @@ ENTRY(psci_cpu_on) pop {pc} ENDPROC(psci_cpu_on) - .globl psci_text_end -psci_text_end: .popsection -- cgit v0.10.2 From 3eff681818e2c858175a7cc4e766f75e95827920 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:39 +0800 Subject: ARM: Add CONFIG_ARMV7_SECURE_MAX_SIZE and check size of secure section As the PSCI implementation grows, we might exceed the size of the secure memory that holds the firmware. Add a configurable CONFIG_ARMV7_SECURE_MAX_SIZE so platforms can define how much secure memory is available. The linker then checks the size of the whole secure section against this. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index 002706a..5a65c27 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -86,6 +86,17 @@ SECTIONS . = ALIGN(CONSTANT(COMMONPAGESIZE)); KEEP(*(.__secure_stack_end)) + +#ifdef CONFIG_ARMV7_SECURE_MAX_SIZE + /* + * We are not checking (__secure_end - __secure_start) here, + * as these are the load addresses, and do not include the + * stack section. Instead, use the end of the stack section + * and the start of the text section. + */ + ASSERT((. - ADDR(.secure_text)) <= CONFIG_ARMV7_SECURE_MAX_SIZE, + "Error: secure section exceeds secure memory size"); +#endif } #ifndef __ARMV7_PSCI_STACK_IN_RAM -- cgit v0.10.2 From 9c4f52b855876862a7a30b63bde4a0d22605c3dc Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:40 +0800 Subject: sunxi: Define CONFIG_ARMV7_SECURE_MAX_SIZE for sun6i/sun7i Both sun6i and sun7i have 64 KB of secure SRAM. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/include/configs/sun6i.h b/include/configs/sun6i.h index 95ccc35..0625502 100644 --- a/include/configs/sun6i.h +++ b/include/configs/sun6i.h @@ -25,6 +25,7 @@ #define CONFIG_ARMV7_PSCI 1 #define CONFIG_ARMV7_PSCI_NR_CPUS 4 #define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE +#define CONFIG_ARMV7_SECURE_MAX_SIZE (64 * 1024) /* 64 KB */ /* * Include common sunxi configuration where most the settings are diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index 1c89fdf..e9074d5 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -23,6 +23,7 @@ #define CONFIG_ARMV7_PSCI 1 #define CONFIG_ARMV7_PSCI_NR_CPUS 2 #define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE +#define CONFIG_ARMV7_SECURE_MAX_SIZE (64 * 1024) /* 64 KB */ /* * Include common sunxi configuration where most the settings are -- cgit v0.10.2 From afc1f65f504324cd5f87a8cb480bebeb0c61e4e0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:41 +0800 Subject: ARM: Move __secure definition to common asm/secure.h sunxi and i.mx7 both define the __secure modifier to put functions in the secure section. Move this to a common place. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/mx7/psci-mx7.c b/arch/arm/cpu/armv7/mx7/psci-mx7.c index 9a33047..502552d 100644 --- a/arch/arm/cpu/armv7/mx7/psci-mx7.c +++ b/arch/arm/cpu/armv7/mx7/psci-mx7.c @@ -1,9 +1,9 @@ #include #include +#include #include #include -#define __secure __attribute__((section("._secure.text"))) #define GPC_CPU_PGC_SW_PDN_REQ 0xfc #define GPC_CPU_PGC_SW_PUP_REQ 0xf0 diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index c7d97fe..be3a1fb 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -17,11 +17,11 @@ #include #include #include +#include #include #include -#define __secure __attribute__ ((section ("._secure.text"))) #define __irq __attribute__ ((interrupt ("IRQ"))) #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET) diff --git a/arch/arm/include/asm/secure.h b/arch/arm/include/asm/secure.h index effdb18..6d9088b 100644 --- a/arch/arm/include/asm/secure.h +++ b/arch/arm/include/asm/secure.h @@ -3,6 +3,8 @@ #include +#define __secure __attribute__ ((section ("._secure.text"))) + #ifdef CONFIG_ARMV7_SECURE_BASE /* * Warning, horror ahead. -- cgit v0.10.2 From a5aa7ff33a942ce8ea38006fd5225a800827bb2c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 Jul 2016 21:45:06 +0800 Subject: ARM: Add secure section for initialized data The secure monitor may need to store global or static values within the secure section of memory, such as target PC or CPU power status. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 9a5a974..8f85862 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -120,8 +120,8 @@ endif ifdef CONFIG_ARM64 OBJCOPYFLAGS += -j .text -j .rodata -j .data -j .u_boot_list -j .rela.dyn else -OBJCOPYFLAGS += -j .text -j .secure_text -j .rodata -j .hash -j .data -j \ - .got -j .got.plt -j .u_boot_list -j .rel.dyn +OBJCOPYFLAGS += -j .text -j .secure_text -j .secure_data -j .rodata -j .hash \ + -j .data -j .got -j .got.plt -j .u_boot_list -j .rel.dyn endif ifdef CONFIG_OF_EMBED diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index 5a65c27..36c9fd0 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -69,12 +69,17 @@ SECTIONS *(._secure.text) } - .secure_stack ALIGN(ADDR(.secure_text) + SIZEOF(.secure_text), + .secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text)) + { + *(._secure.data) + } + + .secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data), CONSTANT(COMMONPAGESIZE)) (NOLOAD) : #ifdef __ARMV7_PSCI_STACK_IN_RAM AT(ADDR(.secure_stack)) #else - AT(LOADADDR(.secure_text) + SIZEOF(.secure_text)) + AT(LOADADDR(.secure_data) + SIZEOF(.secure_data)) #endif { KEEP(*(.__secure_stack_start)) diff --git a/arch/arm/include/asm/secure.h b/arch/arm/include/asm/secure.h index 6d9088b..5a403bc 100644 --- a/arch/arm/include/asm/secure.h +++ b/arch/arm/include/asm/secure.h @@ -4,6 +4,7 @@ #include #define __secure __attribute__ ((section ("._secure.text"))) +#define __secure_data __attribute__ ((section ("._secure.data"))) #ifdef CONFIG_ARMV7_SECURE_BASE /* -- cgit v0.10.2 From 45c334e6b22bae75ada8662b88000c4347b1361b Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 Jul 2016 21:45:07 +0800 Subject: ARM: PSCI: Add helper functions to access per-CPU target PC storage Now that we have a data section, add helper functions to save and fetch per-CPU target PC. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index ddd8d12..0d4bfbc 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -19,7 +19,7 @@ endif endif obj-$(CONFIG_ARMV7_NONSEC) += nonsec_virt.o virt-v7.o virt-dt.o -obj-$(CONFIG_ARMV7_PSCI) += psci.o +obj-$(CONFIG_ARMV7_PSCI) += psci.o psci-common.o obj-$(CONFIG_IPROC) += iproc-common/ obj-$(CONFIG_KONA) += kona-common/ diff --git a/arch/arm/cpu/armv7/psci-common.c b/arch/arm/cpu/armv7/psci-common.c new file mode 100644 index 0000000..d14b693 --- /dev/null +++ b/arch/arm/cpu/armv7/psci-common.c @@ -0,0 +1,39 @@ +/* + * Common PSCI functions + * + * Copyright (C) 2016 Chen-Yu Tsai + * Author: Chen-Yu Tsai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +static u32 psci_target_pc[CONFIG_ARMV7_PSCI_NR_CPUS] __secure_data = { 0 }; + +void __secure psci_save_target_pc(int cpu, u32 pc) +{ + psci_target_pc[cpu] = pc; + DSB; +} + +u32 __secure psci_get_target_pc(int cpu) +{ + return psci_target_pc[cpu]; +} + diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h index dab5769..a0da023 100644 --- a/arch/arm/include/asm/psci.h +++ b/arch/arm/include/asm/psci.h @@ -54,6 +54,10 @@ #ifndef __ASSEMBLY__ #include +/* These 2 helper functions assume cpu < CONFIG_ARMV7_PSCI_NR_CPUS */ +u32 psci_get_target_pc(int cpu); +void psci_save_target_pc(int cpu, u32 pc); + void psci_cpu_entry(void); u32 psci_get_cpu_id(void); u32 psci_get_cpu_stack_top(int cpu); -- cgit v0.10.2 From 6e6622de166f53597172687b7269b07cf48844df Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:44 +0800 Subject: ARM: PSCI: Switch to per-CPU target PC storage in secure data section Now that we have a secure data section and space to store per-CPU target PC address, switch to it instead of storing the target PC on the stack. Also save clobbered r4-r7 registers on the stack and restore them on return in psci_cpu_on for Tegra, i.MX7, and LS102xA platforms. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/ls102xa/psci.S b/arch/arm/cpu/armv7/ls102xa/psci.S index ba043ef..f9b26b4 100644 --- a/arch/arm/cpu/armv7/ls102xa/psci.S +++ b/arch/arm/cpu/armv7/ls102xa/psci.S @@ -29,16 +29,16 @@ @ r2 = target PC .globl psci_cpu_on psci_cpu_on: - push {lr} + push {r4, r5, r6, lr} @ Clear and Get the correct CPU number @ r1 = 0xf01 - and r1, r1, #0xff + and r4, r1, #0xff - mov r0, r1 - bl psci_get_cpu_stack_top - str r2, [r0] - dsb + mov r0, r4 + mov r1, r2 + bl psci_save_target_pc + mov r1, r4 @ Get DCFG base address movw r4, #(CONFIG_SYS_FSL_GUTS_ADDR & 0xffff) @@ -101,7 +101,7 @@ holdoff_release: @ Return mov r0, #ARM_PSCI_RET_SUCCESS - pop {lr} + pop {r4, r5, r6, lr} bx lr .globl psci_cpu_off diff --git a/arch/arm/cpu/armv7/mx7/psci.S b/arch/arm/cpu/armv7/mx7/psci.S index d9e9fbf..96e88d6 100644 --- a/arch/arm/cpu/armv7/mx7/psci.S +++ b/arch/arm/cpu/armv7/mx7/psci.S @@ -11,17 +11,20 @@ .globl psci_cpu_on psci_cpu_on: - push {lr} + push {r4, r5, lr} + mov r4, r0 + mov r5, r1 mov r0, r1 - bl psci_get_cpu_stack_top - str r2, [r0] - dsb + mov r1, r2 + bl psci_save_target_pc + mov r0, r4 + mov r1, r5 ldr r2, =psci_cpu_entry bl imx_cpu_on - pop {pc} + pop {r4, r5, pc} .globl psci_cpu_off psci_cpu_off: diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S index bfc4475..50dfddb 100644 --- a/arch/arm/cpu/armv7/psci.S +++ b/arch/arm/cpu/armv7/psci.S @@ -245,8 +245,7 @@ ENTRY(psci_cpu_entry) bl _nonsec_init bl psci_get_cpu_id @ CPU ID => r0 - bl psci_get_cpu_stack_top @ stack top => r0 - ldr r0, [r0] @ target PC at stack top + bl psci_get_target_pc @ target PC => r0 b _do_nonsec_entry ENDPROC(psci_cpu_entry) diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index be3a1fb..7ac8406 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -209,9 +209,8 @@ int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc) (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; u32 cpu = (mpidr & 0x3); - /* store target PC at target CPU stack top */ - writel(pc, psci_get_cpu_stack_top(cpu)); - DSB; + /* store target PC */ + psci_save_target_pc(cpu, pc); /* Set secondary core power on PC */ writel((u32)&psci_cpu_entry, &cpucfg->priv0); diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S index 85d5b6b..645d08f 100644 --- a/arch/arm/mach-tegra/psci.S +++ b/arch/arm/mach-tegra/psci.S @@ -85,12 +85,13 @@ _loop: wfi ENDPROC(psci_cpu_off) ENTRY(psci_cpu_on) - push {lr} + push {r4, r5, r6, lr} + mov r4, r1 mov r0, r1 - bl psci_get_cpu_stack_top @ get stack top of target CPU - str r2, [r0] @ store target PC at stack top - dsb + mov r1, r2 + bl psci_save_target_pc @ store target PC + mov r1, r4 ldr r6, =TEGRA_RESET_EXCEPTION_VECTOR ldr r5, =psci_cpu_entry @@ -103,7 +104,7 @@ ENTRY(psci_cpu_on) str r5, [r6, r2] mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS - pop {pc} + pop {r4, r5, r6, pc} ENDPROC(psci_cpu_on) .popsection -- cgit v0.10.2 From b7073965a343fca2bcde4195fbba664c98f309d8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 19 Jun 2016 12:38:45 +0800 Subject: ARM: PSCI: Make psci_get_cpu_stack_top local to armv7/psci.S Now that we have a secure data section for storing variables, there should be no need for platform code to get the stack address. Make psci_get_cpu_stack_top a local function, as it should only be used in armv7/psci.S and only by psci_stack_setup. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans de Goede diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S index 50dfddb..350b75c 100644 --- a/arch/arm/cpu/armv7/psci.S +++ b/arch/arm/cpu/armv7/psci.S @@ -213,7 +213,7 @@ ENDPROC(psci_cpu_off_common) @ -------------------- __secure_stack_start @ @ This expects CPU ID in r0 and returns stack top in r0 -ENTRY(psci_get_cpu_stack_top) +LENTRY(psci_get_cpu_stack_top) @ stack top = __secure_stack_end - (cpuid << ARM_PSCI_STACK_SHIFT) ldr r3, =__secure_stack_end sub r0, r3, r0, LSL #ARM_PSCI_STACK_SHIFT diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h index a0da023..7ba7ce3 100644 --- a/arch/arm/include/asm/psci.h +++ b/arch/arm/include/asm/psci.h @@ -60,7 +60,6 @@ void psci_save_target_pc(int cpu, u32 pc); void psci_cpu_entry(void); u32 psci_get_cpu_id(void); -u32 psci_get_cpu_stack_top(int cpu); void psci_cpu_off_common(void); int psci_update_dt(void *fdt); -- cgit v0.10.2