summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/boards/imx/embest-i.mx6s-riotboard.rst133
-rw-r--r--Documentation/user/state.rst8
-rw-r--r--Documentation/user/variables.rst8
-rw-r--r--Makefile2
-rw-r--r--arch/arm/boards/Makefile1
-rw-r--r--arch/arm/boards/ls1046ardb/lowlevel.c3
-rw-r--r--arch/arm/boards/marvell-armada-xp-db/Makefile2
-rw-r--r--arch/arm/boards/marvell-armada-xp-db/board.c31
-rw-r--r--arch/arm/boards/marvell-armada-xp-db/kwbimage.cfg3
-rw-r--r--arch/arm/boards/marvell-armada-xp-db/lowlevel.c33
-rw-r--r--arch/arm/boards/raspberry-pi/rpi-common.c8
-rw-r--r--arch/arm/boards/tqmls1046a/board.c33
-rw-r--r--arch/arm/boards/tqmls1046a/lowlevel.c93
-rw-r--r--arch/arm/boards/tqmls1046a/tqmls1046a_pbi.cfg (renamed from arch/arm/boards/tqmls1046a/tqmls1046a_pbi_sd.cfg)2
-rw-r--r--arch/arm/boards/tqmls1046a/tqmls1046a_pbi_qspi.cfg33
-rw-r--r--arch/arm/boards/tqmls1046a/tqmls1046a_rcw_emmc_3333_5559.cfg84
-rw-r--r--arch/arm/boards/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg8
-rw-r--r--arch/arm/boards/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg14
-rw-r--r--arch/arm/boards/zii-imx51-rdu1/Makefile1
-rw-r--r--arch/arm/boards/zii-imx51-rdu1/lowlevel.c7
-rw-r--r--arch/arm/boards/zii-imx8mq-dev/lowlevel.c7
-rw-r--r--arch/arm/boards/zii-vf610-dev/Makefile1
-rw-r--r--arch/arm/boards/zii-vf610-dev/board.c4
-rw-r--r--arch/arm/boards/zii-vf610-dev/lowlevel.c22
-rw-r--r--arch/arm/configs/layerscape_defconfig8
-rw-r--r--arch/arm/configs/mvebu_defconfig21
-rw-r--r--arch/arm/configs/rpi_defconfig2
-rw-r--r--arch/arm/cpu/Kconfig1
-rw-r--r--arch/arm/cpu/cache-armv7.S13
-rw-r--r--arch/arm/cpu/lowlevel_64.S7
-rw-r--r--arch/arm/cpu/mmu-early.c27
-rw-r--r--arch/arm/cpu/mmu.c16
-rw-r--r--arch/arm/cpu/mmu.h8
-rw-r--r--arch/arm/dts/Makefile5
-rw-r--r--arch/arm/dts/armada-xp-db-bb.dts12
-rw-r--r--arch/arm/dts/bcm2837-rpi-3.dts5
-rw-r--r--arch/arm/dts/fsl-tqmls1046a-mbls10xxa.dts123
-rw-r--r--arch/arm/dts/fsl-tqmls1046a.dtsi4
-rw-r--r--arch/arm/dts/imx8mq.dtsi4
-rw-r--r--arch/arm/dts/vf610-zii-ssmb-dtu.dts16
-rw-r--r--arch/arm/include/asm/bitops.h8
-rw-r--r--arch/arm/include/asm/system.h20
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--arch/arm/mach-imx/imx6.c2
-rw-r--r--arch/arm/mach-imx/include/mach/imx25-regs.h2
-rw-r--r--arch/arm/mach-layerscape/Makefile2
-rw-r--r--arch/arm/mach-layerscape/boot.c39
-rw-r--r--arch/arm/mach-layerscape/include/mach/bbu.h22
-rw-r--r--arch/arm/mach-layerscape/include/mach/layerscape.h2
-rw-r--r--arch/arm/mach-layerscape/include/mach/xload.h4
-rw-r--r--arch/arm/mach-layerscape/xload-qspi.c37
-rw-r--r--arch/arm/mach-layerscape/xload.c24
-rw-r--r--arch/arm/mach-mvebu/Kconfig4
-rw-r--r--arch/arm/mach-socfpga/cyclone5-reset-manager.c17
-rw-r--r--arch/mips/Kconfig19
-rw-r--r--arch/mips/Makefile9
-rw-r--r--arch/mips/boards/8devices-lima/lowlevel.S3
-rw-r--r--arch/mips/boards/black-swift/lowlevel.S3
-rw-r--r--arch/mips/boards/dlink-dir-320/lowlevel.S3
-rw-r--r--arch/mips/boards/dptechnics-dpt-module/lowlevel.S3
-rw-r--r--arch/mips/boards/img-ci20/lowlevel.S3
-rw-r--r--arch/mips/boards/loongson-ls1b/lowlevel.S3
-rw-r--r--arch/mips/boards/qemu-malta/lowlevel.S3
-rw-r--r--arch/mips/boards/ritmix-rzx50/lowlevel.S3
-rw-r--r--arch/mips/boards/tplink-mr3020/lowlevel.S3
-rw-r--r--arch/mips/boards/tplink-wdr4300/lowlevel.S3
-rw-r--r--arch/mips/boot/dtb.c4
-rw-r--r--arch/mips/boot/main_entry-pbl.c13
-rw-r--r--arch/mips/boot/main_entry.c17
-rw-r--r--arch/mips/boot/start.S4
-rw-r--r--arch/mips/include/asm/asm.h3
-rw-r--r--arch/mips/include/asm/pbl_macros.h4
-rw-r--r--arch/mips/include/asm/relocs.h23
-rw-r--r--arch/mips/include/asm/sections.h14
-rw-r--r--arch/mips/lib/Makefile2
-rw-r--r--arch/mips/lib/barebox.lds.S27
-rw-r--r--arch/mips/lib/cpu-probe.c14
-rw-r--r--arch/mips/lib/pbl.lds.S13
-rw-r--r--arch/mips/lib/reloc.c182
-rw-r--r--arch/mips/lib/sections.c9
-rw-r--r--arch/ppc/Makefile2
-rw-r--r--arch/ppc/boards/owc-da923rc/Makefile (renamed from arch/ppc/boards/geip-da923rc/Makefile)2
-rw-r--r--arch/ppc/boards/owc-da923rc/config.h (renamed from arch/ppc/boards/geip-da923rc/config.h)0
-rw-r--r--arch/ppc/boards/owc-da923rc/da923rc.c (renamed from arch/ppc/boards/geip-da923rc/da923rc.c)9
-rw-r--r--arch/ppc/boards/owc-da923rc/ddr.c (renamed from arch/ppc/boards/geip-da923rc/ddr.c)0
-rw-r--r--arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/boot (renamed from arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/boot)0
-rw-r--r--arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/init (renamed from arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/init)0
-rw-r--r--arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/config (renamed from arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/config)0
-rw-r--r--arch/ppc/boards/owc-da923rc/law.c (renamed from arch/ppc/boards/geip-da923rc/law.c)0
-rw-r--r--arch/ppc/boards/owc-da923rc/nand.c (renamed from arch/ppc/boards/geip-da923rc/nand.c)0
-rw-r--r--arch/ppc/boards/owc-da923rc/product_data.c (renamed from arch/ppc/boards/geip-da923rc/product_data.c)17
-rw-r--r--arch/ppc/boards/owc-da923rc/product_data.h (renamed from arch/ppc/boards/geip-da923rc/product_data.h)5
-rw-r--r--arch/ppc/boards/owc-da923rc/tlb.c (renamed from arch/ppc/boards/geip-da923rc/tlb.c)0
-rw-r--r--arch/ppc/configs/owc_da923rc_defconfig (renamed from arch/ppc/configs/da923rc_defconfig)2
-rw-r--r--arch/sandbox/os/common.c2
-rw-r--r--commands/Kconfig10
-rw-r--r--commands/Makefile1
-rw-r--r--commands/blobgen.c122
-rw-r--r--commands/hwclock.c2
-rw-r--r--commands/spi.c16
-rw-r--r--commands/timeout.c2
-rw-r--r--common/bbu.c4
-rw-r--r--common/console.c52
-rw-r--r--common/console_countdown.c19
-rw-r--r--common/ddr_spd.c2
-rw-r--r--common/filetype.c7
-rw-r--r--common/hush.c3
-rw-r--r--common/parser.c1
-rw-r--r--common/startup.c188
-rw-r--r--crypto/digest.c11
-rw-r--r--defaultenv/defaultenv-2-base/bin/init77
-rw-r--r--drivers/clk/imx/clk-imx25.c7
-rw-r--r--drivers/clk/imx/clk-vf610.c33
-rw-r--r--drivers/crypto/Kconfig1
-rw-r--r--drivers/crypto/Makefile1
-rw-r--r--drivers/crypto/caam/Makefile1
-rw-r--r--drivers/crypto/caam/caam-blobgen.c229
-rw-r--r--drivers/crypto/caam/ctrl.c9
-rw-r--r--drivers/crypto/caam/intern.h1
-rw-r--r--drivers/crypto/caam/rng_self_test.c1
-rw-r--r--drivers/crypto/imx-scc/Kconfig14
-rw-r--r--drivers/crypto/imx-scc/Makefile2
-rw-r--r--drivers/crypto/imx-scc/scc-blobgen.c159
-rw-r--r--drivers/crypto/imx-scc/scc.c504
-rw-r--r--drivers/crypto/imx-scc/scc.h13
-rw-r--r--drivers/ddr/fsl/fsl_ddr.h2
-rw-r--r--drivers/mci/Kconfig4
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/bcm2835-sdhost.c638
-rw-r--r--drivers/mci/imx-esdhc-pbl.c5
-rw-r--r--drivers/mtd/devices/Kconfig1
-rw-r--r--drivers/mtd/devices/m25p80.c160
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c257
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c673
-rw-r--r--drivers/mtd/ubi/ubi-barebox.h1
-rw-r--r--drivers/net/fsl-fman.c2
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-imxdi.c623
-rw-r--r--drivers/spi/Kconfig18
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/spi-fsl-qspi.c869
-rw-r--r--drivers/spi/spi-mem.c524
-rw-r--r--drivers/spi/spi.c110
-rw-r--r--dts/Bindings/arm/cpus.yaml2
-rw-r--r--dts/Bindings/hwmon/adc128d818.txt4
-rw-r--r--dts/Bindings/net/davinci_emac.txt2
-rw-r--r--dts/Bindings/net/ethernet.txt5
-rw-r--r--dts/Bindings/net/macb.txt4
-rw-r--r--dts/include/dt-bindings/clock/sifive-fu540-prci.h18
-rw-r--r--dts/include/dt-bindings/input/linux-event-codes.h6
-rw-r--r--dts/include/dt-bindings/reset/amlogic,meson-g12a-reset.h5
-rw-r--r--dts/src/arc/hsdk.dts13
-rw-r--r--dts/src/arm/am335x-evm.dts26
-rw-r--r--dts/src/arm/am335x-evmsk.dts26
-rw-r--r--dts/src/arm/am33xx-l4.dtsi4
-rw-r--r--dts/src/arm/rk3288-tinker.dtsi3
-rw-r--r--dts/src/arm/rk3288-veyron.dtsi2
-rw-r--r--dts/src/arm/rk3288.dtsi20
-rw-r--r--dts/src/arm/sama5d2-pinfunc.h2
-rw-r--r--dts/src/arm/ste-nomadik-nhk15.dts9
-rw-r--r--dts/src/arm64/altera/socfpga_stratix10.dtsi3
-rw-r--r--dts/src/arm64/rockchip/rk3328-roc-cc.dts4
-rw-r--r--dts/src/arm64/rockchip/rk3328-rock64.dts3
-rw-r--r--dts/src/arm64/rockchip/rk3328.dtsi58
-rw-r--r--dts/src/arm64/rockchip/rk3399-rock-pi-4.dts1
-rw-r--r--fs/nfs.c11
-rw-r--r--images/Makefile.layerscape21
-rw-r--r--images/Makefile.mvebu8
-rw-r--r--include/base64.h9
-rw-r--r--include/bbu.h4
-rw-r--r--include/blobgen.h58
-rw-r--r--include/common.h4
-rw-r--r--include/console.h3
-rw-r--r--include/console_countdown.h2
-rw-r--r--include/crypto.h27
-rw-r--r--include/crypto/des.h16
-rw-r--r--include/filetype.h2
-rw-r--r--include/linux/mtd/spi-nor.h230
-rw-r--r--include/linux/spi/spi-mem.h307
-rw-r--r--include/soc/fsl/fsl_ddr_sdram.h1
-rw-r--r--include/spi/spi.h95
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile2
-rw-r--r--lib/base64.c154
-rw-r--r--lib/blobgen.c223
-rw-r--r--scripts/Makefile1
-rw-r--r--scripts/Makefile.lib2
-rw-r--r--scripts/mips-relocs.c426
-rw-r--r--scripts/pblimage.c36
190 files changed, 7646 insertions, 1034 deletions
diff --git a/Documentation/boards/imx/embest-i.mx6s-riotboard.rst b/Documentation/boards/imx/embest-i.mx6s-riotboard.rst
new file mode 100644
index 0000000000..9157f53c2a
--- /dev/null
+++ b/Documentation/boards/imx/embest-i.mx6s-riotboard.rst
@@ -0,0 +1,133 @@
+Embest RIoTboard
+================
+
+RIoTboard Standard kit
+----------------------
+
+http://www.embest-tech.com/prod_view.aspx?TypeId=74&Id=345&FId=t3:74:3
+
+General Features:
+ * Product Dimensions: 75mm x 120mm
+ * Operating Temperature: 0 ~ 50℃
+ * Operating Humidity: 20% ~ 90% (non-condensing)
+ * DC-in:5V/4A
+
+Processor:
+ * i.MX 6Solo based on ARM Cortexâ„¢-A9
+ * 32 Kbyte L1 instruction buffer
+ * 32 Kbyte L1 data buffer
+ * Private counter and watchdog
+ * Cortex-A9 NEON MPE (media processing engine) coprocessor
+
+On-Board Memories:
+ * 4GByte eMMC
+ * 2*512MB DDR3 SDRAM
+
+One-Board Interfaces/Buttons:
+
+ * Audio input/output interfaces
+ * A LVDS interface
+ * A HDMI interface
+ * A LCD display interface (expansion interface)
+ * A digital camera interface
+ * A MIPI interface
+ * 4 TTL-level serial interfaces (one for debugging, the rest for expansion)
+ * 4 high-speed USB2.0 host interfaces (480Mbps)
+ * A high-speed USB2.0 OTG interface (480Mbps)
+ * A SD card slot
+ * A TF card slot
+ * A 10/100M/1Gbps RJ45 interface
+ * 2 I2C interfaces (expansion interfaces)
+ * 2 SPI interfaces (expansion interfaces)
+ * 3 PWM interfaces (expansion interfaces)
+ * A GPIO interface (expansion interfaces)
+ * A JTAG interface
+ * A boot-mode switch
+ * 4 LEDs (1 system LED, 2 custom LED, 1 open SDA LED)
+ * A reset button
+
+How to build barebox for Embest RIoTboard
+-----------------------------------------
+
+Using the default configuration:
+
+.. code-block:: sh
+
+ make ARCH=arm imx_v7_defconfig
+
+Build the binary image:
+
+.. code-block:: sh
+
+ make ARCH=arm CROSS_COMPILE=armv7compiler
+
+.. note:: replace ``armv7compiler`` with your ARM v7 cross compiler prefix,
+ e.g.: ``arm-linux-gnueabihf-``
+
+The resulting binary image to be flashed will be ``images/barebox-embest-imx6s-riotboard.img``.
+
+Replacing U-Boot with barebox
+-----------------------------
+
+ 1. Connect to the boards's UART (115200 8N1);
+
+ 2. Turn board's power on;
+
+ 3. Wait ``Hit any key to stop autoboot: 2`` prompt and press any key;
+
+ 4. Upload barebox image to the board via tftp and start it
+
+.. code-block:: none
+
+ MX6Solo RIoTboard U-Boot > setenv ethaddr d2:2d:ce:88:f4:f1
+ MX6Solo RIoTboard U-Boot > tftpboot 0x20800000 192.168.23.4:ore-barebox-riotboard
+ ----phy_addr=0x4, id= 0x4dd072
+ FEC: Link is Up 796d
+ Using FEC0 device
+ TFTP from server 192.168.23.4; our IP address is 192.168.1.103
+ Filename 'ore-barebox-riotboard'.
+ Load address: 0x20800000
+ Loading: ## Warning: gatewayip needed but not set
+ ## Warning: gatewayip needed but not set
+ ############################################
+ done
+ Bytes transferred = 637947 (9bbfb hex)
+ MX6Solo RIoTboard U-Boot > go 0x20800000
+
+ ## Starting application at 0x20800000 ...
+
+ barebox 2019.04.0-00151-g86762248de #391 Mon Apr 29 14:41:58 CEST 2019
+
+ Board: RIoTboard i.MX6S
+ detected i.MX6 Solo revision 1.1
+ i.MX reset reason POR (SRSR: 0x00000001)
+ mdio_bus: miibus0: probed
+ imx-usb 2184200.usb@2184200.of: USB EHCI 1.00
+ imx-esdhc 2194000.usdhc@2194000.of: registered as mmc1
+ imx-esdhc 2198000.usdhc@2198000.of: registered as mmc2
+ imx-esdhc 219c000.usdhc@219c000.of: registered as mmc3
+ imx-ipuv3 2400000.ipu@2400000.of: IPUv3H probed
+ imx-hdmi 120000.hdmi@120000.of: Detected HDMI controller 0x13:0x1a:0xa0:0xc1
+ netconsole: registered as netconsole-1
+ malloc space: 0x2fefb5e0 -> 0x4fdf6bbf (size 511 MiB)
+ mmc3: detected MMC card version 4.41
+ mmc3: registered mmc3.boot0
+ mmc3: registered mmc3.boot1
+ mmc3: registered mmc3
+ partition mmc3.3 not completely inside device mmc3
+ mmc3: Failed to register partition 3 on mmc3 (-22)
+ envfs: no envfs (magic mismatch) - envfs never written?
+ running /env/bin/init...
+
+ Hit m for menu or any key to stop autoboot: 2
+
+ type exit to get to the menu
+ barebox@RIoTboard i.MX6S:/
+..
+
+ 5. Install barebox to the eMMC
+
+.. code-block:: none
+
+ barebox@RIoTboard i.MX6S:/ cp /mnt/tftp/ore-barebox-riotboard /dev/mmc3.barebox
+..
diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst
index 07743df0cb..78ce24f9ed 100644
--- a/Documentation/user/state.rst
+++ b/Documentation/user/state.rst
@@ -21,7 +21,13 @@ RAUC_.
.. _RAUC: https://rauc.readthedocs.io/en/latest/
barebox itself uses a *state* driver to access the variables in the
-persistent memory. For the Linux run-time there is a userspace tool_ to do
+persistent memory.
+
+Currently there is only one implementation, enabled by
+``CONFIG_STATE_DRV=y``. Without driver, the state framework will silently
+fail and be non-functional.
+
+For the Linux run-time there is a userspace tool_ to do
the same.
.. _tool: https://git.pengutronix.de/cgit/tools/dt-utils/
diff --git a/Documentation/user/variables.rst b/Documentation/user/variables.rst
index ddfd485740..cb1296c06d 100644
--- a/Documentation/user/variables.rst
+++ b/Documentation/user/variables.rst
@@ -46,9 +46,11 @@ currently running barebox, while changing a nv variable changes the
behaviour persistently over reboots.
nv variables can be created or removed with the :ref:`command_nv`
-command. The nv variables are made persistent using the environment
-facilities of barebox, so a :ref:`command_saveenv` must be issued to store the
-actual values.
+command.
+
+The nv variables are made persistent using the environment facilities of
+barebox. They are saved automatically to the storage medium whenever barebox
+shuts down (that is, a :ref:`command_reset` is issued or a kernel is started).
examples:
diff --git a/Makefile b/Makefile
index 9aa8fd35bc..29347b6780 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
VERSION = 2019
-PATCHLEVEL = 04
+PATCHLEVEL = 05
SUBLEVEL = 0
EXTRAVERSION =
NAME = None
diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
index 91f17374c9..cbd6bd1ed6 100644
--- a/arch/arm/boards/Makefile
+++ b/arch/arm/boards/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_MACH_LENOVO_IX4_300D) += lenovo-ix4-300d/
obj-$(CONFIG_MACH_LUBBOCK) += lubbock/
obj-$(CONFIG_MACH_MAINSTONE) += mainstone/
obj-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += marvell-armada-xp-gp/
+obj-$(CONFIG_MACH_MARVELL_ARMADA_XP_DB) += marvell-armada-xp-db/
obj-$(CONFIG_MACH_MB7707) += module-mb7707/
obj-$(CONFIG_MACH_MIOA701) += mioa701/
obj-$(CONFIG_MACH_MX23EVK) += freescale-mx23-evk/
diff --git a/arch/arm/boards/ls1046ardb/lowlevel.c b/arch/arm/boards/ls1046ardb/lowlevel.c
index 6de16063a7..0c95fbb035 100644
--- a/arch/arm/boards/ls1046ardb/lowlevel.c
+++ b/arch/arm/boards/ls1046ardb/lowlevel.c
@@ -3,6 +3,7 @@
#include <common.h>
#include <debug_ll.h>
#include <ddr_spd.h>
+#include <image-metadata.h>
#include <platform_data/mmc-esdhc-imx.h>
#include <i2c/i2c-early.h>
#include <soc/fsl/fsl_ddr_sdram.h>
@@ -201,6 +202,8 @@ static noinline __noreturn void ls1046ardb_r_entry(unsigned long memsize)
debug_ll_init();
ls1046a_init_lowlevel();
+ IMD_USED_OF(fsl_ls1046a_rdb);
+
i2c = ls1046_i2c_init(IOMEM(LSCH2_I2C1_BASE_ADDR));
ret = spd_read_eeprom(i2c, i2c_fsl_xfer, 0x51, &spd_eeprom);
if (ret) {
diff --git a/arch/arm/boards/marvell-armada-xp-db/Makefile b/arch/arm/boards/marvell-armada-xp-db/Makefile
new file mode 100644
index 0000000000..01c7a259e9
--- /dev/null
+++ b/arch/arm/boards/marvell-armada-xp-db/Makefile
@@ -0,0 +1,2 @@
+obj-y += board.o
+lwl-y += lowlevel.o
diff --git a/arch/arm/boards/marvell-armada-xp-db/board.c b/arch/arm/boards/marvell-armada-xp-db/board.c
new file mode 100644
index 0000000000..4591c6bb65
--- /dev/null
+++ b/arch/arm/boards/marvell-armada-xp-db/board.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright
+ * (C) 2019 Sascha Hauer <s.hauer@pengutronix.de>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+
+static int pinctrl_init(void)
+{
+ if (!of_machine_is_compatible("marvell,axp-db"))
+ return 0;
+
+ /* IOMUX setup for the poor */
+ writel(0x11111111, 0xf1018000);
+ writel(0x22221111, 0xf1018004);
+ writel(0x22222222, 0xf1018008);
+ writel(0x11040000, 0xf101800c);
+ writel(0x11111111, 0xf1018010);
+ writel(0x04221130, 0xf1018014);
+ writel(0x11111113, 0xf1018018);
+ writel(0x11111101, 0xf101801c);
+
+ writel(0x7fffffe1, 0xf1020184);
+ writel(0x00010008, 0xf1020258);
+
+ return 0;
+}
+coredevice_initcall(pinctrl_init);
diff --git a/arch/arm/boards/marvell-armada-xp-db/kwbimage.cfg b/arch/arm/boards/marvell-armada-xp-db/kwbimage.cfg
new file mode 100644
index 0000000000..28e60e2f18
--- /dev/null
+++ b/arch/arm/boards/marvell-armada-xp-db/kwbimage.cfg
@@ -0,0 +1,3 @@
+VERSION 1
+BOOT_FROM uart
+BINARY ./binary.0 0000005b 00000000
diff --git a/arch/arm/boards/marvell-armada-xp-db/lowlevel.c b/arch/arm/boards/marvell-armada-xp-db/lowlevel.c
new file mode 100644
index 0000000000..4752bbf1b4
--- /dev/null
+++ b/arch/arm/boards/marvell-armada-xp-db/lowlevel.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright
+ * (C) 2019 Sascha Hauer <s.hauer@pengutronix.de>
+ */
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <asm/barebox-arm.h>
+#include <asm/barebox-arm-head.h>
+#include <mach/lowlevel.h>
+#include <io.h>
+
+extern char __dtb_armada_xp_db_bb_start[];
+
+ENTRY_FUNCTION(start_marvell_armada_xp_db, r0, r1, r2)
+{
+ void *fdt;
+ uint32_t reg;
+ void __iomem *base = mvebu_get_initial_int_reg_base();
+
+ arm_cpu_lowlevel_init();
+
+ /* enable L2 parity and ECC */
+#define L2_AUX_CONTRL_ADDRESS (base + 0x8104)
+ reg = readl(L2_AUX_CONTRL_ADDRESS);
+ reg |= 3 << 20;
+ writel(0x1a09ef00, L2_AUX_CONTRL_ADDRESS);
+
+ fdt = __dtb_armada_xp_db_bb_start + get_runtime_offset();
+
+ armada_370_xp_barebox_entry(fdt);
+}
diff --git a/arch/arm/boards/raspberry-pi/rpi-common.c b/arch/arm/boards/raspberry-pi/rpi-common.c
index bc877f853a..dd6bbd5bdd 100644
--- a/arch/arm/boards/raspberry-pi/rpi-common.c
+++ b/arch/arm/boards/raspberry-pi/rpi-common.c
@@ -319,6 +319,14 @@ static int rpi_clock_init(void)
clkdev_add_physbase(clk, 0x20300000, NULL);
clkdev_add_physbase(clk, 0x3f300000, NULL);
+ clk = rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_CORE,
+ "bcm2835_sdhost");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ clkdev_add_physbase(clk, 0x20202000, NULL);
+ clkdev_add_physbase(clk, 0x3f202000, NULL);
+
return 0;
}
postconsole_initcall(rpi_clock_init);
diff --git a/arch/arm/boards/tqmls1046a/board.c b/arch/arm/boards/tqmls1046a/board.c
index 5d6d5ad62c..8cc4d73de5 100644
--- a/arch/arm/boards/tqmls1046a/board.c
+++ b/arch/arm/boards/tqmls1046a/board.c
@@ -3,10 +3,15 @@
#include <common.h>
#include <init.h>
#include <envfs.h>
+#include <bbu.h>
+#include <bootsource.h>
#include <asm/memory.h>
#include <linux/sizes.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <soc/fsl/immap_lsch2.h>
+#include <mach/bbu.h>
+#include <mach/layerscape.h>
static int tqmls1046a_mem_init(void)
{
@@ -21,11 +26,39 @@ mem_initcall(tqmls1046a_mem_init);
static int tqmls1046a_postcore_init(void)
{
+ struct ccsr_scfg *scfg = IOMEM(LSCH2_SCFG_ADDR);
+ enum bootsource bootsource;
+ unsigned long sd_bbu_flags = 0, qspi_bbu_flags = 0;
+
if (!of_machine_is_compatible("tqc,tqmls1046a"))
return 0;
defaultenv_append_directory(defaultenv_tqmls1046a);
+ /* Configure iomux for i2c4 */
+ out_be32(&scfg->rcwpmuxcr0, 0x3300);
+
+ /* divide CGA1/CGA2 PLL by 24 to get QSPI interface clock */
+ out_be32(&scfg->qspi_cfg, 0x30100000);
+
+ bootsource = ls1046_bootsource_get();
+
+ switch (bootsource) {
+ case BOOTSOURCE_MMC:
+ of_device_enable_path("/chosen/environment-sd");
+ sd_bbu_flags = BBU_HANDLER_FLAG_DEFAULT;
+ break;
+ case BOOTSOURCE_SPI_NOR:
+ of_device_enable_path("/chosen/environment-qspi");
+ qspi_bbu_flags = BBU_HANDLER_FLAG_DEFAULT;
+ break;
+ default:
+ break;
+ }
+
+ ls1046a_bbu_mmc_register_handler("sd", "/dev/mmc0.barebox", sd_bbu_flags);
+ ls1046a_bbu_qspi_register_handler("qspi", "/dev/qspiflash0.barebox", qspi_bbu_flags);
+
return 0;
}
diff --git a/arch/arm/boards/tqmls1046a/lowlevel.c b/arch/arm/boards/tqmls1046a/lowlevel.c
index 044d6a418d..dc0e179694 100644
--- a/arch/arm/boards/tqmls1046a/lowlevel.c
+++ b/arch/arm/boards/tqmls1046a/lowlevel.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <debug_ll.h>
+#include <image-metadata.h>
#include <platform_data/mmc-esdhc-imx.h>
#include <soc/fsl/fsl_ddr_sdram.h>
#include <soc/fsl/immap_lsch2.h>
@@ -116,7 +117,7 @@ found:
DDR_CDR2_VREF_TRAIN_EN | DDR_CDR2_VREF_RANGE_2;
/* optimize cpo for erratum A-009942 */
- popts->cpo_sample = 0x61;
+ popts->cpo_sample = 0x48;
}
static struct dimm_params dimm_params[] = {
@@ -169,37 +170,97 @@ static struct fsl_ddr_controller ddrc[] = {
.erratum_A009942 = 1,
.chip_selects_per_ctrl = 4,
.board_options = ddr_board_options,
+ .fsl_ddr_config_reg = {
+ .cs[0].bnds = 0x0000007F,
+ .cs[0].config = 0x80010312,
+ .cs[0].config_2 = 0x00000000,
+ .cs[1].bnds = 0x00000000,
+ .cs[1].config = 0x00000000,
+ .cs[1].config_2 = 0x00000000,
+ .cs[2].bnds = 0x00000000,
+ .cs[2].config = 0x00000000,
+ .cs[2].config_2 = 0x00000000,
+ .cs[3].bnds = 0x00000000,
+ .cs[3].config = 0x00000000,
+ .cs[3].config_2 = 0x00000000,
+ .timing_cfg_3 = 0x020F1100,
+ .timing_cfg_0 = 0x77660008,
+ .timing_cfg_1 = 0xF1FCC265,
+ .timing_cfg_2 = 0x0059415E,
+ .ddr_sdram_cfg = 0x65000000,
+ .ddr_sdram_cfg_2 = 0x00401150,
+ .ddr_sdram_cfg_3 = 0x00000000,
+ .ddr_sdram_mode = 0x03010625,
+ .ddr_sdram_mode_2 = 0x00100200,
+ .ddr_sdram_mode_3 = 0x00010625,
+ .ddr_sdram_mode_4 = 0x00100200,
+ .ddr_sdram_mode_5 = 0x00010625,
+ .ddr_sdram_mode_6 = 0x00100200,
+ .ddr_sdram_mode_7 = 0x00010625,
+ .ddr_sdram_mode_8 = 0x00100200,
+ .ddr_sdram_mode_9 = 0x00000500,
+ .ddr_sdram_mode_10 = 0x04400000,
+ .ddr_sdram_mode_11 = 0x00000400,
+ .ddr_sdram_mode_12 = 0x04400000,
+ .ddr_sdram_mode_13 = 0x00000400,
+ .ddr_sdram_mode_14 = 0x04400000,
+ .ddr_sdram_mode_15 = 0x00000400,
+ .ddr_sdram_mode_16 = 0x04400000,
+ .ddr_sdram_interval = 0x0F3C0000,
+ .ddr_data_init = 0xDEADBEEF,
+ .ddr_sdram_clk_cntl = 0x02000000,
+ .ddr_init_addr = 0x00000000,
+ .ddr_init_ext_addr = 0x00000000,
+ .timing_cfg_4 = 0x00224002,
+ .timing_cfg_5 = 0x04401400,
+ .timing_cfg_6 = 0x00000000,
+ .timing_cfg_7 = 0x25500000,
+ .timing_cfg_8 = 0x03335A00,
+ .timing_cfg_9 = 0x00000000,
+ .ddr_zq_cntl = 0x8A090705,
+ .ddr_wrlvl_cntl = 0x86550609,
+ .ddr_wrlvl_cntl_2 = 0x09080806,
+ .ddr_wrlvl_cntl_3 = 0x06040409,
+ .ddr_sr_cntr = 0x00000000,
+ .ddr_sdram_rcw_1 = 0x00000000,
+ .ddr_sdram_rcw_2 = 0x00000000,
+ .ddr_sdram_rcw_3 = 0x00000000,
+ .ddr_cdr1 = 0x80080000,
+ .ddr_cdr2 = 0x000000C0,
+ .dq_map_0 = 0x00000000,
+ .dq_map_1 = 0x00000000,
+ .dq_map_2 = 0x00000000,
+ .dq_map_3 = 0x00000000,
+ .debug[28] = 0x00700046,
+ },
},
};
-static struct fsl_ddr_info ls1046a_info = {
- .num_ctrls = ARRAY_SIZE(ddrc),
- .c = ddrc,
-};
-
extern char __dtb_fsl_tqmls1046a_mbls10xxa_start[];
-static noinline __noreturn void tqmls1046a_r_entry(unsigned long memsize)
+static noinline __noreturn void tqmls1046a_r_entry(void)
{
unsigned long membase = LS1046A_DDR_SDRAM_BASE;
- if (get_pc() >= membase) {
- if (memsize + membase >= 0x100000000)
- memsize = 0x100000000 - membase;
-
+ if (get_pc() >= membase)
barebox_arm_entry(membase, 0x80000000,
__dtb_fsl_tqmls1046a_mbls10xxa_start);
- }
arm_cpu_lowlevel_init();
- debug_ll_init();
ls1046a_init_lowlevel();
- memsize = fsl_ddr_sdram(&ls1046a_info);
+ debug_ll_init();
+
+ udelay(500);
+ putc_ll('>');
+
+ IMD_USED_OF(fsl_tqmls1046a_mbls10xxa);
+
+ fsl_ddr_set_memctl_regs(&ddrc[0], 0);
ls1046a_errata_post_ddr();
- ls1046a_esdhc_start_image(memsize, 0, 0);
+ ls1046a_xload_start_image(0, 0, 0);
pr_err("Booting failed\n");
@@ -213,5 +274,5 @@ __noreturn void tqmls1046a_entry(unsigned long r0, unsigned long r1, unsigned lo
relocate_to_current_adr();
setup_c();
- tqmls1046a_r_entry(r0);
+ tqmls1046a_r_entry();
}
diff --git a/arch/arm/boards/tqmls1046a/tqmls1046a_pbi_sd.cfg b/arch/arm/boards/tqmls1046a/tqmls1046a_pbi.cfg
index 7ac1398123..0a04afa770 100644
--- a/arch/arm/boards/tqmls1046a/tqmls1046a_pbi_sd.cfg
+++ b/arch/arm/boards/tqmls1046a/tqmls1046a_pbi.cfg
@@ -1,3 +1,5 @@
+#Configure QSPI clock
+0957015c 40100000
#Configure Scratch register
09570600 00000000
09570604 10000000
diff --git a/arch/arm/boards/tqmls1046a/tqmls1046a_pbi_qspi.cfg b/arch/arm/boards/tqmls1046a/tqmls1046a_pbi_qspi.cfg
deleted file mode 100644
index 32865ca2d0..0000000000
--- a/arch/arm/boards/tqmls1046a/tqmls1046a_pbi_qspi.cfg
+++ /dev/null
@@ -1,33 +0,0 @@
-#Configure QSPI clock
-0957015c 40100000
-#Configure Scratch register
-09570600 00000000
-09570604 40010000
-#Disable CCI barrier tranaction
-09570178 0000e010
-09180000 00000008
-#USB PHY frequency sel
-09570418 0000009c
-0957041c 0000009c
-09570420 0000009c
-#Serdes SATA
-09eb1300 80104e20
-09eb08dc 00502880
-#PEX gen3 link (errata A-010477)
-09570158 00000300
-89400890 01048000
-89500890 01048000
-89600890 01048000
-#PEX gen3 equalization preset values (errata A-008851)
-894008bc 01000000
-89400154 47474747
-89400158 47474747
-894008bc 00000000
-895008bc 01000000
-89500154 47474747
-89500158 47474747
-895008bc 00000000
-896008bc 01000000
-89600154 47474747
-89600158 47474747
-896008bc 00000000
diff --git a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_emmc_3333_5559.cfg b/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_emmc_3333_5559.cfg
deleted file mode 100644
index 6c72d001c3..0000000000
--- a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_emmc_3333_5559.cfg
+++ /dev/null
@@ -1,84 +0,0 @@
-# RCW values
-# 0: 1 - SYS_PLL_CFG : 0 [0x0 / 0b00]
-# 2: 6 - SYS_PLL_RAT : 6 [0x6 / 0b00110]
-# 8: 9 - MEM_PLL_CFG : 0 [0x0 / 0b00]
-# 10: 15 - MEM_PLL_RAT : 20 [0x14 / 0b010100]
-# 24: 25 - CGA_PLL1_CFG : 0 [0x0 / 0b00]
-# 26: 31 - CGA_PLL1_RAT : 16 [0x10 / 0b010000]
-# 32: 33 - CGA_PLL2_CFG : 0 [0x0 / 0b00]
-# 34: 39 - CGA_PLL2_RAT : 14 [0xe / 0b001110]
-# 96: 99 - C1_PLL_SEL : 0 [0x0 / 0b0000]
-# 128:143 - SRDS_PRTCL_S1 : 13107 [0x3333 / 0b0011001100110011]
-# 144:159 - SRDS_PRTCL_S2 : 21849 [0x5559 / 0b0101010101011001]
-# 160:161 - SRDS_PLL_REF_CLK_SEL_S1 : 3 [0x3 / 0b11]
-# 162:163 - SRDS_PLL_REF_CLK_SEL_S2 : 3 [0x3 / 0b11]
-# 168:169 - SRDS_PLL_PD_S1 : 0 [0x0 / 0b00]
-# 170:171 - SRDS_PLL_PD_S2 : 0 [0x0 / 0b00]
-# 176:177 - SRDS_DIV_PEX_S1 : 1 [0x1 / 0b01]
-# 178:179 - SRDS_DIV_PEX_S2 : 1 [0x1 / 0b01]
-# 186:187 - DDR_REFCLK_SEL : 0 [0x0 / 0b00]
-# 188:188 - SRDS_REFCLK_SEL_S1 : 0 [0x0 / 0b0]
-# 189:189 - SRDS_REFCLK_SEL_S2 : 0 [0x0 / 0b0]
-# 190:191 - DDR_FDBK_MULT : 2 [0x2 / 0b10]
-# 192:195 - PBI_SRC : 6 [0x6 / 0b0110]
-# 201:201 - BOOT_HO : 0 [0x0 / 0b0]
-# 202:202 - SB_EN : 0 [0x0 / 0b0]
-# 203:211 - IFC_MODE : 64 [0x40 / 0b001000000]
-# 224:226 - HWA_CGA_M1_CLK_SEL : 6 [0x6 / 0b110]
-# 230:231 - DRAM_LAT : 1 [0x1 / 0b01]
-# 232:232 - DDR_RATE : 0 [0x0 / 0b0]
-# 234:234 - DDR_RSV0 : 0 [0x0 / 0b0]
-# 242:242 - SYS_PLL_SPD : 0 [0x0 / 0b0]
-# 243:243 - MEM_PLL_SPD : 0 [0x0 / 0b0]
-# 244:244 - CGA_PLL1_SPD : 0 [0x0 / 0b0]
-# 245:245 - CGA_PLL2_SPD : 0 [0x0 / 0b0]
-# 264:266 - HOST_AGT_PEX : 0 [0x0 / 0b000]
-# 288:295 - GP_INFO1 : 0 [0x00 / 0b00000000]
-# 299:319 - GP_INFO2 : 0 [0x00000 / 0b000000000000000000000]
-# 354:356 - UART_EXT : 0 [0x0 / 0b000]
-# 357:359 - IRQ_EXT : 0 [0x0 / 0b000]
-# 360:362 - SPI_EXT : 0 [0x0 / 0b000]
-# 363:365 - SDHC_EXT : 0 [0x0 / 0b000]
-# 366:368 - UART_BASE : 5 [0x5 / 0b101]
-# 369:369 - ASLEEP : 0 [0x0 / 0b0]
-# 370:370 - RTC : 0 [0x0 / 0b0]
-# 371:371 - SDHC_BASE : 0 [0x0 / 0b0]
-# 372:372 - IRQ_OUT : 1 [0x1 / 0b1]
-# 373:381 - IRQ_BASE : 0 [0x00 / 0b000000000]
-# 382:383 - SPI_BASE : 0 [0x0 / 0b00]
-# 384:386 - IFC_GRP_A_EXT : 1 [0x1 / 0b001]
-# 393:395 - IFC_GRP_D_EXT : 0 [0x0 / 0b000]
-# 396:398 - IFC_GRP_E1_EXT : 0 [0x0 / 0b000]
-# 399:401 - IFC_GRP_F_EXT : 1 [0x1 / 0b001]
-# 405:405 - IFC_GRP_E1_BASE : 0 [0x0 / 0b0]
-# 407:407 - IFC_GRP_D_BASE : 0 [0x0 / 0b0]
-# 412:413 - IFC_GRP_A_BASE : 0 [0x0 / 0b00]
-# 415:415 - IFC_A_22_24 : 0 [0x0 / 0b0]
-# 416:418 - EC1 : 0 [0x0 / 0b000]
-# 419:421 - EC2 : 0 [0x0 / 0b000]
-# 422:423 - LVDD_VSEL : 1 [0x1 / 0b01]
-# 424:424 - I2C_IPGCLK_SEL : 0 [0x0 / 0b0]
-# 425:425 - EM1 : 0 [0x0 / 0b0]
-# 426:426 - EM2 : 0 [0x0 / 0b0]
-# 427:427 - EMI2_DMODE : 1 [0x1 / 0b1]
-# 428:428 - EMI2_CMODE : 0 [0x0 / 0b0]
-# 429:429 - USB_DRVVBUS : 0 [0x0 / 0b0]
-# 430:430 - USB_PWRFAULT : 0 [0x0 / 0b0]
-# 433:434 - TVDD_VSEL : 1 [0x1 / 0b01]
-# 435:436 - DVDD_VSEL : 2 [0x2 / 0b10]
-# 438:438 - EMI1_DMODE : 1 [0x1 / 0b1]
-# 439:440 - EVDD_VSEL : 0 [0x0 / 0b00]
-# 441:443 - IIC2_BASE : 0 [0x0 / 0b000]
-# 444:444 - EMI1_CMODE : 0 [0x0 / 0b0]
-# 445:447 - IIC2_EXT : 2 [0x2 / 0b010]
-# 472:481 - SYSCLK_FREQ : 600 [0x258 / 0b1001011000]
-# 509:511 - HWA_CGA_M2_CLK_SEL : 1 [0x1 / 0b001]
-
-
-#PBL preamble and RCW header
-aa55aa55 01ee0100
-# RCW
-0c140010 0e000000 00000000 00000000
-33335559 f0005002 60040000 c1000000
-00000000 00000000 00000000 00028800
-20004000 01103202 00000096 00000001
diff --git a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg b/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg
index 395c75c7d0..2df229c56c 100644
--- a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg
+++ b/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg
@@ -8,7 +8,7 @@
# 32: 33 - CGA_PLL2_CFG : 0 [0x0 / 0b00]
# 34: 39 - CGA_PLL2_RAT : 14 [0xe / 0b001110]
# 96: 99 - C1_PLL_SEL : 0 [0x0 / 0b0000]
-# 128:143 - SRDS_PRTCL_S1 : 13107 [0x3333 / 0b0011001100110011]
+# 128:143 - SRDS_PRTCL_S1 : 4403 [0x1133 / 0b0001000100110011]
# 144:159 - SRDS_PRTCL_S2 : 21849 [0x5559 / 0b0101010101011001]
# 160:161 - SRDS_PLL_REF_CLK_SEL_S1 : 3 [0x3 / 0b11]
# 162:163 - SRDS_PLL_REF_CLK_SEL_S2 : 3 [0x3 / 0b11]
@@ -39,7 +39,7 @@
# 357:359 - IRQ_EXT : 0 [0x0 / 0b000]
# 360:362 - SPI_EXT : 0 [0x0 / 0b000]
# 363:365 - SDHC_EXT : 0 [0x0 / 0b000]
-# 366:368 - UART_BASE : 5 [0x5 / 0b101]
+# 366:368 - UART_BASE : 6 [0x6 / 0b110]
# 369:369 - ASLEEP : 0 [0x0 / 0b0]
# 370:370 - RTC : 0 [0x0 / 0b0]
# 371:371 - SDHC_BASE : 0 [0x0 / 0b0]
@@ -79,6 +79,6 @@
aa55aa55 01ee0100
# RCW
0c140010 0e000000 00000000 00000000
-33335559 f0005002 40025000 c1000000
-00000000 00000000 00000000 00028800
+11335559 f0005002 40025000 c1000000
+00000000 00000000 00000000 00030800
20004000 01103202 00000096 00000001
diff --git a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg b/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg
index 4ef6d576ed..72ab1cd7d7 100644
--- a/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg
+++ b/arch/arm/boards/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg
@@ -8,7 +8,7 @@
# 32: 33 - CGA_PLL2_CFG : 0 [0x0 / 0b00]
# 34: 39 - CGA_PLL2_RAT : 14 [0xe / 0b001110]
# 96: 99 - C1_PLL_SEL : 0 [0x0 / 0b0000]
-# 128:143 - SRDS_PRTCL_S1 : 13107 [0x3333 / 0b0011001100110011]
+# 128:143 - SRDS_PRTCL_S1 : 4403 [0x1133 / 0b0001000100110011]
# 144:159 - SRDS_PRTCL_S2 : 21849 [0x5559 / 0b0101010101011001]
# 160:161 - SRDS_PLL_REF_CLK_SEL_S1 : 3 [0x3 / 0b11]
# 162:163 - SRDS_PLL_REF_CLK_SEL_S2 : 3 [0x3 / 0b11]
@@ -39,7 +39,7 @@
# 357:359 - IRQ_EXT : 0 [0x0 / 0b000]
# 360:362 - SPI_EXT : 0 [0x0 / 0b000]
# 363:365 - SDHC_EXT : 0 [0x0 / 0b000]
-# 366:368 - UART_BASE : 5 [0x5 / 0b101]
+# 366:368 - UART_BASE : 6 [0x6 / 0b110]
# 369:369 - ASLEEP : 0 [0x0 / 0b0]
# 370:370 - RTC : 0 [0x0 / 0b0]
# 371:371 - SDHC_BASE : 0 [0x0 / 0b0]
@@ -67,10 +67,10 @@
# 433:434 - TVDD_VSEL : 1 [0x1 / 0b01]
# 435:436 - DVDD_VSEL : 2 [0x2 / 0b10]
# 438:438 - EMI1_DMODE : 1 [0x1 / 0b1]
-# 439:440 - EVDD_VSEL : 2 [0x2 / 0b10]
+# 439:440 - EVDD_VSEL : 0 [0x0 / 0b00]
# 441:443 - IIC2_BASE : 0 [0x0 / 0b000]
# 444:444 - EMI1_CMODE : 0 [0x0 / 0b0]
-# 445:447 - IIC2_EXT : 1 [0x1 / 0b001]
+# 445:447 - IIC2_EXT : 2 [0x2 / 0b010]
# 472:481 - SYSCLK_FREQ : 600 [0x258 / 0b1001011000]
# 509:511 - HWA_CGA_M2_CLK_SEL : 1 [0x1 / 0b001]
@@ -79,6 +79,6 @@
aa55aa55 01ee0100
# RCW
0c140010 0e000000 00000000 00000000
-33335559 f0005002 60040000 c1000000
-00000000 00000000 00000000 00028800
-20004000 01103301 00000096 00000001
+11335559 f0005002 60040000 c1000000
+00000000 00000000 00000000 00030800
+20004000 01103202 00000096 00000001
diff --git a/arch/arm/boards/zii-imx51-rdu1/Makefile b/arch/arm/boards/zii-imx51-rdu1/Makefile
index 01c7a259e9..604b3621be 100644
--- a/arch/arm/boards/zii-imx51-rdu1/Makefile
+++ b/arch/arm/boards/zii-imx51-rdu1/Makefile
@@ -1,2 +1,3 @@
obj-y += board.o
+CFLAGS_pbl-lowlevel.o := -fno-tree-switch-conversion -fno-jump-tables
lwl-y += lowlevel.o
diff --git a/arch/arm/boards/zii-imx51-rdu1/lowlevel.c b/arch/arm/boards/zii-imx51-rdu1/lowlevel.c
index 849c5624c5..da05b0564c 100644
--- a/arch/arm/boards/zii-imx51-rdu1/lowlevel.c
+++ b/arch/arm/boards/zii-imx51-rdu1/lowlevel.c
@@ -84,13 +84,6 @@ ENTRY_FUNCTION(start_imx51_zii_rdu1, r0, r1, r2)
switch (system_type) {
default:
- /*
- * see similar code in
- * arch/arm/boards/zii-vf610-dev/lowlevel.c for
- * reasoning for placing barrier() below.
- */
- barrier();
-
if (IS_ENABLED(CONFIG_DEBUG_LL)) {
relocate_to_current_adr();
setup_c();
diff --git a/arch/arm/boards/zii-imx8mq-dev/lowlevel.c b/arch/arm/boards/zii-imx8mq-dev/lowlevel.c
index 0fd2ddfca5..33c007e05d 100644
--- a/arch/arm/boards/zii-imx8mq-dev/lowlevel.c
+++ b/arch/arm/boards/zii-imx8mq-dev/lowlevel.c
@@ -179,13 +179,6 @@ ENTRY_FUNCTION(start_zii_imx8mq_dev, r0, r1, r2)
switch (system_type) {
default:
- /*
- * see similar code in
- * arch/arm/boards/zii-vf610-dev/lowlevel.c for
- * reasoning for placing barrier() below.
- */
- barrier();
-
if (IS_ENABLED(CONFIG_DEBUG_LL)) {
relocate_to_current_adr();
setup_c();
diff --git a/arch/arm/boards/zii-vf610-dev/Makefile b/arch/arm/boards/zii-vf610-dev/Makefile
index 1297d815e3..3c3a3f2387 100644
--- a/arch/arm/boards/zii-vf610-dev/Makefile
+++ b/arch/arm/boards/zii-vf610-dev/Makefile
@@ -1,3 +1,4 @@
obj-y += board.o
+CFLAGS_pbl-lowlevel.o := -fno-tree-switch-conversion -fno-jump-tables
lwl-y += lowlevel.o
bbenv-y += defaultenv-zii-vf610-dev
diff --git a/arch/arm/boards/zii-vf610-dev/board.c b/arch/arm/boards/zii-vf610-dev/board.c
index 1296f70317..90d4535684 100644
--- a/arch/arm/boards/zii-vf610-dev/board.c
+++ b/arch/arm/boards/zii-vf610-dev/board.c
@@ -122,6 +122,7 @@ static int zii_vf610_dev_set_hostname(void)
const char *compatible;
const char *hostname;
} boards[] = {
+ { "zii,vf610dtu", "dtu" },
{ "zii,vf610spu3", "spu3" },
{ "zii,vf610spb4", "spb4" },
{ "zii,vf610cfu1", "cfu1" },
@@ -170,7 +171,8 @@ static int zii_vf610_register_emmc_bbu(void)
if (!of_machine_is_compatible("zii,vf610spu3") &&
!of_machine_is_compatible("zii,vf610cfu1") &&
- !of_machine_is_compatible("zii,vf610spb4"))
+ !of_machine_is_compatible("zii,vf610spb4") &&
+ !of_machine_is_compatible("zii,vf610dtu"))
return 0;
ret = vf610_bbu_internal_mmcboot_register_handler("eMMC",
diff --git a/arch/arm/boards/zii-vf610-dev/lowlevel.c b/arch/arm/boards/zii-vf610-dev/lowlevel.c
index 79588ac381..b320fbc0cf 100644
--- a/arch/arm/boards/zii-vf610-dev/lowlevel.c
+++ b/arch/arm/boards/zii-vf610-dev/lowlevel.c
@@ -42,6 +42,7 @@ enum zii_platform_vf610_type {
ZII_PLATFORM_VF610_CFU1 = 0x04,
ZII_PLATFORM_VF610_DEV_REV_C = 0x05,
ZII_PLATFORM_VF610_SPB4 = 0x06,
+ ZII_PLATFORM_VF610_SSMB_DTU = 0x07,
};
static unsigned int get_system_type(void)
@@ -79,6 +80,7 @@ extern char __dtb_vf610_zii_dev_rev_c_start[];
extern char __dtb_vf610_zii_cfu1_start[];
extern char __dtb_vf610_zii_ssmb_spu3_start[];
extern char __dtb_vf610_zii_scu4_aib_start[];
+extern char __dtb_vf610_zii_ssmb_dtu_start[];
extern char __dtb_vf610_zii_spb4_start[];
ENTRY_FUNCTION(start_zii_vf610_dev, r0, r1, r2)
@@ -93,23 +95,6 @@ ENTRY_FUNCTION(start_zii_vf610_dev, r0, r1, r2)
switch (system_type) {
default:
- /*
- * GCC can be smart enough to, when DEBUG_LL is
- * disabled, reduce this switch statement to a LUT
- * fetch. Unfortunately here, this early in the boot
- * process before any relocation/address fixups could
- * happen, the address of that LUT used by the code is
- * incorrect and any access to it would result in
- * bogus values.
- *
- * Adding the following barrier() statement seem to
- * force the compiler to always translate this block
- * to a sequence of consecutive checks and jumps with
- * relative fetches, which should work with or without
- * relocation/fixups.
- */
- barrier();
-
if (IS_ENABLED(CONFIG_DEBUG_LL)) {
relocate_to_current_adr();
setup_c();
@@ -137,6 +122,9 @@ ENTRY_FUNCTION(start_zii_vf610_dev, r0, r1, r2)
case ZII_PLATFORM_VF610_SPB4:
fdt = __dtb_vf610_zii_spb4_start;
break;
+ case ZII_PLATFORM_VF610_SSMB_DTU:
+ fdt = __dtb_vf610_zii_ssmb_dtu_start;
+ break;
}
vf610_barebox_entry(fdt + get_runtime_offset());
diff --git a/arch/arm/configs/layerscape_defconfig b/arch/arm/configs/layerscape_defconfig
index dadbcc214c..b81f375415 100644
--- a/arch/arm/configs/layerscape_defconfig
+++ b/arch/arm/configs/layerscape_defconfig
@@ -22,7 +22,6 @@ CONFIG_PBL_CONSOLE=y
CONFIG_PARTITION_DISK_EFI=y
CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
CONFIG_RESET_SOURCE=y
-CONFIG_DEBUG_LL=y
CONFIG_CMD_DMESG=y
CONFIG_LONGHELP=y
CONFIG_CMD_IOMEM=y
@@ -85,8 +84,13 @@ CONFIG_DRIVER_NET_FSL_FMAN=y
CONFIG_DP83867_PHY=y
CONFIG_REALTEK_PHY=y
CONFIG_NET_DSA_MV88E6XXX=y
+CONFIG_DRIVER_SPI_FSL_QUADSPI=y
CONFIG_I2C=y
CONFIG_I2C_IMX=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_MTD=y
+CONFIG_MTD_M25P80=y
CONFIG_MCI=y
CONFIG_MCI_MMC_BOOT_PARTITIONS=y
CONFIG_MCI_IMX_ESDHC=y
@@ -94,10 +98,12 @@ CONFIG_LED=y
CONFIG_LED_GPIO=y
CONFIG_LED_GPIO_OF=y
CONFIG_LED_TRIGGERS=y
+CONFIG_LED_PCA955X=y
CONFIG_EEPROM_AT25=y
CONFIG_EEPROM_AT24=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_IMX=y
+CONFIG_GPIO_PCA953X=y
CONFIG_NVMEM=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED=y
diff --git a/arch/arm/configs/mvebu_defconfig b/arch/arm/configs/mvebu_defconfig
index cfbccb5a14..c830cf2f09 100644
--- a/arch/arm/configs/mvebu_defconfig
+++ b/arch/arm/configs/mvebu_defconfig
@@ -2,8 +2,11 @@ CONFIG_ARCH_MVEBU=y
CONFIG_MACH_GLOBALSCALE_MIRABOX=y
CONFIG_MACH_NETGEAR_RN104=y
CONFIG_MACH_LENOVO_IX4_300D=y
+CONFIG_MACH_MARVELL_ARMADA_XP_DB=y
CONFIG_MACH_MARVELL_ARMADA_XP_GP=y
+CONFIG_MACH_NETGEAR_RN2120=y
CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3=y
+CONFIG_MACH_TURRIS_OMNIA=y
CONFIG_MACH_SOLIDRUN_CUBOX=y
CONFIG_MACH_GLOBALSCALE_GURUPLUG=y
CONFIG_MACH_PLATHOME_OPENBLOCKS_A6=y
@@ -11,7 +14,6 @@ CONFIG_MACH_USI_TOPKICK=y
CONFIG_AEABI=y
CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
CONFIG_MMU=y
-CONFIG_TEXT_BASE=0x0
CONFIG_MALLOC_SIZE=0x0
CONFIG_MALLOC_TLSF=y
CONFIG_KALLSYMS=y
@@ -26,48 +28,43 @@ CONFIG_BOOTM_INITRD=y
CONFIG_BOOTM_OFTREE=y
CONFIG_BOOTM_OFTREE_UIMAGE=y
CONFIG_BLSPEC=y
-CONFIG_FLEXIBLE_BOOTARGS=y
CONFIG_IMD_TARGET=y
CONFIG_CONSOLE_ACTIVATE_NONE=y
+CONFIG_CONSOLE_ALLOW_COLOR=y
+CONFIG_PBL_CONSOLE=y
CONFIG_PARTITION_DISK_EFI=y
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
+CONFIG_CMD_DMESG=y
CONFIG_LONGHELP=y
CONFIG_CMD_IOMEM=y
CONFIG_CMD_IMD=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_ARM_MMUINFO=y
-CONFIG_CMD_BOOT=y
CONFIG_CMD_GO=y
CONFIG_CMD_LOADS=y
CONFIG_CMD_LOADY=y
CONFIG_CMD_RESET=y
CONFIG_CMD_UIMAGE=y
CONFIG_CMD_PARTITION=y
-CONFIG_CMD_AUTOMOUNT=y
CONFIG_CMD_EXPORT=y
-CONFIG_CMD_GLOBAL=y
CONFIG_CMD_LOADENV=y
CONFIG_CMD_PRINTENV=y
CONFIG_CMD_MAGICVAR=y
CONFIG_CMD_MAGICVAR_HELP=y
CONFIG_CMD_SAVEENV=y
-CONFIG_CMD_BASENAME=y
-CONFIG_CMD_DIRNAME=y
CONFIG_CMD_FILETYPE=y
CONFIG_CMD_LN=y
CONFIG_CMD_MD5SUM=y
-CONFIG_CMD_READLINK=y
CONFIG_CMD_SHA1SUM=y
CONFIG_CMD_SHA224SUM=y
CONFIG_CMD_SHA256SUM=y
CONFIG_CMD_UNCOMPRESS=y
-CONFIG_CMD_GETOPT=y
CONFIG_CMD_LET=y
CONFIG_CMD_MSLEEP=y
CONFIG_CMD_READF=y
CONFIG_CMD_SLEEP=y
CONFIG_CMD_DHCP=y
-CONFIG_CMD_HOST=y
-CONFIG_NET_CMD_IFUP=y
+CONFIG_CMD_MIITOOL=y
CONFIG_CMD_PING=y
CONFIG_CMD_ECHO_E=y
CONFIG_CMD_EDIT=y
@@ -80,6 +77,8 @@ CONFIG_CMD_CRC=y
CONFIG_CMD_CRC_CMP=y
CONFIG_CMD_CLK=y
CONFIG_CMD_DETECT=y
+CONFIG_CMD_FLASH=y
+CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_LED=y
CONFIG_CMD_SPI=y
diff --git a/arch/arm/configs/rpi_defconfig b/arch/arm/configs/rpi_defconfig
index f167625c96..0ae0c42b12 100644
--- a/arch/arm/configs/rpi_defconfig
+++ b/arch/arm/configs/rpi_defconfig
@@ -66,12 +66,12 @@ CONFIG_SERIAL_AMBA_PL011=y
CONFIG_DRIVER_SERIAL_NS16550=y
CONFIG_MCI=y
CONFIG_MCI_BCM283X=y
+CONFIG_MCI_BCM283X_SDHOST=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
CONFIG_LED_TRIGGERS=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_BCM2835=y
-CONFIG_PINCTRL=y
CONFIG_PINCTRL_BCM283X=y
CONFIG_REGULATOR=y
CONFIG_FS_EXT4=y
diff --git a/arch/arm/cpu/Kconfig b/arch/arm/cpu/Kconfig
index 2577103293..f8abbccfca 100644
--- a/arch/arm/cpu/Kconfig
+++ b/arch/arm/cpu/Kconfig
@@ -86,6 +86,7 @@ config CPU_V8
select CPU_64v8
select CPU_SUPPORTS_64BIT_KERNEL
select ARM_EXCEPTIONS
+ select GENERIC_FIND_NEXT_BIT
config CPU_XSC3
bool
diff --git a/arch/arm/cpu/cache-armv7.S b/arch/arm/cpu/cache-armv7.S
index 7a1c5c0189..6a8aff8bb1 100644
--- a/arch/arm/cpu/cache-armv7.S
+++ b/arch/arm/cpu/cache-armv7.S
@@ -83,7 +83,10 @@ hierarchical:
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
beq finished @ if loc is 0, then no need to clean
- mov r12, #0 @ start clean at cache level 0
+ cmp r8, #0
+THUMB( ite eq )
+ moveq r12, #0
+ subne r12, r3, #2 @ start invalidate at outmost cache level
loop1:
add r2, r12, r12, lsr #1 @ work out 3x current cache level
mov r1, r0, lsr r2 @ extract cache type bits from clidr
@@ -118,8 +121,16 @@ THUMB( ite eq )
subs r7, r7, #1 @ decrement the index
bge loop2
skip:
+ cmp r8, #0
+ bne inval_check
add r12, r12, #2 @ increment cache number
cmp r3, r12
+ b loop_end_check
+inval_check:
+ cmp r12, #0
+ sub r12, r12, #2 @ decrement cache number
+loop_end_check:
+ dsb @ work-around Cortex-A7 erratum 814220
bgt loop1
finished:
ldmfd sp!, {r4-r11}
diff --git a/arch/arm/cpu/lowlevel_64.S b/arch/arm/cpu/lowlevel_64.S
index af1cd8b5bc..6a23132ed1 100644
--- a/arch/arm/cpu/lowlevel_64.S
+++ b/arch/arm/cpu/lowlevel_64.S
@@ -12,6 +12,13 @@ ENTRY(arm_cpu_lowlevel_init)
orr x0, x0, #(1 << 10) /* 64-bit EL2 */
msr scr_el3, x0
msr cptr_el3, xzr
+
+ mrs x0, sctlr_el3
+ ldr x1, =SCTLR_ELx_FLAGS
+ bic x0, x0, x1
+ msr sctlr_el3, x0
+ isb
+
b done
2:
diff --git a/arch/arm/cpu/mmu-early.c b/arch/arm/cpu/mmu-early.c
index d39a03ed95..2f5876fc46 100644
--- a/arch/arm/cpu/mmu-early.c
+++ b/arch/arm/cpu/mmu-early.c
@@ -5,17 +5,20 @@
#include <asm/memory.h>
#include <asm/system.h>
#include <asm/cache.h>
+#include <asm-generic/sections.h>
#include "mmu.h"
static uint32_t *ttb;
-static void map_cachable(unsigned long start, unsigned long size)
+static inline void map_region(unsigned long start, unsigned long size,
+ uint64_t flags)
+
{
start = ALIGN_DOWN(start, SZ_1M);
size = ALIGN(size, SZ_1M);
- create_sections(ttb, start, start + size - 1, PMD_SECT_DEF_CACHED);
+ create_sections(ttb, start, start + size - 1, flags);
}
void mmu_early_enable(unsigned long membase, unsigned long memsize,
@@ -28,9 +31,27 @@ void mmu_early_enable(unsigned long membase, unsigned long memsize,
set_ttbr(ttb);
set_domain(DOMAIN_MANAGER);
+ /*
+ * This marks the whole address space as uncachable as well as
+ * unexecutable if possible
+ */
create_flat_mapping(ttb);
- map_cachable(membase, memsize);
+ /*
+ * There can be SoCs that have a section shared between device memory
+ * and the on-chip RAM hosting the PBL. Thus mark this section
+ * uncachable, but executable.
+ * On such SoCs, executing from OCRAM could cause the instruction
+ * prefetcher to speculatively access that device memory, triggering
+ * potential errant behavior.
+ *
+ * If your SoC has such a memory layout, you should rewrite the code
+ * here to map the OCRAM page-wise.
+ */
+ map_region((unsigned long)_stext, _etext - _stext, PMD_SECT_DEF_UNCACHED);
+
+ /* maps main memory as cachable */
+ map_region(membase, memsize, PMD_SECT_DEF_CACHED);
__mmu_cache_on();
}
diff --git a/arch/arm/cpu/mmu.c b/arch/arm/cpu/mmu.c
index 29816ad563..123e19e9e5 100644
--- a/arch/arm/cpu/mmu.c
+++ b/arch/arm/cpu/mmu.c
@@ -34,7 +34,6 @@
#include "mmu.h"
-#define PMD_SECT_DEF_CACHED (PMD_SECT_WB | PMD_SECT_DEF_UNCACHED)
#define PTRS_PER_PTE (PGDIR_SIZE / PAGE_SIZE)
#define ARCH_MAP_WRITECOMBINE ((unsigned)-1)
@@ -58,11 +57,13 @@ static inline void tlb_invalidate(void)
}
#define PTE_FLAGS_CACHED_V7 (PTE_EXT_TEX(1) | PTE_BUFFERABLE | PTE_CACHEABLE)
-#define PTE_FLAGS_WC_V7 PTE_EXT_TEX(1)
-#define PTE_FLAGS_UNCACHED_V7 (0)
+#define PTE_FLAGS_WC_V7 (PTE_EXT_TEX(1) | PTE_EXT_XN)
+#define PTE_FLAGS_UNCACHED_V7 PTE_EXT_XN
#define PTE_FLAGS_CACHED_V4 (PTE_SMALL_AP_UNO_SRW | PTE_BUFFERABLE | PTE_CACHEABLE)
#define PTE_FLAGS_UNCACHED_V4 PTE_SMALL_AP_UNO_SRW
-#define PGD_FLAGS_WC_V7 (PMD_SECT_TEX(1) | PMD_TYPE_SECT | PMD_SECT_BUFFERABLE)
+#define PGD_FLAGS_WC_V7 (PMD_SECT_TEX(1) | PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | \
+ PMD_SECT_XN)
+#define PGD_FLAGS_UNCACHED_V7 (PMD_SECT_DEF_UNCACHED | PMD_SECT_XN)
/*
* PTE flags to set cached and uncached areas.
@@ -72,6 +73,7 @@ static uint32_t pte_flags_cached;
static uint32_t pte_flags_wc;
static uint32_t pte_flags_uncached;
static uint32_t pgd_flags_wc;
+static uint32_t pgd_flags_uncached;
#define PTE_MASK ((1 << 12) - 1)
@@ -164,7 +166,7 @@ int arch_remap_range(void *start, size_t size, unsigned flags)
break;
case MAP_UNCACHED:
pte_flags = pte_flags_uncached;
- pgd_flags = PMD_SECT_DEF_UNCACHED;
+ pgd_flags = pgd_flags_uncached;
break;
case ARCH_MAP_WRITECOMBINE:
pte_flags = pte_flags_wc;
@@ -247,7 +249,7 @@ void *map_io_sections(unsigned long phys, void *_start, size_t size)
unsigned long start = (unsigned long)_start, sec;
for (sec = start; sec < start + size; sec += PGDIR_SIZE, phys += PGDIR_SIZE)
- ttb[pgd_index(sec)] = phys | PMD_SECT_DEF_UNCACHED;
+ ttb[pgd_index(sec)] = phys | pgd_flags_uncached;
dma_flush_range(ttb, 0x4000);
tlb_invalidate();
@@ -411,11 +413,13 @@ void __mmu_init(bool mmu_on)
pte_flags_cached = PTE_FLAGS_CACHED_V7;
pte_flags_wc = PTE_FLAGS_WC_V7;
pgd_flags_wc = PGD_FLAGS_WC_V7;
+ pgd_flags_uncached = PGD_FLAGS_UNCACHED_V7;
pte_flags_uncached = PTE_FLAGS_UNCACHED_V7;
} else {
pte_flags_cached = PTE_FLAGS_CACHED_V4;
pte_flags_wc = PTE_FLAGS_UNCACHED_V4;
pgd_flags_wc = PMD_SECT_DEF_UNCACHED;
+ pgd_flags_uncached = PMD_SECT_DEF_UNCACHED;
pte_flags_uncached = PTE_FLAGS_UNCACHED_V4;
}
diff --git a/arch/arm/cpu/mmu.h b/arch/arm/cpu/mmu.h
index 338728aacd..c911ee209f 100644
--- a/arch/arm/cpu/mmu.h
+++ b/arch/arm/cpu/mmu.h
@@ -3,6 +3,7 @@
#include <asm/pgtable.h>
#include <linux/sizes.h>
+#include <asm/system_info.h>
#include "mmu-common.h"
@@ -62,8 +63,13 @@ create_sections(uint32_t *ttb, unsigned long first,
static inline void create_flat_mapping(uint32_t *ttb)
{
+ unsigned int flags = PMD_SECT_DEF_UNCACHED;
+
+ if (cpu_architecture() >= CPU_ARCH_ARMv7)
+ flags |= PMD_SECT_XN;
+
/* create a flat mapping using 1MiB sections */
- create_sections(ttb, 0, 0xffffffff, PMD_SECT_DEF_UNCACHED);
+ create_sections(ttb, 0, 0xffffffff, flags);
}
#endif /* __ARM_MMU_H */
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 1c6129816d..c3292354a1 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -35,7 +35,7 @@ pbl-dtb-$(CONFIG_MACH_GW_VENTANA) += imx6q-gw54xx.dtb.o
pbl-dtb-$(CONFIG_MACH_KONTRON_SAMX6I) += imx6q-samx6i.dtb.o \
imx6dl-samx6i.dtb.o
pbl-dtb-$(CONFIG_MACH_LENOVO_IX4_300D) += armada-xp-lenovo-ix4-300d-bb.dtb.o
-pbl-dtb-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += armada-xp-gp-bb.dtb.o
+pbl-dtb-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += armada-xp-gp-bb.dtb.o armada-xp-db-bb.dtb.o
pbl-dtb-$(CONFIG_MACH_NETGEAR_RN104) += armada-370-rn104-bb.dtb.o
pbl-dtb-$(CONFIG_MACH_NETGEAR_RN2120) += armada-xp-rn2120-bb.dtb.o
pbl-dtb-$(CONFIG_MACH_NITROGEN6) += imx6q-nitrogen6x.dtb.o imx6dl-nitrogen6x.dtb.o imx6qp-nitrogen6_max.dtb.o
@@ -125,7 +125,8 @@ pbl-dtb-$(CONFIG_MACH_ZII_VF610_DEV) += \
vf610-zii-cfu1.dtb.o \
vf610-zii-ssmb-spu3.dtb.o \
vf610-zii-scu4-aib.dtb.o \
- vf610-zii-spb4.dtb.o
+ vf610-zii-spb4.dtb.o \
+ vf610-zii-ssmb-dtu.dtb.o
pbl-dtb-$(CONFIG_MACH_AT91SAM9263EK_DT) += at91sam9263ek.dtb.o
pbl-dtb-$(CONFIG_MACH_MICROCHIP_KSZ9477_EVB) += at91-microchip-ksz9477-evb.dtb.o
pbl-dtb-$(CONFIG_MACH_AT91SAM9X5EK) += at91sam9x5ek.dtb.o
diff --git a/arch/arm/dts/armada-xp-db-bb.dts b/arch/arm/dts/armada-xp-db-bb.dts
new file mode 100644
index 0000000000..7201f4aaa1
--- /dev/null
+++ b/arch/arm/dts/armada-xp-db-bb.dts
@@ -0,0 +1,12 @@
+/*
+ * Barebox specific DT overlay for Marvell Armada XP DB-MV784MP-GP
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ */
+
+#include "arm/armada-xp-db.dts"
+
+/ {
+ chosen {
+ stdout-path = "/soc/internal-regs/serial@12000";
+ };
+};
diff --git a/arch/arm/dts/bcm2837-rpi-3.dts b/arch/arm/dts/bcm2837-rpi-3.dts
index 51883613ce..420525b9e8 100644
--- a/arch/arm/dts/bcm2837-rpi-3.dts
+++ b/arch/arm/dts/bcm2837-rpi-3.dts
@@ -11,10 +11,5 @@
};
&sdhci {
- pinctrl-0 = <&emmc_gpio48>;
- /delete-node/ wifi@1;
-};
-
-&sdhost {
status = "disabled";
};
diff --git a/arch/arm/dts/fsl-tqmls1046a-mbls10xxa.dts b/arch/arm/dts/fsl-tqmls1046a-mbls10xxa.dts
index f21479eef8..f0332e3999 100644
--- a/arch/arm/dts/fsl-tqmls1046a-mbls10xxa.dts
+++ b/arch/arm/dts/fsl-tqmls1046a-mbls10xxa.dts
@@ -20,10 +20,30 @@
serial0 = &duart0;
serial1 = &duart1;
mmc0 = &esdhc;
+ qspiflash0 = &qflash0;
+ qspiflash1 = &qflash1;
+ qsgmii_s1_p1 = &qsgmii1_phy1;
+ qsgmii_s1_p2 = &qsgmii1_phy2;
+ qsgmii_s2_p1 = &qsgmii2_phy1;
+ qsgmii_s2_p2 = &qsgmii2_phy2;
+ qsgmii_s2_p3 = &qsgmii2_phy3;
+ qsgmii_s2_p4 = &qsgmii2_phy4;
};
chosen {
stdout-path = "serial1:115200n8";
+
+ environment-sd {
+ compatible = "barebox,environment";
+ device-path = &environment_sd;
+ status = "disabled";
+ };
+
+ environment-qspi {
+ compatible = "barebox,environment";
+ device-path = &environment_qspi;
+ status = "disabled";
+ };
};
gpio-keys-polled {
@@ -57,6 +77,24 @@
};
+&esdhc {
+ partitions {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "fixed-partitions";
+
+ partition@0 {
+ label = "barebox";
+ reg = <0x1000 0xdf000>;
+ };
+
+ environment_sd: partition@e0000 {
+ label = "barebox-environment";
+ reg = <0xe0000 0x20000>;
+ };
+ };
+};
&duart0 {
status = "okay";
@@ -66,6 +104,14 @@
status = "okay";
};
+&esdhc {
+ mmc-hs200-1_8v;
+ sd-uhs-sdr104;
+ sd-uhs-sdr50;
+ sd-uhs-sdr25;
+ sd-uhs-sdr12;
+};
+
&i2c3 {
status = "okay";
@@ -132,43 +178,40 @@
&fman0 {
status = "okay";
- ethernet@e0000 {
- status = "disabled";
+ ethernet@e0000 { /* EMAC.1 */
+ phy-connection-type = "sgmii";
+
};
- ethernet@e2000 {
- phy-handle = <&qsgmii1_phy2>;
+ ethernet@e2000 { /* EMAC.2 */
phy-connection-type = "sgmii";
};
- ethernet@e4000 {
+ ethernet@e4000 { /* EMAC.3 */
phy-handle = <&rgmii_phy1>;
phy-connection-type = "rgmii";
phy-mode = "rgmii-id";
};
- ethernet@e6000 {
+ ethernet@e6000 { /* EMAC.4 */
phy-handle = <&rgmii_phy2>;
phy-connection-type = "rgmii";
phy-mode = "rgmii-id";
};
- ethernet@e8000 {
- status = "disabled";
+ ethernet@e8000 { /* EMAC.5 */
+ phy-connection-type = "sgmii";
};
- ethernet@ea000 {
- phy-handle = <&qsgmii2_phy2>;
+ ethernet@ea000 { /* EMAC.6 */
phy-connection-type = "sgmii";
};
- ethernet@f0000 {
- phy-handle = <&qsgmii1_phy1>;
+ ethernet@f0000 { /* EMAC.9 */
phy-connection-type = "sgmii";
};
- ethernet@f2000 {
- phy-handle = <&qsgmii2_phy1>;
+ ethernet@f2000 { /* EMAC.10 */
phy-connection-type = "sgmii";
};
@@ -212,6 +255,13 @@
ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_8_B_NIB>;
};
+ rgmii_phy2: ethernet-phy@0c {
+ reg = <0x0c>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_1_50_NS>;
+ ti,tx-internal-delay = <DP83867_RGMIIDCTL_1_50_NS>;
+ ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_8_B_NIB>;
+ };
+
qsgmii1_phy1: ethernet-phy@1c {
reg = <0x1c>;
};
@@ -219,15 +269,6 @@
qsgmii1_phy2: ethernet-phy@1d {
reg = <0x1d>;
};
- };
-
- mdio@fd000 {
- rgmii_phy2: ethernet-phy@0c {
- reg = <0x0c>;
- ti,rx-internal-delay = <DP83867_RGMIIDCTL_1_50_NS>;
- ti,tx-internal-delay = <DP83867_RGMIIDCTL_1_50_NS>;
- ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_8_B_NIB>;
- };
qsgmii2_phy1: ethernet-phy@00 {
reg = <0x00>;
@@ -236,5 +277,41 @@
qsgmii2_phy2: ethernet-phy@01 {
reg = <0x01>;
};
+
+ qsgmii2_phy3: ethernet-phy@02 {
+ reg = <0x02>;
+ };
+
+ qsgmii2_phy4: ethernet-phy@03 {
+ reg = <0x03>;
+ };
+ };
+
+ mdio@fd000 {
+ status = "disabled";
+ };
+};
+
+&qflash0 {
+ partitions {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ compatible = "fixed-partitions";
+
+ partition@0 {
+ label = "barebox";
+ reg = <0x0 0x200000>;
+ };
+
+ environment_qspi: partition@200000 {
+ label = "barebox-environment";
+ reg = <0x200000 0x80000>;
+ };
+
+ partition@280000 {
+ label = "data";
+ reg = <0x280000 0x0>;
+ };
};
};
diff --git a/arch/arm/dts/fsl-tqmls1046a.dtsi b/arch/arm/dts/fsl-tqmls1046a.dtsi
index 4717e66857..0ea2612cbf 100644
--- a/arch/arm/dts/fsl-tqmls1046a.dtsi
+++ b/arch/arm/dts/fsl-tqmls1046a.dtsi
@@ -40,15 +40,15 @@
#address-cells = <1>;
#size-cells = <1>;
compatible = "jedec,spi-nor";
- spi-max-frequency = <108000000>;
+ spi-max-frequency = <62500000>;
reg = <0>;
};
qflash1: mx66u51235f@1 {
#address-cells = <1>;
#size-cells = <1>;
- spi-max-frequency = <108000000>;
compatible = "jedec,spi-nor";
+ spi-max-frequency = <62500000>;
reg = <1>;
};
};
diff --git a/arch/arm/dts/imx8mq.dtsi b/arch/arm/dts/imx8mq.dtsi
index d1d8bdaa0e..1d59615238 100644
--- a/arch/arm/dts/imx8mq.dtsi
+++ b/arch/arm/dts/imx8mq.dtsi
@@ -198,3 +198,7 @@
<25000000>,
<125000000>;
};
+
+&pgc_pcie1 {
+ power-domains = <&pgc_pcie2>;
+};
diff --git a/arch/arm/dts/vf610-zii-ssmb-dtu.dts b/arch/arm/dts/vf610-zii-ssmb-dtu.dts
new file mode 100644
index 0000000000..6ffb7aa62d
--- /dev/null
+++ b/arch/arm/dts/vf610-zii-ssmb-dtu.dts
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+
+#include <arm/vf610-zii-ssmb-dtu.dts>
+
+#include "vf610-zii-dev.dtsi"
+
+/ {
+ aliases {
+ /*
+ * NVMEM device corresponding to EEPROM attached to
+ * the switch shared DT node with it, so we use that
+ * fact to create a desirable naming
+ */
+ switch-eeprom = &switch0;
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
index 348a76b2c1..d8a4d9b667 100644
--- a/arch/arm/include/asm/bitops.h
+++ b/arch/arm/include/asm/bitops.h
@@ -82,6 +82,12 @@ static inline int test_bit(int nr, const void * addr)
#define test_and_clear_bit(x, y) __test_and_clear_bit(x, y)
#define test_and_change_bit(x, y) __test_and_change_bit(x, y)
+#ifdef CONFIG_CPU_V8
+
+#include <asm-generic/bitops/find.h>
+
+#else /* CONFIG_CPU_V8 */
+
#ifndef __ARMEB__
/*
* These are the little endian definitions.
@@ -115,6 +121,8 @@ extern int _find_next_bit_be(const unsigned long *p, int size, int offset);
#endif /* __ARMEB__ */
+#endif /* CONFIG_CPU_V8 */
+
#if defined (CONFIG_CPU_32) && defined(__LINUX_ARM_ARCH__) && (__LINUX_ARM_ARCH__ >= 5)
static inline int constant_fls(int x)
{
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index ef9cb98bf0..a0180f2df8 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -1,6 +1,26 @@
#ifndef __ASM_ARM_SYSTEM_H
#define __ASM_ARM_SYSTEM_H
+#include <linux/const.h>
+
+/* Common SCTLR_ELx flags. */
+#define SCTLR_ELx_DSSBS (_BITUL(44))
+#define SCTLR_ELx_ENIA (_BITUL(31))
+#define SCTLR_ELx_ENIB (_BITUL(30))
+#define SCTLR_ELx_ENDA (_BITUL(27))
+#define SCTLR_ELx_EE (_BITUL(25))
+#define SCTLR_ELx_IESB (_BITUL(21))
+#define SCTLR_ELx_WXN (_BITUL(19))
+#define SCTLR_ELx_ENDB (_BITUL(13))
+#define SCTLR_ELx_I (_BITUL(12))
+#define SCTLR_ELx_SA (_BITUL(3))
+#define SCTLR_ELx_C (_BITUL(2))
+#define SCTLR_ELx_A (_BITUL(1))
+#define SCTLR_ELx_M (_BITUL(0))
+
+#define SCTLR_ELx_FLAGS (SCTLR_ELx_M | SCTLR_ELx_A | SCTLR_ELx_C | \
+ SCTLR_ELx_SA | SCTLR_ELx_I | SCTLR_ELx_IESB)
+
#if __LINUX_ARM_ARCH__ >= 7
#define isb() __asm__ __volatile__ ("isb" : : : "memory")
#ifdef CONFIG_CPU_64v8
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index c4e7500e8f..71d37cee90 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -454,6 +454,7 @@ config MACH_ZII_IMX8MQ_DEV
select FIRMWARE_IMX8MQ_ATF
select ARM_SMCCC
select MCI_IMX_ESDHC_PBL
+ select MACH_ZII_COMMON
config MACH_ZII_VF610_DEV
bool "ZII VF610 Dev Family"
diff --git a/arch/arm/mach-imx/imx6.c b/arch/arm/mach-imx/imx6.c
index 01b4274ed3..e898be9ab5 100644
--- a/arch/arm/mach-imx/imx6.c
+++ b/arch/arm/mach-imx/imx6.c
@@ -117,7 +117,7 @@ static void imx6_setup_ipu_qos(void)
uint32_t val;
if (!cpu_mx6_is_mx6q() && !cpu_mx6_is_mx6d() &&
- !cpu_mx6_is_mx6dl() && cpu_mx6_is_mx6s())
+ !cpu_mx6_is_mx6dl() && !cpu_mx6_is_mx6s())
return;
val = readl(iomux + IOMUXC_GPR4);
diff --git a/arch/arm/mach-imx/include/mach/imx25-regs.h b/arch/arm/mach-imx/include/mach/imx25-regs.h
index 71812764c9..5974897a16 100644
--- a/arch/arm/mach-imx/include/mach/imx25-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx25-regs.h
@@ -62,12 +62,14 @@
#define MX25_SSI2_BASE_ADDR 0x50014000
#define MX25_SSI1_BASE_ADDR 0x50034000
#define MX25_NFC_BASE_ADDR 0xbb000000
+#define MX25_SCC_BASE_ADDR 0x53fac000
#define MX25_IIM_BASE_ADDR 0x53ff0000
#define MX25_DRYICE_BASE_ADDR 0x53ffc000
#define MX25_ESDHC1_BASE_ADDR 0x53fb4000
#define MX25_ESDHC2_BASE_ADDR 0x53fb8000
#define MX25_LCDC_BASE_ADDR 0x53fbc000
#define MX25_KPP_BASE_ADDR 0x43fa8000
+#define MX25_RNGB_BASE_ADDR 0x53fb0000
#define MX25_SDMA_BASE_ADDR 0x53fd4000
#define MX25_USB_BASE_ADDR 0x53ff4000
#define MX25_USB_OTG_BASE_ADDR (MX25_USB_BASE_ADDR + 0x0000)
diff --git a/arch/arm/mach-layerscape/Makefile b/arch/arm/mach-layerscape/Makefile
index 269839254b..73cd61a7cf 100644
--- a/arch/arm/mach-layerscape/Makefile
+++ b/arch/arm/mach-layerscape/Makefile
@@ -2,3 +2,5 @@ obj- := __dummy__.o
lwl-y += lowlevel.o errata.o
lwl-$(CONFIG_ARCH_LS1046) += lowlevel-ls1046a.o
obj-y += icid.o
+obj-pbl-y += boot.o
+pbl-y += xload-qspi.o xload.o
diff --git a/arch/arm/mach-layerscape/boot.c b/arch/arm/mach-layerscape/boot.c
new file mode 100644
index 0000000000..c804977d22
--- /dev/null
+++ b/arch/arm/mach-layerscape/boot.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <init.h>
+#include <bootsource.h>
+#include <mach/layerscape.h>
+#include <soc/fsl/immap_lsch2.h>
+
+enum bootsource ls1046_bootsource_get(void)
+{
+ void __iomem *dcfg = IOMEM(LSCH2_DCFG_ADDR);
+ uint32_t rcw_src;
+
+ rcw_src = in_be32(dcfg) >> 23;
+
+ if (rcw_src == 0x40)
+ return BOOTSOURCE_MMC;
+ if ((rcw_src & 0x1fe) == 0x44)
+ return BOOTSOURCE_SPI_NOR;
+ if ((rcw_src & 0x1f0) == 0x10)
+ /* 8bit NOR Flash */
+ return BOOTSOURCE_NOR;
+ if ((rcw_src & 0x1f0) == 0x20)
+ /* 16bit NOR Flash */
+ return BOOTSOURCE_NOR;
+
+ return BOOTSOURCE_UNKNOWN;
+}
+
+static int ls1046a_bootsource_init(void)
+{
+ if (!of_machine_is_compatible("fsl,ls1046a"))
+ return 0;
+
+ bootsource_set(ls1046_bootsource_get());
+
+ return 0;
+}
+coredevice_initcall(ls1046a_bootsource_init); \ No newline at end of file
diff --git a/arch/arm/mach-layerscape/include/mach/bbu.h b/arch/arm/mach-layerscape/include/mach/bbu.h
new file mode 100644
index 0000000000..1ea0cbb11f
--- /dev/null
+++ b/arch/arm/mach-layerscape/include/mach/bbu.h
@@ -0,0 +1,22 @@
+#ifndef __MACH_LAYERSCAPE_BBU_H
+#define __MACH_LAYERSCAPE_BBU_H
+
+#include <bbu.h>
+
+static inline int ls1046a_bbu_mmc_register_handler(const char *name,
+ const char *devicefile,
+ unsigned long flags)
+{
+ return bbu_register_std_file_update(name, flags, devicefile,
+ filetype_layerscape_image);
+}
+
+static inline int ls1046a_bbu_qspi_register_handler(const char *name,
+ const char *devicefile,
+ unsigned long flags)
+{
+ return bbu_register_std_file_update(name, flags, devicefile,
+ filetype_layerscape_qspi_image);
+}
+
+#endif /* __MACH_LAYERSCAPE_BBU_H */ \ No newline at end of file
diff --git a/arch/arm/mach-layerscape/include/mach/layerscape.h b/arch/arm/mach-layerscape/include/mach/layerscape.h
index 55e0b7bc96..3366e7f258 100644
--- a/arch/arm/mach-layerscape/include/mach/layerscape.h
+++ b/arch/arm/mach-layerscape/include/mach/layerscape.h
@@ -4,4 +4,6 @@
#define LS1046A_DDR_SDRAM_BASE 0x80000000
#define LS1046A_DDR_FREQ 2100000000
+enum bootsource ls1046_bootsource_get(void);
+
#endif /* __MACH_LAYERSCAPE_H */
diff --git a/arch/arm/mach-layerscape/include/mach/xload.h b/arch/arm/mach-layerscape/include/mach/xload.h
index fedd36e020..eb2d998865 100644
--- a/arch/arm/mach-layerscape/include/mach/xload.h
+++ b/arch/arm/mach-layerscape/include/mach/xload.h
@@ -2,5 +2,9 @@
#define __MACH_XLOAD_H
int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long r2);
+int ls1046a_qspi_start_image(unsigned long r0, unsigned long r1,
+ unsigned long r2);
+int ls1046a_xload_start_image(unsigned long r0, unsigned long r1,
+ unsigned long r2);
#endif /* __MACH_XLOAD_H */
diff --git a/arch/arm/mach-layerscape/xload-qspi.c b/arch/arm/mach-layerscape/xload-qspi.c
new file mode 100644
index 0000000000..c76780a0e8
--- /dev/null
+++ b/arch/arm/mach-layerscape/xload-qspi.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <soc/fsl/immap_lsch2.h>
+#include <asm-generic/sections.h>
+#include <asm/cache.h>
+#include <mach/xload.h>
+#include <mach/layerscape.h>
+
+/*
+ * The offset of the 2nd stage image in the output file. This must match with the
+ * offset the pblimage tool puts barebox to.
+ */
+#define BAREBOX_START (128 * 1024)
+
+int ls1046a_qspi_start_image(unsigned long r0, unsigned long r1,
+ unsigned long r2)
+{
+ void *qspi_reg_base = IOMEM(LSCH2_QSPI0_BASE_ADDR);
+ void *membase = (void *)LS1046A_DDR_SDRAM_BASE;
+ void *qspi_mem_base = IOMEM(0x40000000);
+ void (*barebox)(unsigned long, unsigned long, unsigned long) = membase;
+
+ /* Switch controller into little endian mode */
+ out_be32(qspi_reg_base, 0x000f400c);
+
+ memcpy(membase, qspi_mem_base + BAREBOX_START, barebox_image_size);
+ icache_invalidate();
+
+ printf("Starting barebox\n");
+
+ barebox(r0, r1, r2);
+
+ printf("failed\n");
+
+ return -EIO;
+}
diff --git a/arch/arm/mach-layerscape/xload.c b/arch/arm/mach-layerscape/xload.c
new file mode 100644
index 0000000000..54495d7f97
--- /dev/null
+++ b/arch/arm/mach-layerscape/xload.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <bootsource.h>
+#include <mach/layerscape.h>
+#include <mach/xload.h>
+
+int ls1046a_xload_start_image(unsigned long r0, unsigned long r1,
+ unsigned long r2)
+{
+ enum bootsource src;
+
+ src = ls1046_bootsource_get();
+
+ switch (src) {
+ case BOOTSOURCE_SPI_NOR:
+ return ls1046a_qspi_start_image(r0, r1, r2);
+ case BOOTSOURCE_MMC:
+ return ls1046a_esdhc_start_image(r0, r1, r2);
+ default:
+ pr_err("Unknown bootsource\n");
+ return -EINVAL;
+ }
+}
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index ad97e83701..bab22f07ff 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -56,6 +56,10 @@ config MACH_LENOVO_IX4_300D
bool "Lenovo Iomega ix4-300d"
select ARCH_ARMADA_XP
+config MACH_MARVELL_ARMADA_XP_DB
+ bool "Marvell Armada XP DB"
+ select ARCH_ARMADA_XP
+
config MACH_MARVELL_ARMADA_XP_GP
bool "Marvell Armada XP GP"
select ARCH_ARMADA_XP
diff --git a/arch/arm/mach-socfpga/cyclone5-reset-manager.c b/arch/arm/mach-socfpga/cyclone5-reset-manager.c
index 4bbe1a8101..8635806846 100644
--- a/arch/arm/mach-socfpga/cyclone5-reset-manager.c
+++ b/arch/arm/mach-socfpga/cyclone5-reset-manager.c
@@ -22,23 +22,6 @@
#include <mach/cyclone5-regs.h>
#include <mach/cyclone5-reset-manager.h>
-/* Disable the watchdog (toggle reset to watchdog) */
-void watchdog_disable(void)
-{
- void __iomem *rm = (void *)CYCLONE5_RSTMGR_ADDRESS;
- uint32_t val;
-
- /* assert reset for watchdog */
- val = readl(rm + RESET_MGR_PER_MOD_RESET_OFS);
- val |= 1 << RSTMGR_PERMODRST_L4WD0_LSB;
- writel(val, rm + RESET_MGR_PER_MOD_RESET_OFS);
-
- /* deassert watchdog from reset (watchdog in not running state) */
- val = readl(rm + RESET_MGR_PER_MOD_RESET_OFS);
- val &= ~(1 << RSTMGR_PERMODRST_L4WD0_LSB);
- writel(val, rm + RESET_MGR_PER_MOD_RESET_OFS);
-}
-
/* Write the reset manager register to cause reset */
static void __noreturn socfpga_restart_soc(struct restart_handler *rst)
{
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index a4070cfe32..5901930a73 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -7,7 +7,6 @@ config MIPS
select GENERIC_LIB_ASHRDI3
select GENERIC_LIB_LSHRDI3
select HAS_KALLSYMS
- select HAVE_CONFIGURABLE_MEMORY_LAYOUT
select HAVE_CONFIGURABLE_TEXT_BASE
select HAVE_PBL_MULTI_IMAGES
select HAS_DMA
@@ -32,6 +31,24 @@ config PHYS_ADDR_T_64BIT
menu "Machine selection"
+config MIPS_RELOCATION_TABLE_SIZE
+ hex "Relocation table size"
+ range 0x100 0x10000
+ default "0x8000"
+ ---help---
+ A table of relocation data will be appended to the Barebox binary
+ and parsed in relocate_code() to fix up all offsets in the relocated
+ Barebox.
+
+ This option allows the amount of space reserved for the table to be
+ adjusted in a range from 256 up to 64k. The default is 32k and should
+ be ok in most cases. Reduce this value to shrink the size of U-Boot
+ binary.
+
+ The build will fail and a valid size suggested if this is too small.
+
+ If unsure, leave at the default value.
+
config BUILTIN_DTB
bool "link a DTB into the barebox image"
depends on OFTREE
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 5fbd51ceee..6f1a1ce617 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -59,7 +59,7 @@ CPPFLAGS += -DTEXT_BASE=$(CONFIG_TEXT_BASE)
ifndef CONFIG_MODULES
# Add cleanup flags
CPPFLAGS += -fdata-sections -ffunction-sections
-LDFLAGS_barebox += -static --gc-sections
+LDFLAGS_barebox += -static --gc-sections --emit-relocs
endif
ifdef CONFIG_IMAGE_COMPRESSION
@@ -116,6 +116,13 @@ CFLAGS += $(cflags-y)
lds-$(CONFIG_GENERIC_LINKER_SCRIPT) := arch/mips/lib/barebox.lds
+cmd_barebox__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_barebox) -o $@ \
+ -T $(barebox-lds) \
+ --start-group $(barebox-common) --end-group \
+ $(filter-out $(barebox-lds) $(barebox-common) FORCE ,$^); \
+ $(objtree)/scripts/mips-relocs $@
+
+
CLEAN_FILES += arch/mips/lib/barebox.lds barebox.map barebox.S
pbl := arch/mips/pbl
diff --git a/arch/mips/boards/8devices-lima/lowlevel.S b/arch/mips/boards/8devices-lima/lowlevel.S
index b53b23b42a..dd1ab6247d 100644
--- a/arch/mips/boards/8devices-lima/lowlevel.S
+++ b/arch/mips/boards/8devices-lima/lowlevel.S
@@ -11,6 +11,7 @@
#include <mach/pbl_macros.h>
#include <mach/pbl_ll_init_qca4531.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -38,4 +39,4 @@ skip_flash_test:
dcache_enable
skip_pll_ram_config:
-ENTRY_FUNCTION_END(BOARD_PBL_START, qca4531_8devices_lima)
+ENTRY_FUNCTION_END(BOARD_PBL_START, qca4531_8devices_lima, SZ_64M)
diff --git a/arch/mips/boards/black-swift/lowlevel.S b/arch/mips/boards/black-swift/lowlevel.S
index 0ba77435f8..5c5afcdf09 100644
--- a/arch/mips/boards/black-swift/lowlevel.S
+++ b/arch/mips/boards/black-swift/lowlevel.S
@@ -11,9 +11,10 @@
#include <asm/asm.h>
#include <mach/pbl_macros.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
ar9331_pbl_generic_start
-ENTRY_FUNCTION_END(BOARD_PBL_START, black_swift)
+ENTRY_FUNCTION_END(BOARD_PBL_START, black_swift, SZ_64M)
diff --git a/arch/mips/boards/dlink-dir-320/lowlevel.S b/arch/mips/boards/dlink-dir-320/lowlevel.S
index 9f3bd5dead..da969bc74e 100644
--- a/arch/mips/boards/dlink-dir-320/lowlevel.S
+++ b/arch/mips/boards/dlink-dir-320/lowlevel.S
@@ -9,6 +9,7 @@
#include <asm/pbl_macros.h>
#include <mach/debug_ll.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -19,4 +20,4 @@ ENTRY_FUNCTION(BOARD_PBL_START)
/* CPU/SoC specific setup ... */
/* ... absent */
-ENTRY_FUNCTION_END(BOARD_PBL_START, dlink_dir_320)
+ENTRY_FUNCTION_END(BOARD_PBL_START, dlink_dir_320, SZ_32M)
diff --git a/arch/mips/boards/dptechnics-dpt-module/lowlevel.S b/arch/mips/boards/dptechnics-dpt-module/lowlevel.S
index 28b8f4fac7..b5621963c3 100644
--- a/arch/mips/boards/dptechnics-dpt-module/lowlevel.S
+++ b/arch/mips/boards/dptechnics-dpt-module/lowlevel.S
@@ -12,9 +12,10 @@
#include <asm/pbl_macros.h>
#include <mach/pbl_macros.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
ar9331_pbl_generic_start
-ENTRY_FUNCTION_END(BOARD_PBL_START, ar9331_dptechnics_dpt_module)
+ENTRY_FUNCTION_END(BOARD_PBL_START, ar9331_dptechnics_dpt_module, SZ_64M)
diff --git a/arch/mips/boards/img-ci20/lowlevel.S b/arch/mips/boards/img-ci20/lowlevel.S
index 056df17bf8..0295e44d1a 100644
--- a/arch/mips/boards/img-ci20/lowlevel.S
+++ b/arch/mips/boards/img-ci20/lowlevel.S
@@ -10,6 +10,7 @@
#include <asm/pbl_macros.h>
#include <mach/debug_ll.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -23,4 +24,4 @@ ENTRY_FUNCTION(BOARD_PBL_START)
debug_ll_outc '.'
debug_ll_ns16550_outnl
-ENTRY_FUNCTION_END(BOARD_PBL_START, img_ci20)
+ENTRY_FUNCTION_END(BOARD_PBL_START, img_ci20, SZ_1G)
diff --git a/arch/mips/boards/loongson-ls1b/lowlevel.S b/arch/mips/boards/loongson-ls1b/lowlevel.S
index 37744e9fac..c533df3ce5 100644
--- a/arch/mips/boards/loongson-ls1b/lowlevel.S
+++ b/arch/mips/boards/loongson-ls1b/lowlevel.S
@@ -9,6 +9,7 @@
#include <mach/loongson1.h>
#include <mach/debug_ll.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -21,4 +22,4 @@ ENTRY_FUNCTION(BOARD_PBL_START)
debug_ll_outc '.'
debug_ll_ns16550_outnl
-ENTRY_FUNCTION_END(BOARD_PBL_START, loongson_ls1b)
+ENTRY_FUNCTION_END(BOARD_PBL_START, loongson_ls1b, SZ_64M)
diff --git a/arch/mips/boards/qemu-malta/lowlevel.S b/arch/mips/boards/qemu-malta/lowlevel.S
index 3861ae9e46..e4ecde45cf 100644
--- a/arch/mips/boards/qemu-malta/lowlevel.S
+++ b/arch/mips/boards/qemu-malta/lowlevel.S
@@ -10,6 +10,7 @@
#include <asm/asm.h>
#include <asm/pbl_macros.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
#include <asm/addrspace.h>
#include <asm/gt64120.h>
@@ -82,4 +83,4 @@ __start:
li t0, GT_LD(0x1bdfffff)
sw t0, GT_PCI0M1HD_OFS(t1)
-ENTRY_FUNCTION_END(BOARD_PBL_START, qemu_malta)
+ENTRY_FUNCTION_END(BOARD_PBL_START, qemu_malta, SZ_256M)
diff --git a/arch/mips/boards/ritmix-rzx50/lowlevel.S b/arch/mips/boards/ritmix-rzx50/lowlevel.S
index 7a9743835e..33810f67f5 100644
--- a/arch/mips/boards/ritmix-rzx50/lowlevel.S
+++ b/arch/mips/boards/ritmix-rzx50/lowlevel.S
@@ -10,6 +10,7 @@
#include <asm/pbl_macros.h>
#include <mach/debug_ll.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -23,4 +24,4 @@ ENTRY_FUNCTION(BOARD_PBL_START)
debug_ll_outc '.'
debug_ll_ns16550_outnl
-ENTRY_FUNCTION_END(BOARD_PBL_START, rzx50)
+ENTRY_FUNCTION_END(BOARD_PBL_START, rzx50, SZ_64M)
diff --git a/arch/mips/boards/tplink-mr3020/lowlevel.S b/arch/mips/boards/tplink-mr3020/lowlevel.S
index 0a1c193c1e..b96292ecc4 100644
--- a/arch/mips/boards/tplink-mr3020/lowlevel.S
+++ b/arch/mips/boards/tplink-mr3020/lowlevel.S
@@ -11,9 +11,10 @@
#include <asm/asm.h>
#include <mach/pbl_macros.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
ar9331_pbl_generic_start
-ENTRY_FUNCTION_END(BOARD_PBL_START, tplink_mr3020)
+ENTRY_FUNCTION_END(BOARD_PBL_START, tplink_mr3020, SZ_32M)
diff --git a/arch/mips/boards/tplink-wdr4300/lowlevel.S b/arch/mips/boards/tplink-wdr4300/lowlevel.S
index 461b257f41..8aed5ddcf1 100644
--- a/arch/mips/boards/tplink-wdr4300/lowlevel.S
+++ b/arch/mips/boards/tplink-wdr4300/lowlevel.S
@@ -11,6 +11,7 @@
#include <mach/pbl_macros.h>
#include <mach/pbl_ll_init_ar9344_1.1.h>
#include <asm/pbl_nmon.h>
+#include <linux/sizes.h>
ENTRY_FUNCTION(BOARD_PBL_START)
@@ -35,4 +36,4 @@ skip_flash_test:
skip_pll_ram_config:
-ENTRY_FUNCTION_END(BOARD_PBL_START, ar9344_tl_wdr4300_v1.7)
+ENTRY_FUNCTION_END(BOARD_PBL_START, ar9344_tl_wdr4300_v1.7, SZ_128M)
diff --git a/arch/mips/boot/dtb.c b/arch/mips/boot/dtb.c
index ea30e16f21..5e316270f6 100644
--- a/arch/mips/boot/dtb.c
+++ b/arch/mips/boot/dtb.c
@@ -29,10 +29,6 @@ void of_add_memory_bank(struct device_node *node, bool dump, int r,
if (dump)
pr_info("%s: %s: 0x%llx@0x%llx\n", node->name, str, size, base);
-
- if (glob_fdt && glob_fdt_size)
- request_sdram_region("fdt", (resource_size_t)glob_fdt,
- glob_fdt_size);
}
extern char __dtb_start[];
diff --git a/arch/mips/boot/main_entry-pbl.c b/arch/mips/boot/main_entry-pbl.c
index b40887b064..02ddd5ec24 100644
--- a/arch/mips/boot/main_entry-pbl.c
+++ b/arch/mips/boot/main_entry-pbl.c
@@ -18,14 +18,14 @@ extern void *input_data_end;
unsigned long free_mem_ptr;
unsigned long free_mem_end_ptr;
-void pbl_main_entry(void *fdt, void *fdt_end);
+void pbl_main_entry(void *fdt, void *fdt_end, u32 ram_size);
static unsigned long *ttb;
static void barebox_uncompress(void *compressed_start, unsigned int len)
{
/* set 128 KiB at the end of the MALLOC_BASE for early malloc */
- free_mem_ptr = MALLOC_BASE + MALLOC_SIZE - SZ_128K;
+ free_mem_ptr = TEXT_BASE - SZ_128K;
free_mem_end_ptr = free_mem_ptr + SZ_128K;
ttb = (void *)((free_mem_ptr - 0x4000) & ~0x3fff);
@@ -33,11 +33,12 @@ static void barebox_uncompress(void *compressed_start, unsigned int len)
pbl_barebox_uncompress((void*)TEXT_BASE, compressed_start, len);
}
-void __section(.text_entry) pbl_main_entry(void *fdt, void *fdt_end)
+void __section(.text_entry) pbl_main_entry(void *fdt, void *fdt_end,
+ u32 ram_size)
{
u32 pg_start, pg_end, pg_len, fdt_len;
void *fdt_new;
- void (*barebox)(void *fdt, u32 fdt_len);
+ void (*barebox)(void *fdt, u32 fdt_len, u32 ram_size);
puts_ll("pbl_main_entry()\n");
@@ -51,9 +52,9 @@ void __section(.text_entry) pbl_main_entry(void *fdt, void *fdt_end)
barebox_uncompress(&input_data, pg_len);
fdt_len = (u32)fdt_end - (u32)fdt;
- fdt_new = (void *)PAGE_ALIGN_DOWN(STACK_BASE - fdt_len);
+ fdt_new = (void *)PAGE_ALIGN_DOWN(TEXT_BASE - MALLOC_SIZE - STACK_SIZE - fdt_len);
memcpy(fdt_new, fdt, fdt_len);
barebox = (void *)TEXT_BASE;
- barebox(fdt_new, fdt_len);
+ barebox(fdt_new, fdt_len, ram_size);
}
diff --git a/arch/mips/boot/main_entry.c b/arch/mips/boot/main_entry.c
index 84325da93a..5b88730b07 100644
--- a/arch/mips/boot/main_entry.c
+++ b/arch/mips/boot/main_entry.c
@@ -12,6 +12,7 @@
#include <asm/cpu-features.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
+#include <linux/sizes.h>
extern void handle_reserved(void);
@@ -61,6 +62,7 @@ static void trap_init(void)
extern void *glob_fdt;
extern u32 glob_fdt_size;
+extern unsigned long mips_stack_top;
/**
* Called plainly from assembler code
@@ -69,6 +71,7 @@ extern u32 glob_fdt_size;
*/
void __bare_init main_entry(void *fdt, u32 fdt_size)
{
+ unsigned long malloc_start, malloc_end;
/* clear the BSS first */
memset(__bss_start, 0x00, __bss_stop - __bss_start);
@@ -82,8 +85,18 @@ void __bare_init main_entry(void *fdt, u32 fdt_size)
trap_init();
- mem_malloc_init((void *)MALLOC_BASE,
- (void *)(MALLOC_BASE + MALLOC_SIZE - 1));
+ malloc_end = _stext;
+
+ if (MALLOC_SIZE > 0)
+ malloc_start = malloc_end - MALLOC_SIZE;
+ else
+ malloc_start = malloc_end - SZ_8M;
+
+ pr_debug("initializing malloc pool at 0x%08lx (size 0x%08lx)\n",
+ malloc_start, malloc_end - malloc_start);
+
+ mem_malloc_init((void *)malloc_start, (void *)_stext - 1);
+ mips_stack_top = malloc_start;
glob_fdt = fdt;
glob_fdt_size = fdt_size;
diff --git a/arch/mips/boot/start.S b/arch/mips/boot/start.S
index 6efe03e98d..c1cd2d9dd5 100644
--- a/arch/mips/boot/start.S
+++ b/arch/mips/boot/start.S
@@ -18,6 +18,7 @@ EXPORT(_start)
/* save dtb pointer */
move s0, a0
move s1, a1
+ move s2, a2
/* disable watchpoints */
mtc0 zero, CP0_WATCHLO
@@ -32,7 +33,8 @@ EXPORT(_start)
/* restore dtb pointer */
move a0, s0
move a1, s1
- la v0, main_entry
+ move a2, s2
+ la v0, relocate_code
jal v0
nop
diff --git a/arch/mips/include/asm/asm.h b/arch/mips/include/asm/asm.h
index 7d2b673bd9..a85467ceed 100644
--- a/arch/mips/include/asm/asm.h
+++ b/arch/mips/include/asm/asm.h
@@ -89,13 +89,14 @@ EXPORT(symbol)
/*
* ENTRY_FUNCTION_END - mark end of entry function
*/
-#define ENTRY_FUNCTION_END(symbol, dtb) \
+#define ENTRY_FUNCTION_END(symbol, dtb, ram_size) \
mips_nmon; \
copy_to_link_location symbol; \
stack_setup; \
\
la a0, __dtb_ ## dtb##_start; \
la a1, __dtb_ ## dtb##_end; \
+ li a2, ram_size; \
la v0, pbl_main_entry; \
jal v0; \
nop; \
diff --git a/arch/mips/include/asm/pbl_macros.h b/arch/mips/include/asm/pbl_macros.h
index e78d1afe6a..c62910ff60 100644
--- a/arch/mips/include/asm/pbl_macros.h
+++ b/arch/mips/include/asm/pbl_macros.h
@@ -187,7 +187,7 @@ copy_loop_exit:
*
*/
-#if (STACK_BASE + STACK_SIZE) % 16 != 0
+#if (TEXT_BASE - MALLOC_SIZE) % 16 != 0
#error stack pointer must be 16-byte-aligned
#endif
@@ -196,7 +196,7 @@ copy_loop_exit:
.set noreorder
/* set stack pointer; reserve four 32-bit argument slots */
- la sp, STACK_BASE + STACK_SIZE - 16
+ la sp, (TEXT_BASE - MALLOC_SIZE - 16)
.set pop
.endm
diff --git a/arch/mips/include/asm/relocs.h b/arch/mips/include/asm/relocs.h
new file mode 100644
index 0000000000..0987c4bb13
--- /dev/null
+++ b/arch/mips/include/asm/relocs.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * MIPS Relocations
+ *
+ * Copyright (c) 2017 Imagination Technologies Ltd.
+ */
+
+#ifndef __ASM_MIPS_RELOCS_H__
+#define __ASM_MIPS_RELOCS_H__
+
+#define R_MIPS_NONE 0
+#define R_MIPS_32 2
+#define R_MIPS_26 4
+#define R_MIPS_HI16 5
+#define R_MIPS_LO16 6
+#define R_MIPS_PC16 10
+#define R_MIPS_64 18
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+#define R_MIPS_PC21_S2 60
+#define R_MIPS_PC26_S2 61
+
+#endif /* __ASM_MIPS_RELOCS_H__ */
diff --git a/arch/mips/include/asm/sections.h b/arch/mips/include/asm/sections.h
index 2b8c516038..8e004eaf34 100644
--- a/arch/mips/include/asm/sections.h
+++ b/arch/mips/include/asm/sections.h
@@ -1 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __ASM_MIPS_SECTIONS_H
+#define __ASM_MIPS_SECTIONS_H
+
#include <asm-generic/sections.h>
+
+/**
+ * __rel_start: Relocation data generated by the mips-relocs tool
+ *
+ * See arch/mips/lib/reloc.c for details on the format & use of this data.
+ */
+extern uint8_t __rel_start[];
+
+#endif
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 7372e8dc27..4ba42e526e 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -4,6 +4,8 @@ obj-y += cpu-probe.o
lwl-y += end.o
obj-y += traps.o
obj-y += genex.o
+obj-y += reloc.o
+obj-y += sections.o
obj-y += shutdown.o
obj-y += dma-default.o
diff --git a/arch/mips/lib/barebox.lds.S b/arch/mips/lib/barebox.lds.S
index 8ddf954517..58b0c5919d 100644
--- a/arch/mips/lib/barebox.lds.S
+++ b/arch/mips/lib/barebox.lds.S
@@ -13,6 +13,8 @@ SECTIONS
. = TEXT_BASE;
. = ALIGN(4);
+
+ .image_start : { *(.__image_start) }
.text :
{
_stext = .;
@@ -79,9 +81,28 @@ SECTIONS
.dtb : { BAREBOX_DTB() }
_edata = .;
+ .image_end : { *(.__image_end) }
+
. = ALIGN(4);
- __bss_start = .;
- .bss : { *(.bss*) }
- __bss_stop = .;
+ /*
+ * .rel must come last so that the mips-relocs tool can shrink
+ * the section size & the PT_LOAD program header filesz.
+ */
+ .data.reloc : {
+ __rel_start = .;
+ BYTE(0x0)
+ . += (32 * 1024) - 1;
+ }
+
_end = .;
+
+ .bss __rel_start (OVERLAY) : {
+ __bss_start = .;
+ *(.sbss.*)
+ *(.bss.*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_stop = .;
+ }
+
}
diff --git a/arch/mips/lib/cpu-probe.c b/arch/mips/lib/cpu-probe.c
index cf63849743..2556a8b240 100644
--- a/arch/mips/lib/cpu-probe.c
+++ b/arch/mips/lib/cpu-probe.c
@@ -11,6 +11,9 @@
#include <asm/mipsregs.h>
#include <asm/cpu-info.h>
#include <asm/cpu.h>
+#include <memory.h>
+#include <asm-generic/memory_layout.h>
+#include <init.h>
const char *__cpu_name;
struct cpuinfo_mips cpu_data[1];
@@ -161,3 +164,14 @@ void cpu_probe(void)
break;
}
}
+
+unsigned long mips_stack_top;
+
+static int mips_request_stack(void)
+{
+ if (!request_sdram_region("stack", mips_stack_top - STACK_SIZE, STACK_SIZE))
+ pr_err("Error: Cannot request SDRAM region for stack\n");
+
+ return 0;
+}
+coredevice_initcall(mips_request_stack);
diff --git a/arch/mips/lib/pbl.lds.S b/arch/mips/lib/pbl.lds.S
index f1752ec720..75069b0c50 100644
--- a/arch/mips/lib/pbl.lds.S
+++ b/arch/mips/lib/pbl.lds.S
@@ -6,11 +6,14 @@
#include <asm-generic/barebox.lds.h>
#include <asm-generic/memory_layout.h>
+#include <linux/sizes.h>
+
+#define BASE (TEXT_BASE - SZ_2M)
OUTPUT_ARCH("mips")
SECTIONS
{
- . = HEAD_TEXT_BASE;
+ . = BASE;
PRE_IMAGE
@@ -38,21 +41,21 @@ SECTIONS
. = ALIGN(4);
.data : { *(.data*) }
- pbl_code_size = . - HEAD_TEXT_BASE;
+ pbl_code_size = . - BASE;
. = ALIGN(4);
__piggydata_start = .;
.piggydata : {
*(.piggydata)
}
- __piggydata_end = .;
+ __piggydata_end = . - BASE;
- pbl_image_size = . - HEAD_TEXT_BASE;
+ pbl_image_size = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss*) }
__bss_stop = .;
- pbl_memory_size = . - HEAD_TEXT_BASE;
+ pbl_memory_size = . - BASE;
_end = .;
}
diff --git a/arch/mips/lib/reloc.c b/arch/mips/lib/reloc.c
new file mode 100644
index 0000000000..9756d61666
--- /dev/null
+++ b/arch/mips/lib/reloc.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPS Relocation
+ *
+ * Copyright (c) 2017 Imagination Technologies Ltd.
+ *
+ * Relocation data, found in the .rel section, is generated by the mips-relocs
+ * tool & contains a record of all locations in the Barebox binary that need to
+ * be fixed up during relocation.
+ *
+ * The data is a sequence of unsigned integers, which are of somewhat arbitrary
+ * size. This is achieved by encoding integers as a sequence of bytes, each of
+ * which contains 7 bits of data with the most significant bit indicating
+ * whether any further bytes need to be read. The least significant bits of the
+ * integer are found in the first byte - ie. it somewhat resembles little
+ * endian.
+ *
+ * Each pair of two integers represents a relocation that must be applied. The
+ * first integer represents the type of relocation as a standard ELF relocation
+ * type (ie. R_MIPS_*). The second integer represents the offset at which to
+ * apply the relocation, relative to the previous relocation or for the first
+ * relocation the start of the relocated .text section.
+ *
+ * The end of the relocation data is indicated when type R_MIPS_NONE (0) is
+ * read, at which point no further integers should be read. That is, the
+ * terminating R_MIPS_NONE reloc includes no offset.
+ */
+
+#include <common.h>
+
+#include <asm/bitops.h>
+#include <asm/cache.h>
+#include <asm/cacheops.h>
+#include <asm/cpu.h>
+#include <asm/cpu-info.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/relocs.h>
+#include <asm/sections.h>
+#include <linux/sizes.h>
+#include <asm-generic/memory_layout.h>
+
+#define MAX_BSS_SIZE SZ_1M
+
+void main_entry(void *fdt, u32 fdt_size);
+void relocate_code(void *fdt, u32 fdt_size, u32 relocaddr);
+
+/**
+ * read_uint() - Read an unsigned integer from the buffer
+ * @buf: pointer to a pointer to the reloc buffer
+ *
+ * Read one whole unsigned integer from the relocation data pointed to by @buf,
+ * advancing @buf past the bytes encoding the integer.
+ *
+ * Returns: the integer read from @buf
+ */
+static unsigned long read_uint(uint8_t **buf)
+{
+ unsigned long val = 0;
+ unsigned int shift = 0;
+ uint8_t new;
+
+ do {
+ new = *(*buf)++;
+ val |= (new & 0x7f) << shift;
+ shift += 7;
+ } while (new & 0x80);
+
+ return val;
+}
+
+/**
+ * apply_reloc() - Apply a single relocation
+ * @type: the type of reloc (R_MIPS_*)
+ * @addr: the address that the reloc should be applied to
+ * @off: the relocation offset, ie. number of bytes we're moving Barebox by
+ *
+ * Apply a single relocation of type @type at @addr. This function is
+ * intentionally simple, and does the bare minimum needed to fixup the
+ * relocated Barebox - in particular, it does not check for overflows.
+ */
+static void apply_reloc(unsigned int type, void *addr, long off)
+{
+ uint32_t u32;
+
+ switch (type) {
+ case R_MIPS_26:
+ u32 = *(uint32_t *)addr;
+ u32 = (u32 & GENMASK(31, 26)) |
+ ((u32 + (off >> 2)) & GENMASK(25, 0));
+ *(uint32_t *)addr = u32;
+ break;
+
+ case R_MIPS_32:
+ *(uint32_t *)addr += off;
+ break;
+
+ case R_MIPS_64:
+ *(uint64_t *)addr += off;
+ break;
+
+ case R_MIPS_HI16:
+ *(uint32_t *)addr += off >> 16;
+ break;
+
+ default:
+ panic("Unhandled reloc type %u\n", type);
+ }
+}
+
+/**
+ * relocate_code() - Relocate Barebox, generally from flash to DDR
+ * @start_addr_sp: new stack pointer
+ * @new_gd: pointer to relocated global data
+ * @relocaddr: the address to relocate to
+ *
+ * Relocate Barebox from its current location (generally in flash) to a new one
+ * (generally in DDR). This function will copy the Barebox binary & apply
+ * relocations as necessary, then jump to board_init_r in the new build of
+ * Barebox. As such, this function does not return.
+ */
+void relocate_code(void *fdt, u32 fdt_size, u32 ram_size)
+{
+ unsigned long addr, length, bss_len;
+ u32 relocaddr, new_stack;
+ uint8_t *buf, *bss_start;
+ unsigned int type;
+ long off;
+
+ length = barebox_image_size + MAX_BSS_SIZE;
+ relocaddr = ALIGN_DOWN(ram_size - barebox_image_size, SZ_64K);
+ relocaddr = KSEG0ADDR(relocaddr);
+ new_stack = relocaddr - MALLOC_SIZE - 16;
+
+ /*
+ * Ensure that we're relocating by an offset which is a multiple of
+ * 64KiB, ie. doesn't change the least significant 16 bits of any
+ * addresses. This allows us to discard R_MIPS_LO16 relocs, saving
+ * space in the Barebox binary & complexity in handling them.
+ */
+ off = relocaddr - (unsigned long)__image_start;
+ if (off & 0xffff)
+ panic("Mis-aligned relocation\n");
+
+ /* Copy Barebox to RAM */
+ memcpy((void *)relocaddr, __image_start, length);
+
+ /* Now apply relocations to the copy in RAM */
+ buf = __rel_start;
+ addr = relocaddr;
+ while (true) {
+ type = read_uint(&buf);
+ if (type == R_MIPS_NONE)
+ break;
+
+ addr += read_uint(&buf) << 2;
+ apply_reloc(type, (void *)addr, off);
+ }
+
+ /* Ensure the icache is coherent */
+ flush_cache_all();
+
+ /* Clear the .bss section */
+ bss_start = (uint8_t *)((unsigned long)__bss_start + off);
+ bss_len = (unsigned long)&__bss_stop - (unsigned long)__bss_start;
+ memset(bss_start, 0, bss_len);
+
+ __asm__ __volatile__ (
+ "move $a0, %0\n"
+ " move $a1, %1\n"
+ " move $31, $0\n"
+ " move $sp, %2\n"
+ " jr %3\n"
+ : /* no outputs */
+ : "r"(fdt),
+ "r"(fdt_size),
+ "r"(new_stack),
+ "r"((unsigned long)main_entry + off));
+
+ /* Since we jumped to the new Barebox above, we won't get here */
+ unreachable();
+}
diff --git a/arch/mips/lib/sections.c b/arch/mips/lib/sections.c
new file mode 100644
index 0000000000..66d559f94a
--- /dev/null
+++ b/arch/mips/lib/sections.c
@@ -0,0 +1,9 @@
+#include <common.h>
+#include <asm/sections.h>
+#include <linux/types.h>
+
+char _text[0] __attribute__((section("._text")));
+char __bss_start[0] __attribute__((section(".__bss_start")));
+char __bss_stop[0] __attribute__((section(".__bss_stop")));
+char __image_start[0] __attribute__((section(".__image_start")));
+char __image_end[0] __attribute__((section(".__image_end")));
diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile
index 05ec2438ac..2fb9b14422 100644
--- a/arch/ppc/Makefile
+++ b/arch/ppc/Makefile
@@ -15,7 +15,7 @@ board-$(CONFIG_MACH_PHYCORE_MPC5200B_TINY) := pcm030
board-$(CONFIG_P1010RDB) := freescale-p1010rdb
board-$(CONFIG_P2020RDB) := freescale-p2020rdb
board-$(CONFIG_P1022DS) := freescale-p1022ds
-board-$(CONFIG_DA923RC) := geip-da923rc
+board-$(CONFIG_DA923RC) := owc-da923rc
machine-$(CONFIG_ARCH_MPC5200) := mpc5xxx
machine-$(CONFIG_ARCH_MPC85XX) := mpc85xx
diff --git a/arch/ppc/boards/geip-da923rc/Makefile b/arch/ppc/boards/owc-da923rc/Makefile
index 7177bfac2c..4cf6c5cf3c 100644
--- a/arch/ppc/boards/geip-da923rc/Makefile
+++ b/arch/ppc/boards/owc-da923rc/Makefile
@@ -4,4 +4,4 @@ obj-y += law.o
obj-y += ddr.o
obj-y += nand.o
obj-y += product_data.o
-bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-geip-da923rc
+bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-owc-da923rc
diff --git a/arch/ppc/boards/geip-da923rc/config.h b/arch/ppc/boards/owc-da923rc/config.h
index 3895324e95..3895324e95 100644
--- a/arch/ppc/boards/geip-da923rc/config.h
+++ b/arch/ppc/boards/owc-da923rc/config.h
diff --git a/arch/ppc/boards/geip-da923rc/da923rc.c b/arch/ppc/boards/owc-da923rc/da923rc.c
index 6ec4ee2b6c..2665a15091 100644
--- a/arch/ppc/boards/geip-da923rc/da923rc.c
+++ b/arch/ppc/boards/owc-da923rc/da923rc.c
@@ -1,5 +1,6 @@
/*
* Copyright 2013 GE Intelligent Platforms, Inc.
+ * Copyright 2019 Abaco Systems, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -11,7 +12,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * GEIP DA923RC/GBX460 board support.
+ * Abaco Systems DA923RC/GBX460 board support.
*/
#include <common.h>
@@ -61,7 +62,7 @@ static struct board_info binfo;
static int board_eth_init(void)
{
void __iomem *gur = IOMEM(MPC85xx_GUTS_ADDR);
- struct ge_product_data product;
+ struct owc_product_data product;
int st;
/* Toggle eth0 reset pin */
@@ -74,7 +75,7 @@ static int board_eth_init(void)
in_be32(gur + MPC85xx_DEVDISR_OFFSET) &
~MPC85xx_DEVDISR_TSEC3);
- st = ge_get_product_data(&product);
+ st = owc_get_product_data(&product);
if (((product.v2.mac.count > 0) && (product.v2.mac.count <= MAX_MAC))
&& (st == 0))
eth_register_ethaddr(0, (const char *)&product.v2.mac.mac[0]);
@@ -98,7 +99,7 @@ static int da923rc_devices_init(void)
board_eth_init();
if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC))
- defaultenv_append_directory(defaultenv_geip_da923rc);
+ defaultenv_append_directory(defaultenv_owc_da923rc);
return 0;
}
diff --git a/arch/ppc/boards/geip-da923rc/ddr.c b/arch/ppc/boards/owc-da923rc/ddr.c
index fc0c50cdcd..fc0c50cdcd 100644
--- a/arch/ppc/boards/geip-da923rc/ddr.c
+++ b/arch/ppc/boards/owc-da923rc/ddr.c
diff --git a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/boot b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/boot
index ce7da18e63..ce7da18e63 100644
--- a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/boot
+++ b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/boot
diff --git a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/init b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/init
index 80cc2cffb3..80cc2cffb3 100644
--- a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/bin/init
+++ b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/bin/init
diff --git a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/config b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/config
index 79e2606a71..79e2606a71 100644
--- a/arch/ppc/boards/geip-da923rc/defaultenv-geip-da923rc/config
+++ b/arch/ppc/boards/owc-da923rc/defaultenv-owc-da923rc/config
diff --git a/arch/ppc/boards/geip-da923rc/law.c b/arch/ppc/boards/owc-da923rc/law.c
index 3d32c7e677..3d32c7e677 100644
--- a/arch/ppc/boards/geip-da923rc/law.c
+++ b/arch/ppc/boards/owc-da923rc/law.c
diff --git a/arch/ppc/boards/geip-da923rc/nand.c b/arch/ppc/boards/owc-da923rc/nand.c
index 550d790570..550d790570 100644
--- a/arch/ppc/boards/geip-da923rc/nand.c
+++ b/arch/ppc/boards/owc-da923rc/nand.c
diff --git a/arch/ppc/boards/geip-da923rc/product_data.c b/arch/ppc/boards/owc-da923rc/product_data.c
index eda10a4178..5135afdd2a 100644
--- a/arch/ppc/boards/geip-da923rc/product_data.c
+++ b/arch/ppc/boards/owc-da923rc/product_data.c
@@ -1,5 +1,6 @@
/*
* Copyright 2013 GE Intelligent Platforms Inc.
+ * Copyright 2019 Abaco Systems Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -21,7 +22,7 @@
#include <mach/fsl_i2c.h>
#include "product_data.h"
-static int ge_pd_header_check(unsigned short header)
+static int owc_pd_header_check(unsigned short header)
{
if (header != 0xa5a5)
return -1;
@@ -29,12 +30,12 @@ static int ge_pd_header_check(unsigned short header)
return 0;
}
-static int ge_is_data_valid(struct ge_product_data *v)
+static int owc_is_data_valid(struct owc_product_data *v)
{
int crc, ret = 0;
const unsigned char *p = (const unsigned char *)v;
- if (ge_pd_header_check(v->v1.pdh.tag))
+ if (owc_pd_header_check(v->v1.pdh.tag))
return -1;
switch (v->v1.pdh.version) {
@@ -56,7 +57,7 @@ static int ge_is_data_valid(struct ge_product_data *v)
return ret;
}
-int ge_get_product_data(struct ge_product_data *productp)
+int owc_get_product_data(struct owc_product_data *productp)
{
struct i2c_adapter *adapter;
struct i2c_client client;
@@ -70,14 +71,14 @@ int ge_get_product_data(struct ge_product_data *productp)
sizeof(unsigned short));
/* If there is no valid header, it may be a 16-bit eeprom. */
- if (ge_pd_header_check(productp->v1.pdh.tag))
+ if (owc_pd_header_check(productp->v1.pdh.tag))
width = I2C_ADDR_16_BIT;
ret = i2c_read_reg(&client, width, (uint8_t *) productp,
- sizeof(struct ge_product_data));
+ sizeof(struct owc_product_data));
- if (ret == sizeof(struct ge_product_data))
- ret = ge_is_data_valid(productp);
+ if (ret == sizeof(struct owc_product_data))
+ ret = owc_is_data_valid(productp);
return ret;
}
diff --git a/arch/ppc/boards/geip-da923rc/product_data.h b/arch/ppc/boards/owc-da923rc/product_data.h
index f172fb5692..cbbb8d377f 100644
--- a/arch/ppc/boards/geip-da923rc/product_data.h
+++ b/arch/ppc/boards/owc-da923rc/product_data.h
@@ -1,5 +1,6 @@
/*
* Copyright 2013 GE Intelligent Platforms, Inc.
+ * Copyright 2019 Abaco Systems, Inc.
*
* The product data structure and function prototypes.
*
@@ -51,12 +52,12 @@ struct __attribute__ ((__packed__)) product_data_v2 {
int crc32;
};
-struct __attribute__ ((__packed__)) ge_product_data {
+struct __attribute__ ((__packed__)) owc_product_data {
union {
struct product_data_v1 v1;
struct product_data_v2 v2;
};
};
-extern int ge_get_product_data(struct ge_product_data *productp);
+extern int owc_get_product_data(struct owc_product_data *productp);
extern void da923rc_boardinfo_get(struct board_info *bi);
diff --git a/arch/ppc/boards/geip-da923rc/tlb.c b/arch/ppc/boards/owc-da923rc/tlb.c
index 889e2743b7..889e2743b7 100644
--- a/arch/ppc/boards/geip-da923rc/tlb.c
+++ b/arch/ppc/boards/owc-da923rc/tlb.c
diff --git a/arch/ppc/configs/da923rc_defconfig b/arch/ppc/configs/owc_da923rc_defconfig
index 6c0a81e8eb..183ddb15f9 100644
--- a/arch/ppc/configs/da923rc_defconfig
+++ b/arch/ppc/configs/owc_da923rc_defconfig
@@ -3,7 +3,7 @@ CONFIG_DA923RC=y
CONFIG_BAREBOX_MAX_IMAGE_SIZE=0x80000
CONFIG_MALLOC_SIZE=0x4000000
CONFIG_RELOCATABLE=y
-CONFIG_PROMPT="GE> "
+CONFIG_PROMPT="OWBOOT> "
CONFIG_BAUDRATE=9600
CONFIG_GLOB=y
CONFIG_CMDLINE_EDITING=y
diff --git a/arch/sandbox/os/common.c b/arch/sandbox/os/common.c
index 665e8194ef..a7ea8f2d3b 100644
--- a/arch/sandbox/os/common.c
+++ b/arch/sandbox/os/common.c
@@ -94,7 +94,7 @@ int linux_tstc(int fd)
return 0;
}
-int ctrlc(void)
+int arch_ctrlc(void)
{
char chr;
diff --git a/commands/Kconfig b/commands/Kconfig
index 4f5d84ac18..039fd7d1ac 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1964,6 +1964,16 @@ config CMD_BAREBOX_UPDATE
-y autom. use 'yes' when asking confirmations
-f LEVEL set force level
+config CMD_BLOBGEN
+ bool
+ select BLOBGEN
+ prompt "blobgen"
+ help
+ Provides the "blobgen" command. This command encrypts and decrypts
+ plaintext to/from blobs. This is done with hardware crypto engines,
+ so this command is only useful when you also enable a blobgen capable
+ driver.
+
config CMD_FIRMWARELOAD
bool
select FIRMWARE
diff --git a/commands/Makefile b/commands/Makefile
index 358671bb5b..e69fb5046f 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_CMD_LINUX_EXEC) += linux_exec.o
obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o
obj-$(CONFIG_CMD_GLOBAL) += global.o
obj-$(CONFIG_CMD_DMESG) += dmesg.o
+obj-$(CONFIG_CMD_BLOBGEN) += blobgen.o
obj-$(CONFIG_CMD_BASENAME) += basename.o
obj-$(CONFIG_CMD_HAB) += hab.o
obj-$(CONFIG_CMD_DIRNAME) += dirname.o
diff --git a/commands/blobgen.c b/commands/blobgen.c
new file mode 100644
index 0000000000..49107d037c
--- /dev/null
+++ b/commands/blobgen.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <blobgen.h>
+#include <environment.h>
+
+static int do_blobgen(int argc, char *argv[])
+{
+ bool do_encrypt = false, do_decrypt = false;
+ int opt;
+ const char *varname = NULL;
+ const char *modifier = NULL;
+ const char *blobdev = NULL;
+ struct blobgen *bg;
+ int plainsize;
+ int ret;
+ const char *message = NULL;
+
+ while ((opt = getopt(argc, argv, "edm:V:b:")) > 0) {
+ switch (opt) {
+ case 'e':
+ do_encrypt = true;
+ break;
+ case 'd':
+ do_decrypt = true;
+ break;
+ case 'm':
+ modifier = optarg;
+ break;
+ case 'V':
+ varname = optarg;
+ break;
+ case 'b':
+ blobdev = optarg;
+ break;
+ }
+ }
+
+ if (!varname) {
+ printf("varname not specified\n");
+ return -EINVAL;
+ }
+
+ if (!modifier) {
+ printf("Modifier not specified\n");
+ return -EINVAL;
+ }
+
+ bg = blobgen_get(blobdev);
+ if (!bg) {
+ printf("blobdev \"%s\" not found\n", blobdev);
+ return -ENOENT;
+ }
+
+ if (do_encrypt && do_decrypt) {
+ printf("Both encrypt and decrypt given\n");
+ return -EINVAL;
+ }
+
+ if (!do_encrypt && !do_decrypt) {
+ printf("Specify either -e or -d option\n");
+ return -EINVAL;
+ }
+
+ if (argc > optind) {
+ message = argv[optind];
+ } else {
+ printf("No message to %scrypt provided\n",
+ do_encrypt ? "en" : "de");
+ return -EINVAL;
+ }
+
+ if (do_encrypt) {
+ ret = blob_encrypt_to_env(bg, modifier, message, strlen(message),
+ varname);
+ if (ret)
+ return ret;
+ }
+
+ if (do_decrypt) {
+ void *plain;
+ char *str;
+
+ ret = blob_decrypt_from_base64(bg, modifier, message, &plain,
+ &plainsize);
+ if (ret)
+ return ret;
+
+ str = malloc(plainsize + 1);
+ if (!str)
+ return -ENOMEM;
+
+ memcpy(str, plain, plainsize);
+ str[plainsize] = 0;
+
+ setenv(varname, str);
+ free(plain);
+ free(str);
+ }
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(blobgen)
+BAREBOX_CMD_HELP_TEXT("This command utilizes hardware crypto engines to en/decrypt")
+BAREBOX_CMD_HELP_TEXT("data blobs.")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-e\t", "encrypt")
+BAREBOX_CMD_HELP_OPT("-d\t", "decrypt")
+BAREBOX_CMD_HELP_OPT("-m <modifier>", "Set modifier")
+BAREBOX_CMD_HELP_OPT("-V <varname>", "specify variable name to set with the result")
+BAREBOX_CMD_HELP_OPT("-b <blobdev>", "specify blob device to use")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(blobgen)
+ .cmd = do_blobgen,
+ BAREBOX_CMD_DESC("en/decrypt blobs")
+ BAREBOX_CMD_OPTS("[-edmVb] <plaintext/ciphertext>")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_blobgen_help)
+BAREBOX_CMD_END
diff --git a/commands/hwclock.c b/commands/hwclock.c
index 5073618675..1b5c2cd100 100644
--- a/commands/hwclock.c
+++ b/commands/hwclock.c
@@ -175,7 +175,7 @@ BAREBOX_CMD_HELP_START(hwclock)
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-f NAME\t\t\t", "RTC device name (default rtc0)")
BAREBOX_CMD_HELP_OPT ("-e VARNAME\t\t", "store RTC readout into variable VARNAME")
-BAREBOX_CMD_HELP_OPT ("-n NTPSERVER\t", "set RTC from NTP server")
+BAREBOX_CMD_HELP_OPT ("-n NTPSERVER\t\t", "set RTC from NTP server")
BAREBOX_CMD_HELP_OPT ("-s ccyymmddHHMM[.SS]\t", "set time")
BAREBOX_CMD_HELP_END
diff --git a/commands/spi.c b/commands/spi.c
index 7bf193b0bb..55a0e255af 100644
--- a/commands/spi.c
+++ b/commands/spi.c
@@ -62,21 +62,21 @@ static int do_spi(int argc, char *argv[])
return COMMAND_ERROR_USAGE;
- spi.master = spi_get_master(bus);
- if (!spi.master) {
+ spi.controller = spi_get_controller(bus);
+ if (!spi.controller) {
printf("spi bus %d not found\n", bus);
return -ENODEV;
}
- if (spi.chip_select >= spi.master->num_chipselect) {
- printf("spi chip select (%d) >= master num chipselect (%d)\n",
- spi.chip_select, spi.master->num_chipselect);
+ if (spi.chip_select >= spi.controller->num_chipselect) {
+ printf("spi chip select (%d) >= controller num chipselect (%d)\n",
+ spi.chip_select, spi.controller->num_chipselect);
return -EINVAL;
}
- ret = spi.master->setup(&spi);
+ ret = spi.controller->setup(&spi);
if (ret) {
- printf("can not setup the master (%d)\n", ret);
+ printf("can not setup the controller (%d)\n", ret);
return ret;
}
@@ -93,7 +93,7 @@ static int do_spi(int argc, char *argv[])
byte_per_word = max(spi.bits_per_word / 8, 1);
if (verbose) {
printf("device config\n");
- printf(" bus_num = %d\n", spi.master->bus_num);
+ printf(" bus_num = %d\n", spi.controller->bus_num);
printf(" max_speed_hz = %d\n", spi.max_speed_hz);
printf(" chip_select = %d\n", spi.chip_select);
printf(" mode = 0x%x\n", spi.mode);
diff --git a/commands/timeout.c b/commands/timeout.c
index d197cedd8b..db88900287 100644
--- a/commands/timeout.c
+++ b/commands/timeout.c
@@ -61,7 +61,7 @@ static int do_timeout(int argc, char *argv[])
return COMMAND_ERROR_USAGE;
timeout = simple_strtoul(argv[optind], NULL, 0);
- ret = console_countdown(timeout, flags, str);
+ ret = console_countdown(timeout, flags, NULL, str);
if (varname && str[0])
setenv(varname, str);
diff --git a/common/bbu.c b/common/bbu.c
index 5cb09e4eb0..00bec32a86 100644
--- a/common/bbu.c
+++ b/common/bbu.c
@@ -299,6 +299,8 @@ static int bbu_std_file_handler(struct bbu_handler *handler,
return -EINVAL;
}
+ device_detect_by_name(devpath_to_name(data->devicefile));
+
ret = stat(data->devicefile, &s);
if (ret) {
oflags |= O_CREAT;
@@ -361,7 +363,7 @@ err_close:
* Return: 0 if successful, negative error code otherwise
*/
int bbu_register_std_file_update(const char *name, unsigned long flags,
- char *devicefile, enum filetype imagetype)
+ const char *devicefile, enum filetype imagetype)
{
struct bbu_std *std;
struct bbu_handler *handler;
diff --git a/common/console.c b/common/console.c
index 47ccf2e54d..406722a1da 100644
--- a/common/console.c
+++ b/common/console.c
@@ -574,18 +574,60 @@ void console_flush(void)
}
EXPORT_SYMBOL(console_flush);
-#ifndef ARCH_HAS_CTRLC
+static int ctrlc_abort;
+static int ctrlc_allowed;
+
+void ctrlc_handled(void)
+{
+ ctrlc_abort = 0;
+}
+
/* test if ctrl-c was pressed */
-int ctrlc (void)
+int ctrlc(void)
{
+ int ret = 0;
+
+ if (!ctrlc_allowed)
+ return 0;
+
+ if (ctrlc_abort)
+ return 1;
+
poller_call();
+#ifdef ARCH_HAS_CTRLC
+ ret = arch_ctrlc();
+#else
if (tstc() && getchar() == 3)
- return 1;
- return 0;
+ ret = 1;
+#endif
+
+ if (ret)
+ ctrlc_abort = 1;
+
+ return ret;
}
EXPORT_SYMBOL(ctrlc);
-#endif /* ARCH_HAS_CTRC */
+
+static int console_ctrlc_init(void)
+{
+ globalvar_add_simple_bool("console.ctrlc_allowed", &ctrlc_allowed);
+ return 0;
+}
+device_initcall(console_ctrlc_init);
+
+void console_ctrlc_allow(void)
+{
+ ctrlc_allowed = 1;
+}
+
+void console_ctrlc_forbid(void)
+{
+ ctrlc_allowed = 0;
+}
+
+BAREBOX_MAGICVAR_NAMED(global_console_ctrlc_allowed, global.console.ctrlc_allowed,
+ "If true, scripts can be aborted with ctrl-c");
BAREBOX_MAGICVAR_NAMED(global_linux_bootargs_console, global.linux.bootargs.console,
"console= argument for Linux from the stdout-path property in /chosen node");
diff --git a/common/console_countdown.c b/common/console_countdown.c
index 36da1ce577..8d09894c30 100644
--- a/common/console_countdown.c
+++ b/common/console_countdown.c
@@ -30,7 +30,22 @@ void console_countdown_abort(void)
console_countdown_timeout_abort = true;
}
-int console_countdown(int timeout_s, unsigned flags, char *out_key)
+static int key_in_list(char key, const char *keys)
+{
+ if (!keys)
+ return false;
+
+ while (*keys) {
+ if (key == *keys)
+ return true;
+ keys++;
+ }
+
+ return false;
+}
+
+int console_countdown(int timeout_s, unsigned flags, const char *keys,
+ char *out_key)
{
uint64_t start, second;
int countdown, ret = -EINTR;
@@ -48,6 +63,8 @@ int console_countdown(int timeout_s, unsigned flags, char *out_key)
if (tstc()) {
key = getchar();
if (key >= 0) {
+ if (key_in_list(key, keys))
+ goto out;
if (flags & CONSOLE_COUNTDOWN_ANYKEY)
goto out;
if (flags & CONSOLE_COUNTDOWN_RETURN && key == '\n')
diff --git a/common/ddr_spd.c b/common/ddr_spd.c
index 0ba5eaceae..23df3e7119 100644
--- a/common/ddr_spd.c
+++ b/common/ddr_spd.c
@@ -325,7 +325,7 @@ void ddr_spd_print(uint8_t *record)
printf("Error module type\n");
printf("%-48s ", "DRAM Package ");
- if ((s->mod_ranks & 0x10) == 1)
+ if ((s->mod_ranks & 0x10) != 0)
printf("Stack\n");
else
printf("Planar\n");
diff --git a/common/filetype.c b/common/filetype.c
index fb029a7739..e2d707b156 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -75,6 +75,8 @@ static const struct filetype_str filetype_str[] = {
[filetype_elf] = { "ELF", "elf" },
[filetype_imx_image_v1] = { "i.MX image (v1)", "imx-image-v1" },
[filetype_imx_image_v2] = { "i.MX image (v2)", "imx-image-v2" },
+ [filetype_layerscape_image] = { "Layerscape image", "layerscape-PBL" },
+ [filetype_layerscape_qspi_image] = { "Layerscape QSPI image", "layerscape-qspi-PBL" },
};
const char *file_type_to_string(enum filetype f)
@@ -329,6 +331,11 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
if (is_sparse_image(_buf))
return filetype_android_sparse;
+ if (buf[0] == 0x55aa55aa && buf[1] == 0x0001ee01)
+ return filetype_layerscape_image;
+ if (buf[0] == 0x01ee0100 && buf[1] == 0xaa55aa55)
+ return filetype_layerscape_qspi_image;
+
if (bufsize < 64)
return filetype_unknown;
diff --git a/common/hush.c b/common/hush.c
index d2f9cc70f5..dab9b04081 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -1734,7 +1734,7 @@ static int parse_stream_outer(struct p_context *ctx, struct in_str *inp, int fla
return 1;
}
b_free(&temp);
- } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
+ } while (!ctrlc() && rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
return code;
}
@@ -1932,6 +1932,7 @@ int run_shell(void)
login();
do {
+ ctrlc_handled();
setup_file_in_str(&input);
rcode = parse_stream_outer(&ctx, &input, FLAG_PARSE_SEMICOLON);
if (rcode < -1) {
diff --git a/common/parser.c b/common/parser.c
index 397d268da1..fb9ef42e7f 100644
--- a/common/parser.c
+++ b/common/parser.c
@@ -283,6 +283,7 @@ int run_shell(void)
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
+ ctrlc_handled();
}
}
return 0;
diff --git a/common/startup.c b/common/startup.c
index 28edee4fce..9fac0eabbd 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -42,6 +42,9 @@
#include <asm/sections.h>
#include <uncompress.h>
#include <globalvar.h>
+#include <console_countdown.h>
+#include <environment.h>
+#include <linux/ctype.h>
extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[],
__barebox_initcalls_end[];
@@ -143,16 +146,172 @@ static int load_environment(void)
environment_initcall(load_environment);
#endif
+static int global_autoboot_abort_key;
+static const char * const global_autoboot_abort_keys[] = {
+ "any",
+ "ctrl-c",
+};
+static int global_autoboot_timeout = 3;
+static char *global_boot_default;
+static char *global_editcmd;
+static char *global_linux_bootargs_base;
+static char *global_linux_bootargs_console;
+static char *global_linux_bootargs_dyn_ip;
+static char *global_linux_bootargs_dyn_root;
+static char *global_user;
+
+static bool test_abort(void)
+{
+ bool do_abort = false;
+ int c, ret;
+ char key;
+
+ while (tstc()) {
+ c = getchar();
+ if (tolower(c) == 'q' || c == 3)
+ do_abort = true;
+ }
+
+ if (!do_abort)
+ return false;
+
+ printf("Abort init sequence? (y/n)\n"
+ "Will continue with init sequence in:");
+
+ ret = console_countdown(5, CONSOLE_COUNTDOWN_EXTERN, "yYnN", &key);
+ if (!ret)
+ return false;
+
+ if (tolower(key) == 'y')
+ return true;
+
+ return false;
+}
+
+static int run_init(void)
+{
+ DIR *dir;
+ struct dirent *d;
+ const char *initfile = "/env/bin/init";
+ const char *initdir = "/env/init";
+ const char *menufile = "/env/menu/mainmenu";
+ struct stat s;
+ unsigned flags = CONSOLE_COUNTDOWN_EXTERN;
+ unsigned char outkey;
+ int ret;
+ bool menu_exists;
+ bool env_bin_init_exists;
+ char *abortkeys = NULL;
+
+ setenv("PATH", "/env/bin");
+
+ /* Run legacy /env/bin/init if it exists */
+ env_bin_init_exists = stat(initfile, &s) == 0;
+ if (env_bin_init_exists) {
+ pr_info("running %s...\n", initfile);
+ run_command(initfile);
+ return 0;
+ }
+
+ global_editcmd = xstrdup("sedit");
+ global_user = xstrdup("none");
+ globalvar_add_simple_string("user", &global_user);
+ global_boot_default = xstrdup("net");
+
+ globalvar_add_simple_enum("autoboot_abort_key",
+ &global_autoboot_abort_key,
+ global_autoboot_abort_keys,
+ ARRAY_SIZE(global_autoboot_abort_keys));
+ globalvar_add_simple_int("autoboot_timeout",
+ &global_autoboot_timeout, "%u");
+ globalvar_add_simple_string("boot.default", &global_boot_default);
+ globalvar_add_simple_string("editcmd", &global_editcmd);
+ globalvar_add_simple_string("linux.bootargs.base",
+ &global_linux_bootargs_base);
+ globalvar_add_simple_string("linux.bootargs.console",
+ &global_linux_bootargs_console);
+ globalvar_add_simple_string("linux.bootargs.dyn.ip",
+ &global_linux_bootargs_dyn_ip);
+ globalvar_add_simple_string("linux.bootargs.dyn.root",
+ &global_linux_bootargs_dyn_root);
+
+ /* Unblank console cursor */
+ printf("\e[?25h");
+
+ if (test_abort()) {
+ pr_info("Init sequence aborted\n");
+ return -EINTR;
+ }
+
+ /* Run scripts in /env/init/ */
+ dir = opendir(initdir);
+ if (dir) {
+ char *scr;
+
+ while ((d = readdir(dir))) {
+ if (*d->d_name == '.')
+ continue;
+
+ pr_debug("Executing '%s/%s'...\n", initdir, d->d_name);
+ scr = basprintf("source %s/%s", initdir, d->d_name);
+ run_command(scr);
+ free(scr);
+ }
+
+ closedir(dir);
+ }
+
+ menu_exists = stat(menufile, &s) == 0;
+
+ if (menu_exists) {
+ printf("\nHit m for menu or %s to stop autoboot: ",
+ global_autoboot_abort_keys[global_autoboot_abort_key]);
+ abortkeys = "m";
+ } else {
+ printf("\nHit %s to stop autoboot: ",
+ global_autoboot_abort_keys[global_autoboot_abort_key]);
+ }
+
+ switch (global_autoboot_abort_key) {
+ case 0:
+ flags |= CONSOLE_COUNTDOWN_ANYKEY;
+ break;
+ case 1:
+ flags |= CONSOLE_COUNTDOWN_CTRLC;
+ break;
+ default:
+ break;
+ }
+
+ ret = console_countdown(global_autoboot_timeout, flags, abortkeys,
+ &outkey);
+
+ if (ret == 0)
+ run_command("boot");
+
+ console_ctrlc_allow();
+
+ if (menu_exists) {
+ if (outkey == 'm')
+ run_command(menufile);
+
+ printf("Enter 'exit' to get back to the menu\n");
+ run_shell();
+ run_command(menufile);
+ }
+
+ return 0;
+}
+
int (*barebox_main)(void);
void __noreturn start_barebox(void)
{
initcall_t *initcall;
int result;
- struct stat s;
- if (!IS_ENABLED(CONFIG_SHELL_NONE))
- barebox_main = run_shell;
+ if (!IS_ENABLED(CONFIG_SHELL_NONE) && IS_ENABLED(CONFIG_COMMAND_SUPPORT))
+ barebox_main = run_init;
for (initcall = __barebox_initcalls_start;
initcall < __barebox_initcalls_end; initcall++) {
@@ -165,25 +324,16 @@ void __noreturn start_barebox(void)
pr_debug("initcalls done\n");
- if (IS_ENABLED(CONFIG_COMMAND_SUPPORT)) {
- pr_info("running /env/bin/init...\n");
-
- if (!stat("/env/bin/init", &s))
- run_command("source /env/bin/init");
- else
- pr_err("/env/bin/init not found\n");
- }
+ if (barebox_main)
+ barebox_main();
- if (!barebox_main) {
- pr_err("No main function! aborting.\n");
+ if (IS_ENABLED(CONFIG_SHELL_NONE)) {
+ pr_err("Nothing left to do\n");
hang();
+ } else {
+ while (1)
+ run_shell();
}
-
- /* main_loop() can return to retry autoboot, if so just run it again. */
- for (;;)
- barebox_main();
-
- /* NOTREACHED - no way out of command loop except booting */
}
void __noreturn hang (void)
diff --git a/crypto/digest.c b/crypto/digest.c
index 2c4de2e4f1..360c261e40 100644
--- a/crypto/digest.c
+++ b/crypto/digest.c
@@ -233,17 +233,18 @@ static int digest_update_from_fd(struct digest *d, int fd,
}
while (size) {
- const ssize_t now = read(fd, buf, PAGE_SIZE);
- if (now < 0) {
- ret = now;
+ unsigned long now = min_t(typeof(size), PAGE_SIZE, size);
+
+ ret = read(fd, buf, now);
+ if (ret < 0) {
perror("read");
goto out_free;
}
- if (!now)
+ if (!ret)
break;
- ret = digest_update_interruptible(d, buf, now);
+ ret = digest_update_interruptible(d, buf, ret);
if (ret)
goto out_free;
diff --git a/defaultenv/defaultenv-2-base/bin/init b/defaultenv/defaultenv-2-base/bin/init
deleted file mode 100644
index 8d02e3d3ab..0000000000
--- a/defaultenv/defaultenv-2-base/bin/init
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/bin/sh
-
-export PATH=/env/bin
-
-global hostname
-global user
-global autoboot_timeout
-global autoboot_abort_key
-global boot.default
-global linux.bootargs.base
-global linux.bootargs.console
-#linux.bootargs.dyn.* will be cleared at the beginning of boot
-global linux.bootargs.dyn.ip
-global linux.bootargs.dyn.root
-global editcmd
-
-[ -z "${global.hostname}" ] && global.hostname=generic
-[ -z "${global.user}" ] && global.user=none
-magicvar -a global.user "username (used in network filenames)"
-[ -z "${global.autoboot_timeout}" ] && global.autoboot_timeout=3
-magicvar -a global.autoboot_timeout "timeout in seconds before automatic booting"
-[ -z "${global.autoboot_abort_key}" ] && global.autoboot_abort_key=any
-magicvar -a global.autoboot_abort_key "key to abort automatic booting (valid options: any, ctrl-c)"
-[ -z "${global.boot.default}" ] && global.boot.default=net
-[ -z "${global.editcmd}" ] && global.editcmd=sedit
-
-[ -e /env/config-board ] && /env/config-board
-/env/config
-
-# allow to stop the boot before execute the /env/init/*
-# but without waiting
-timeout -s -a -v key 0
-autoboot="$?"
-
-echo -e -n "\e[?25h"
-if [ "${key}" = "q" ]; then
- exit
-fi
-
-for i in /env/init/*; do
- . $i
-done
-
-if [ "${global.autoboot_abort_key}" = "ctrl-c" ]; then
- abort_string="ctrl-c"
- abort_args="-c"
-else
- abort_string="any key"
- abort_args="-a"
-fi
-
-if [ -e /env/menu ]; then
- echo -e -n "\nHit m for menu or $abort_string to stop autoboot: "
-else
- echo -e -n "\nHit $abort_string to stop autoboot: "
-fi
-
-if [ "$autoboot" = 0 ]; then
- timeout $abort_args $global.autoboot_timeout -v key
- autoboot="$?"
-fi
-
-if [ "${key}" = "q" ]; then
- exit
-fi
-
-if [ "$autoboot" = 0 ]; then
- boot
-fi
-
-if [ -e /env/menu ]; then
- if [ "${key}" != "m" ]; then
- echo -e "\ntype exit to get to the menu"
- sh
- fi
- /env/menu/mainmenu
-fi
diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c
index baa42e14f4..ce4fbed68c 100644
--- a/drivers/clk/imx/clk-imx25.c
+++ b/drivers/clk/imx/clk-imx25.c
@@ -95,7 +95,7 @@ static int imx25_ccm_probe(struct device_d *dev)
writel((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 8) | (1 << 9) |
(1 << 10) | (1 << 15) | (1 << 19) | (1 << 21) | (1 << 22) |
- (1 << 23) | (1 << 24) | (1 << 28),
+ (1 << 23) | (1 << 24) | (1 << 25) | (1 << 28),
base + CCM_CGCR0);
writel((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 13) | (1 << 14) |
@@ -152,7 +152,9 @@ static int imx25_ccm_probe(struct device_d *dev)
clks[lcdc_ahb] = imx_clk_gate("lcdc_ahb", "ahb", base + CCM_CGCR0, 24);
clks[lcdc_ipg] = imx_clk_gate("lcdc_ipg", "ipg", base + CCM_CGCR1, 29);
clks[lcdc_ipg_per] = imx_clk_gate("lcdc_ipg_per", "per7", base + CCM_CGCR0, 7);
+ clks[scc_ipg] = imx_clk_gate("scc_ipg", "ipg", base + CCM_CGCR2, 5);
clks[rngb_ipg] = imx_clk_gate("rngb_ipg", "ipg", base + CCM_CGCR2, 3);
+ clks[dryice_ipg] = imx_clk_gate("dryice_ipg", "ipg", base + CCM_CGCR1, 8);
clkdev_add_physbase(clks[per15], MX25_UART1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[per15], MX25_UART2_BASE_ADDR, NULL);
@@ -176,6 +178,9 @@ static int imx25_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[lcdc_ipg_per], MX25_LCDC_BASE_ADDR, "per");
clkdev_add_physbase(clks[lcdc_ipg], MX25_LCDC_BASE_ADDR, "ipg");
clkdev_add_physbase(clks[lcdc_ahb], MX25_LCDC_BASE_ADDR, "ahb");
+ clkdev_add_physbase(clks[scc_ipg], MX25_SCC_BASE_ADDR, "ipg");
+ clkdev_add_physbase(clks[rngb_ipg], MX25_RNGB_BASE_ADDR, "ipg");
+ clkdev_add_physbase(clks[dryice_ipg], MX25_DRYICE_BASE_ADDR, NULL);
return 0;
}
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 1b1b881052..d70f4416cb 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -459,26 +459,30 @@ enum {
DDRMC_CR117_AXI0_FITYPEREG_SYNC = 0b01 << 16,
};
-static int vf610_switch_cpu_clock_to_500mhz(void)
+static bool vf610_cpu_clk_changeable(void)
{
- int ret;
-
/*
* When switching A5 CPU to 500Mhz we expect DDRC to be
* clocked by PLL2_PFD2 and the system to be configured in
* asynchronous mode.
- *
- * We also can't just use default PFD1 output of PLL1 due to
- * Errata e6235, so we have to re-clock the PLL itself and use
- * its output to clock the CPU directly.
*/
-
if (clk_get_parent(clk[VF610_CLK_DDR_SEL]) != clk[VF610_CLK_PLL2_PFD2]) {
- pr_warn("DDRC is clocked by PLL1, can't switch CPU clock");
- return -EINVAL;
+ pr_warn("DDRC is clocked by PLL1, can't switch CPU clock\n");
+ return false;
}
+ return true;
+}
+
+static int vf610_switch_cpu_clock_to_500mhz(void)
+{
+ int ret;
+
/*
+ * We can't just use default PFD1 output of PLL1 due to
+ * Errata e6235, so we have to re-clock the PLL itself and use
+ * its output to clock the CPU directly.
+ *
* Code below alters the frequency of PLL1, and doing so would
* require us to wait for PLL1 lock before proceeding to
* select it as a clock source again.
@@ -533,11 +537,6 @@ static int vf610_switch_cpu_clock_to_400mhz(void)
uint32_t cr117;
void * __iomem ddrmc = IOMEM(VF610_DDR_BASE_ADDR);
- if (clk_get_parent(clk[VF610_CLK_DDR_SEL]) != clk[VF610_CLK_PLL2_PFD2]) {
- pr_warn("DDRC is clocked by PLL1, can't switch CPU clock");
- return -EINVAL;
- }
-
ret = clk_set_parent(clk[VF610_CLK_PLL2_PFD_SEL], clk[VF610_CLK_PLL2_PFD2]);
if (ret < 0) {
pr_crit("Unable to re-parent '%s'\n",
@@ -595,10 +594,14 @@ static int vf610_switch_cpu_clock(void)
return 0;
case VF610_SPEED_500:
+ if (!vf610_cpu_clk_changeable())
+ return 0;
ret = vf610_switch_cpu_clock_to_500mhz();
break;
case VF610_SPEED_400:
+ if (!vf610_cpu_clk_changeable())
+ return 0;
ret = vf610_switch_cpu_clock_to_400mhz();
break;
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index b2709f00f8..77d3782bde 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -5,5 +5,6 @@ menuconfig CRYPTO_HW
if CRYPTO_HW
source "drivers/crypto/caam/Kconfig"
+source "drivers/crypto/imx-scc/Kconfig"
endif
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 67f968f76c..1999929bc2 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += imx-scc/
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 7bd6f3e23c..933b9c0592 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += ctrl.o error.o jr.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG) += caamrng.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) += rng_self_test.o
+obj-$(CONFIG_BLOBGEN) += caam-blobgen.o
diff --git a/drivers/crypto/caam/caam-blobgen.c b/drivers/crypto/caam/caam-blobgen.c
new file mode 100644
index 0000000000..acbe5a110d
--- /dev/null
+++ b/drivers/crypto/caam/caam-blobgen.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <base64.h>
+#include <blobgen.h>
+#include <crypto.h>
+#include <dma.h>
+#include <driver.h>
+#include <init.h>
+#include <fs.h>
+#include <fcntl.h>
+#include "intern.h"
+#include "desc.h"
+#include "desc_constr.h"
+#include "error.h"
+#include "jr.h"
+
+/*
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable
+ * blob.
+ */
+#define INITIAL_DESCSZ 16
+/* 32 bytes key blob + 16 bytes HMAC identifier */
+#define BLOB_OVERHEAD (32 + 16)
+#define KEYMOD_LENGTH 16
+#define RED_BLOB_LENGTH 64
+#define MAX_BLOB_LEN 4096
+#define DESC_LEN 64
+
+struct blob_job_result {
+ int err;
+};
+
+struct blob_priv {
+ struct blobgen bg;
+ u32 desc[DESC_LEN];
+ dma_addr_t dma_modifier;
+ dma_addr_t dma_plaintext;
+ dma_addr_t dma_ciphertext;
+};
+
+static struct blob_priv *to_blob_priv(struct blobgen *bg)
+{
+ return container_of(bg, struct blob_priv, bg);
+}
+
+static void jr_jobdesc_blob_decap(struct blob_priv *ctx, u8 modlen, u16 input_size)
+{
+ u32 *desc = ctx->desc;
+ u16 in_sz;
+ u16 out_sz;
+
+ in_sz = input_size;
+ out_sz = input_size - BLOB_OVERHEAD;
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, ctx->dma_modifier, modlen, CLASS_2);
+ append_seq_in_ptr(desc, ctx->dma_ciphertext, in_sz, 0);
+ append_seq_out_ptr(desc, ctx->dma_plaintext, out_sz, 0);
+ append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void jr_jobdesc_blob_encap(struct blob_priv *ctx, u8 modlen, u16 input_size)
+{
+ u32 *desc = ctx->desc;
+ u16 in_sz;
+ u16 out_sz;
+
+ in_sz = input_size;
+ out_sz = input_size + BLOB_OVERHEAD;
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, ctx->dma_modifier, modlen, CLASS_2);
+ append_seq_in_ptr(desc, ctx->dma_plaintext, in_sz, 0);
+ append_seq_out_ptr(desc, ctx->dma_ciphertext, out_sz, 0);
+ append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void blob_job_done(struct device_d *dev, u32 *desc, u32 err, void *arg)
+{
+ struct blob_job_result *res = arg;
+
+ if (!res)
+ return;
+
+ if (err)
+ caam_jr_strstatus(dev, err);
+
+ res->err = err;
+}
+
+static int caam_blob_decrypt(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize)
+{
+ struct blob_priv *ctx = to_blob_priv(bg);
+ struct device_d *jrdev = bg->dev.parent;
+ struct blob_job_result testres;
+ int modifier_len = strlen(modifier);
+ u32 *desc = ctx->desc;
+ int ret;
+
+ if (blobsize <= BLOB_OVERHEAD)
+ return -EINVAL;
+
+ *plainsize = blobsize - BLOB_OVERHEAD;
+
+ *plain = dma_alloc(*plainsize);
+ if (!*plain)
+ return -ENOMEM;
+
+ memset(desc, 0, DESC_LEN);
+
+ ctx->dma_modifier = (dma_addr_t)modifier;
+ ctx->dma_plaintext = (dma_addr_t)*plain;
+ ctx->dma_ciphertext = (dma_addr_t)blob;
+
+ jr_jobdesc_blob_decap(ctx, modifier_len, blobsize);
+
+ dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ DMA_TO_DEVICE);
+
+ dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)*plain, *plainsize,
+ DMA_FROM_DEVICE);
+ dma_sync_single_for_device((unsigned long)blob, blobsize,
+ DMA_TO_DEVICE);
+
+ testres.err = 0;
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (ret)
+ dev_err(jrdev, "decryption error\n");
+
+ ret = testres.err;
+
+ dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)*plain, *plainsize,
+ DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)blob, blobsize,
+ DMA_TO_DEVICE);
+
+ return ret;
+}
+
+static int caam_blob_encrypt(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize)
+{
+ struct blob_priv *ctx = to_blob_priv(bg);
+ struct device_d *jrdev = bg->dev.parent;
+ struct blob_job_result testres;
+ int modifier_len = strlen(modifier);
+ u32 *desc = ctx->desc;
+ int ret;
+
+ *blobsize = plainsize + BLOB_OVERHEAD;
+
+ memset(desc, 0, DESC_LEN);
+
+ ctx->dma_modifier = (dma_addr_t)modifier;
+ ctx->dma_plaintext = (dma_addr_t)plain;
+ ctx->dma_ciphertext = (dma_addr_t)blob;
+
+ jr_jobdesc_blob_encap(ctx, modifier_len, plainsize);
+
+ dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ DMA_TO_DEVICE);
+
+ dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)plain, plainsize,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)blob, *blobsize,
+ DMA_FROM_DEVICE);
+
+ testres.err = 0;
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (ret)
+ dev_err(jrdev, "encryption error\n");
+
+ ret = testres.err;
+
+ dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)plain, plainsize,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)blob, *blobsize,
+ DMA_FROM_DEVICE);
+
+ return ret;
+}
+
+int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev)
+{
+ struct blob_priv *ctx;
+ struct blobgen *bg;
+ int ret;
+
+ ctx = xzalloc(sizeof(*ctx));
+ bg = &ctx->bg;
+ bg->max_payload_size = MAX_BLOB_LEN - BLOB_OVERHEAD;
+ bg->encrypt = caam_blob_encrypt;
+ bg->decrypt = caam_blob_decrypt;
+
+ ret = blob_gen_register(jrdev, bg);
+ if (ret)
+ free(ctx);
+
+ return ret;
+}
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 4fe3eea3e6..06b075e74a 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -654,6 +654,15 @@ static int caam_probe(struct device_d *dev)
}
}
+ if (IS_ENABLED(CONFIG_BLOBGEN)) {
+ ret = caam_blob_gen_probe(dev, ctrlpriv->jrpdev[0]);
+ if (ret) {
+ dev_err(dev, "failed to instantiate blobgen device");
+ caam_remove(dev);
+ return ret;
+ }
+ }
+
/* NOTE: RTIC detection ought to go here, around Si time */
caam_id = (u64)rd_reg32(&ctrl->perfmon.caam_id_ms) << 32 |
(u64)rd_reg32(&ctrl->perfmon.caam_id_ls);
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index fe19a2c8d2..6dfcea26ac 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -93,5 +93,6 @@ void caam_jr_algapi_init(struct device *dev);
void caam_jr_algapi_remove(struct device *dev);
int caam_rng_probe(struct device_d *dev, struct device_d *jrdev);
+int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev);
int caam_jr_probe(struct device_d *dev);
#endif /* INTERN_H */
diff --git a/drivers/crypto/caam/rng_self_test.c b/drivers/crypto/caam/rng_self_test.c
index aab4fa2e47..7816cd152c 100644
--- a/drivers/crypto/caam/rng_self_test.c
+++ b/drivers/crypto/caam/rng_self_test.c
@@ -51,6 +51,7 @@
#include "error.h"
#include "regs.h"
#include "jr.h"
+#include "rng_self_test.h"
static const u32 rng_dsc1[] = {
0xb0800036, 0x04800010, 0x3c85a15b, 0x50a9d0b1,
diff --git a/drivers/crypto/imx-scc/Kconfig b/drivers/crypto/imx-scc/Kconfig
new file mode 100644
index 0000000000..531304f432
--- /dev/null
+++ b/drivers/crypto/imx-scc/Kconfig
@@ -0,0 +1,14 @@
+config CRYPTO_DEV_MXC_SCC
+ tristate "Support for Freescale Security Controller (SCC)"
+ depends on ARCH_IMX25 && OFTREE
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_DES
+ help
+ This option enables support for the Security Controller (SCC)
+ found in Freescale i.MX25 chips.
+
+config CRYPTO_DEV_MXC_SCC_BLOB_GEN
+ tristate "Support for SCC blob gen"
+ depends on ARCH_IMX25
+ select BLOBGEN
+ select CRYPTO_DEV_MXC_SCC
diff --git a/drivers/crypto/imx-scc/Makefile b/drivers/crypto/imx-scc/Makefile
new file mode 100644
index 0000000000..c30fd1e12d
--- /dev/null
+++ b/drivers/crypto/imx-scc/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += scc.o
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC_BLOB_GEN) += scc-blobgen.o
diff --git a/drivers/crypto/imx-scc/scc-blobgen.c b/drivers/crypto/imx-scc/scc-blobgen.c
new file mode 100644
index 0000000000..e1a1372420
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc-blobgen.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+#include <common.h>
+#include <dma.h>
+#include <digest.h>
+#include <driver.h>
+#include <init.h>
+#include <blobgen.h>
+#include <stdlib.h>
+#include <crypto.h>
+#include <crypto/sha.h>
+
+#include "scc.h"
+
+#define MAX_IVLEN BLOCKSIZE_BYTES
+
+static struct digest *sha256;
+
+static int sha256sum(uint8_t *src, uint8_t *dst, unsigned int size)
+{
+ if (!sha256)
+ sha256 = digest_alloc("sha256");
+
+ if (!sha256) {
+ pr_err("Unable to allocate sha256 digest\n");
+ return -EINVAL;
+ }
+
+ return digest_digest(sha256, src, size, dst);
+}
+
+static int imx_scc_blob_encrypt(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize)
+{
+ char *s;
+ int bufsiz;
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+
+ bufsiz = ALIGN(plainsize + KEYMOD_LENGTH, 8);
+
+ s = malloc(bufsiz + SHA256_DIGEST_SIZE);
+ if (!s)
+ return -ENOMEM;
+
+ memset(s, 0, bufsiz);
+
+ strncpy(s, modifier, KEYMOD_LENGTH);
+ memcpy(s + KEYMOD_LENGTH, plain, plainsize);
+
+ ret = sha256sum(s, hash, bufsiz);
+ if (ret)
+ goto out;
+
+ memcpy(s + bufsiz, hash, SHA256_DIGEST_SIZE);
+
+ bufsiz += SHA256_DIGEST_SIZE;
+
+ req.info = iv;
+ req.src = s;
+ req.dst = blob;
+ req.nbytes = bufsiz;
+
+ get_random_bytes(req.info, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_encrypt(&req);
+ if (ret)
+ goto out;
+
+ memcpy(blob + bufsiz, req.info, MAX_IVLEN);
+ *blobsize = bufsiz + MAX_IVLEN;
+
+out:
+ free(s);
+
+ return ret;
+}
+
+static int imx_scc_blob_decrypt(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize)
+{
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+ uint8_t *data;
+ int ciphersize = blobsize - MAX_IVLEN;
+
+ if (blobsize <= MAX_IVLEN + SHA256_DIGEST_SIZE + KEYMOD_LENGTH)
+ return -EINVAL;
+
+ data = malloc(ciphersize);
+ if (!data)
+ return -ENOMEM;
+
+ req.info = iv;
+ req.nbytes = ciphersize;
+ req.src = (void *)blob;
+ req.dst = data;
+
+ memcpy(req.info, blob + req.nbytes, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_decrypt(&req);
+ if (ret)
+ goto out;
+
+ ret = sha256sum(data, hash, ciphersize - SHA256_DIGEST_SIZE);
+ if (ret)
+ goto out;
+
+ if (memcmp(data + ciphersize - SHA256_DIGEST_SIZE, hash,
+ SHA256_DIGEST_SIZE)) {
+ pr_err("%s: Corrupted SHA256 digest. Can't continue.\n",
+ bg->dev.name);
+ pr_err("%s: Calculated hash:\n", bg->dev.name);
+ memory_display(hash, 0, SHA256_DIGEST_SIZE, 1, 0);
+ pr_err("%s: Received hash:\n", bg->dev.name);
+ memory_display(data + ciphersize - SHA256_DIGEST_SIZE,
+ 0, SHA256_DIGEST_SIZE, 1, 0);
+
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ *plainsize = ciphersize - SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ *plain = xmemdup(data + KEYMOD_LENGTH, *plainsize);
+out:
+ free(data);
+
+ return ret;
+}
+
+int imx_scc_blob_gen_probe(struct device_d *dev)
+{
+ struct blobgen *bg;
+ int ret;
+
+ bg = xzalloc(sizeof(*bg));
+
+ bg->max_payload_size = MAX_BLOB_LEN - MAX_IVLEN -
+ SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ bg->encrypt = imx_scc_blob_encrypt;
+ bg->decrypt = imx_scc_blob_decrypt;
+
+ ret = blob_gen_register(dev, bg);
+ if (ret)
+ free(bg);
+
+ return ret;
+}
diff --git a/drivers/crypto/imx-scc/scc.c b/drivers/crypto/imx-scc/scc.c
new file mode 100644
index 0000000000..5a35c3506d
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * The driver is based on information gathered from
+ * drivers/mxc/security/imx_scc.c which can be found in
+ * the Freescale linux-2.6-imx.git in the imx_2.6.35_maintain branch.
+ *
+ * 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.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <crypto.h>
+#include <linux/barebox-wrapper.h>
+#include <linux/clk.h>
+#include <crypto/des.h>
+
+#include "scc.h"
+
+/* Secure Memory (SCM) registers */
+#define SCC_SCM_RED_START 0x0000
+#define SCC_SCM_BLACK_START 0x0004
+#define SCC_SCM_LENGTH 0x0008
+#define SCC_SCM_CTRL 0x000C
+#define SCC_SCM_STATUS 0x0010
+#define SCC_SCM_ERROR_STATUS 0x0014
+#define SCC_SCM_INTR_CTRL 0x0018
+#define SCC_SCM_CFG 0x001C
+#define SCC_SCM_INIT_VECTOR_0 0x0020
+#define SCC_SCM_INIT_VECTOR_1 0x0024
+#define SCC_SCM_RED_MEMORY 0x0400
+#define SCC_SCM_BLACK_MEMORY 0x0800
+
+/* Security Monitor (SMN) Registers */
+#define SCC_SMN_STATUS 0x1000
+#define SCC_SMN_COMMAND 0x1004
+#define SCC_SMN_SEQ_START 0x1008
+#define SCC_SMN_SEQ_END 0x100C
+#define SCC_SMN_SEQ_CHECK 0x1010
+#define SCC_SMN_BIT_COUNT 0x1014
+#define SCC_SMN_BITBANK_INC_SIZE 0x1018
+#define SCC_SMN_BITBANK_DECREMENT 0x101C
+#define SCC_SMN_COMPARE_SIZE 0x1020
+#define SCC_SMN_PLAINTEXT_CHECK 0x1024
+#define SCC_SMN_CIPHERTEXT_CHECK 0x1028
+#define SCC_SMN_TIMER_IV 0x102C
+#define SCC_SMN_TIMER_CONTROL 0x1030
+#define SCC_SMN_DEBUG_DETECT_STAT 0x1034
+#define SCC_SMN_TIMER 0x1038
+
+#define SCC_SCM_CTRL_START_CIPHER BIT(2)
+#define SCC_SCM_CTRL_CBC_MODE BIT(1)
+#define SCC_SCM_CTRL_DECRYPT_MODE BIT(0)
+
+#define SCC_SCM_STATUS_LEN_ERR BIT(12)
+#define SCC_SCM_STATUS_SMN_UNBLOCKED BIT(11)
+#define SCC_SCM_STATUS_CIPHERING_DONE BIT(10)
+#define SCC_SCM_STATUS_ZEROIZING_DONE BIT(9)
+#define SCC_SCM_STATUS_INTR_STATUS BIT(8)
+#define SCC_SCM_STATUS_SEC_KEY BIT(7)
+#define SCC_SCM_STATUS_INTERNAL_ERR BIT(6)
+#define SCC_SCM_STATUS_BAD_SEC_KEY BIT(5)
+#define SCC_SCM_STATUS_ZEROIZE_FAIL BIT(4)
+#define SCC_SCM_STATUS_SMN_BLOCKED BIT(3)
+#define SCC_SCM_STATUS_CIPHERING BIT(2)
+#define SCC_SCM_STATUS_ZEROIZING BIT(1)
+#define SCC_SCM_STATUS_BUSY BIT(0)
+
+#define SCC_SMN_STATUS_STATE_MASK 0x0000001F
+#define SCC_SMN_STATE_START 0x0
+/* The SMN is zeroizing its RAM during reset */
+#define SCC_SMN_STATE_ZEROIZE_RAM 0x5
+/* SMN has passed internal checks */
+#define SCC_SMN_STATE_HEALTH_CHECK 0x6
+/* Fatal Security Violation. SMN is locked, SCM is inoperative. */
+#define SCC_SMN_STATE_FAIL 0x9
+/* SCC is in secure state. SCM is using secret key. */
+#define SCC_SMN_STATE_SECURE 0xA
+/* SCC is not secure. SCM is using default key. */
+#define SCC_SMN_STATE_NON_SECURE 0xC
+
+#define SCC_SCM_INTR_CTRL_ZEROIZE_MEM BIT(2)
+#define SCC_SCM_INTR_CTRL_CLR_INTR BIT(1)
+#define SCC_SCM_INTR_CTRL_MASK_INTR BIT(0)
+
+/* Size, in blocks, of Red memory. */
+#define SCC_SCM_CFG_BLACK_SIZE_MASK 0x07fe0000
+#define SCC_SCM_CFG_BLACK_SIZE_SHIFT 17
+/* Size, in blocks, of Black memory. */
+#define SCC_SCM_CFG_RED_SIZE_MASK 0x0001ff80
+#define SCC_SCM_CFG_RED_SIZE_SHIFT 7
+/* Number of bytes per block. */
+#define SCC_SCM_CFG_BLOCK_SIZE_MASK 0x0000007f
+
+#define SCC_SMN_COMMAND_TAMPER_LOCK BIT(4)
+#define SCC_SMN_COMMAND_CLR_INTR BIT(3)
+#define SCC_SMN_COMMAND_CLR_BIT_BANK BIT(2)
+#define SCC_SMN_COMMAND_EN_INTR BIT(1)
+#define SCC_SMN_COMMAND_SET_SOFTWARE_ALARM BIT(0)
+
+#define SCC_KEY_SLOTS 20
+#define SCC_MAX_KEY_SIZE 32
+#define SCC_KEY_SLOT_SIZE 32
+
+#define SCC_CRC_CCITT_START 0xFFFF
+
+/*
+ * Offset into each RAM of the base of the area which is not
+ * used for Stored Keys.
+ */
+#define SCC_NON_RESERVED_OFFSET (SCC_KEY_SLOTS * SCC_KEY_SLOT_SIZE)
+
+/* Fixed padding for appending to plaintext to fill out a block */
+static char scc_block_padding[8] = { 0x80, 0, 0, 0, 0, 0, 0, 0 };
+
+struct imx_scc {
+ struct device_d *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct ablkcipher_request *req;
+ unsigned int block_size_bytes;
+ unsigned int black_ram_size_blocks;
+ unsigned int memory_size_bytes;
+ unsigned int bytes_remaining;
+
+ void __iomem *red_memory;
+ void __iomem *black_memory;
+};
+
+struct imx_scc_ctx {
+ struct imx_scc *scc;
+ unsigned int offset;
+ unsigned int size;
+ unsigned int ctrl;
+};
+
+static struct imx_scc *scc_dev;
+
+static int imx_scc_get_data(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ struct imx_scc *scc = ctx->scc;
+ void __iomem *from;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE)
+ from = scc->red_memory;
+ else
+ from = scc->black_memory;
+
+ memcpy(ablkreq->dst, from + ctx->offset, ctx->size);
+
+ pr_debug("GET_DATA:\n");
+ pr_memory_display(MSG_DEBUG, from, 0, ctx->size, 0x40 >> 3, 0);
+
+ ctx->offset += ctx->size;
+
+ if (ctx->offset < ablkreq->nbytes)
+ return -EINPROGRESS;
+
+ return 0;
+}
+
+static int imx_scc_ablkcipher_req_init(struct ablkcipher_request *req,
+ struct imx_scc_ctx *ctx)
+{
+ ctx->size = 0;
+ ctx->offset = 0;
+
+ return 0;
+}
+
+static int imx_scc_put_data(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *req)
+{
+ u8 padding_buffer[sizeof(u16) + sizeof(scc_block_padding)];
+ size_t len = min(req->nbytes - ctx->offset, ctx->scc->bytes_remaining);
+ unsigned int padding_byte_count = 0;
+ struct imx_scc *scc = ctx->scc;
+ void __iomem *to;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE)
+ to = scc->black_memory;
+ else
+ to = scc->red_memory;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_CBC_MODE) {
+ dev_dbg(scc->dev, "set IV@0x%p\n", scc->base + SCC_SCM_INIT_VECTOR_0);
+ memcpy(scc->base + SCC_SCM_INIT_VECTOR_0, req->info,
+ scc->block_size_bytes);
+ }
+
+ memcpy(to, req->src + ctx->offset, len);
+
+ ctx->size = len;
+
+ scc->bytes_remaining -= len;
+
+ padding_byte_count = ((len + scc->block_size_bytes - 1) &
+ ~(scc->block_size_bytes-1)) - len;
+
+ if (padding_byte_count) {
+ memcpy(padding_buffer, scc_block_padding, padding_byte_count);
+ memcpy(to + len, padding_buffer, padding_byte_count);
+ ctx->size += padding_byte_count;
+ }
+
+ dev_dbg(scc->dev, "copied %d bytes to 0x%p\n", ctx->size, to);
+ pr_debug("IV:\n");
+ pr_memory_display(MSG_DEBUG, scc->base + SCC_SCM_INIT_VECTOR_0, 0,
+ scc->block_size_bytes,
+ 0x40 >> 3, 0);
+ pr_debug("DATA:\n");
+ pr_memory_display(MSG_DEBUG, to, 0, ctx->size, 0x40 >> 3, 0);
+
+ return 0;
+}
+
+static int imx_scc_ablkcipher_next(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ struct imx_scc *scc = ctx->scc;
+ int err;
+
+ writel(0, scc->base + SCC_SCM_ERROR_STATUS);
+
+ err = imx_scc_put_data(ctx, ablkreq);
+ if (err)
+ return err;
+
+ dev_dbg(scc->dev, "Start encryption (0x%p/0x%p)\n",
+ (void *)readl(scc->base + SCC_SCM_RED_START),
+ (void *)readl(scc->base + SCC_SCM_BLACK_START));
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR,
+ scc->base + SCC_SCM_INTR_CTRL);
+
+ writel((ctx->size / ctx->scc->block_size_bytes) - 1,
+ scc->base + SCC_SCM_LENGTH);
+
+ dev_dbg(scc->dev, "Process %d block(s) in 0x%p\n",
+ ctx->size / ctx->scc->block_size_bytes,
+ (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE) ? scc->black_memory :
+ scc->red_memory);
+
+ writel(ctx->ctrl, scc->base + SCC_SCM_CTRL);
+
+ return 0;
+}
+
+static int imx_scc_int(struct imx_scc_ctx *ctx)
+{
+ struct ablkcipher_request *ablkreq;
+ struct imx_scc *scc = ctx->scc;
+ uint64_t start;
+
+ start = get_time_ns();
+ while (readl(scc->base + SCC_SCM_STATUS) & SCC_SCM_STATUS_BUSY) {
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_err(scc->dev, "timeout waiting for interrupt\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR, scc->base + SCC_SCM_INTR_CTRL);
+
+ ablkreq = scc->req;
+
+ if (ablkreq)
+ return imx_scc_get_data(ctx, ablkreq);
+
+ return 0;
+}
+
+static int imx_scc_process_req(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ int ret = -EINPROGRESS;
+
+ ctx->scc->req = ablkreq;
+
+ while (ret == -EINPROGRESS) {
+ ret = imx_scc_ablkcipher_next(ctx, ablkreq);
+ if (ret)
+ break;
+ ret = imx_scc_int(ctx);
+ }
+
+ ctx->scc->req = NULL;
+ ctx->scc->bytes_remaining = ctx->scc->memory_size_bytes;
+
+ return 0;
+}
+
+static int imx_scc_des3_op(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *req)
+{
+ int err;
+
+ err = imx_scc_ablkcipher_req_init(req, ctx);
+ if (err)
+ return err;
+
+ return imx_scc_process_req(ctx, req);
+}
+
+int imx_scc_cbc_des_encrypt(struct ablkcipher_request *req)
+{
+ struct imx_scc_ctx *ctx;
+
+ ctx = xzalloc(sizeof(*ctx));
+ ctx->scc = scc_dev;
+
+ ctx->ctrl = SCC_SCM_CTRL_START_CIPHER;
+ ctx->ctrl |= SCC_SCM_CTRL_CBC_MODE;
+
+ return imx_scc_des3_op(ctx, req);
+}
+
+int imx_scc_cbc_des_decrypt(struct ablkcipher_request *req)
+{
+ struct imx_scc_ctx *ctx;
+
+ ctx = xzalloc(sizeof(*ctx));
+ ctx->scc = scc_dev;
+
+ ctx->ctrl = SCC_SCM_CTRL_START_CIPHER;
+ ctx->ctrl |= SCC_SCM_CTRL_CBC_MODE;
+ ctx->ctrl |= SCC_SCM_CTRL_DECRYPT_MODE;
+
+ return imx_scc_des3_op(ctx, req);
+}
+
+static void imx_scc_hw_init(struct imx_scc *scc)
+{
+ int offset;
+
+ offset = SCC_NON_RESERVED_OFFSET / scc->block_size_bytes;
+
+ /* Fill the RED_START register */
+ writel(offset, scc->base + SCC_SCM_RED_START);
+
+ /* Fill the BLACK_START register */
+ writel(offset, scc->base + SCC_SCM_BLACK_START);
+
+ scc->red_memory = scc->base + SCC_SCM_RED_MEMORY +
+ SCC_NON_RESERVED_OFFSET;
+
+ scc->black_memory = scc->base + SCC_SCM_BLACK_MEMORY +
+ SCC_NON_RESERVED_OFFSET;
+
+ scc->bytes_remaining = scc->memory_size_bytes;
+}
+
+static int imx_scc_get_config(struct imx_scc *scc)
+{
+ int config;
+
+ config = readl(scc->base + SCC_SCM_CFG);
+
+ scc->block_size_bytes = config & SCC_SCM_CFG_BLOCK_SIZE_MASK;
+
+ scc->black_ram_size_blocks = config & SCC_SCM_CFG_BLACK_SIZE_MASK;
+
+ scc->memory_size_bytes = (scc->block_size_bytes *
+ scc->black_ram_size_blocks) -
+ SCC_NON_RESERVED_OFFSET;
+
+ return 0;
+}
+
+static int imx_scc_get_state(struct imx_scc *scc)
+{
+ int status, ret;
+ const char *statestr;
+
+ status = readl(scc->base + SCC_SMN_STATUS) &
+ SCC_SMN_STATUS_STATE_MASK;
+
+ /* If in Health Check, try to bringup to secure state */
+ if (status & SCC_SMN_STATE_HEALTH_CHECK) {
+ /*
+ * Write a simple algorithm to the Algorithm Sequence
+ * Checker (ASC)
+ */
+ writel(0xaaaa, scc->base + SCC_SMN_SEQ_START);
+ writel(0x5555, scc->base + SCC_SMN_SEQ_END);
+ writel(0x5555, scc->base + SCC_SMN_SEQ_CHECK);
+
+ status = readl(scc->base + SCC_SMN_STATUS) &
+ SCC_SMN_STATUS_STATE_MASK;
+ }
+
+ switch (status) {
+ case SCC_SMN_STATE_NON_SECURE:
+ statestr = "non-secure";
+ ret = 0;
+ break;
+ case SCC_SMN_STATE_SECURE:
+ statestr = "secure";
+ ret = 0;
+ break;
+ case SCC_SMN_STATE_FAIL:
+ statestr = "fail";
+ ret = -EIO;
+ break;
+ default:
+ statestr = "unknown";
+ ret = -EINVAL;
+ break;
+ }
+
+ dev_info(scc->dev, "starting in %s mode\n", statestr);
+
+ return ret;
+}
+
+static int imx_scc_probe(struct device_d *dev)
+{
+ struct imx_scc *scc;
+ int ret;
+
+ scc = xzalloc(sizeof(*scc));
+
+ scc->base = dev_request_mem_region(dev, 0);
+ if (IS_ERR(scc->base))
+ return PTR_ERR(scc->base);
+
+ scc->clk = clk_get(dev, "ipg");
+ if (IS_ERR(scc->clk)) {
+ dev_err(dev, "Could not get ipg clock\n");
+ return PTR_ERR(scc->clk);
+ }
+
+ clk_enable(scc->clk);
+
+ /* clear error status register */
+
+ writel(0x0, scc->base + SCC_SCM_ERROR_STATUS);
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR |
+ SCC_SCM_INTR_CTRL_MASK_INTR,
+ scc->base + SCC_SCM_INTR_CTRL);
+
+ writel(SCC_SMN_COMMAND_CLR_INTR |
+ SCC_SMN_COMMAND_EN_INTR,
+ scc->base + SCC_SMN_COMMAND);
+
+ scc->dev = dev;
+
+ ret = imx_scc_get_config(scc);
+ if (ret)
+ goto err_out;
+
+ ret = imx_scc_get_state(scc);
+
+ if (ret) {
+ dev_err(dev, "SCC in unusable state\n");
+ goto err_out;
+ }
+
+ imx_scc_hw_init(scc);
+
+ scc_dev = scc;
+
+ if (IS_ENABLED(CONFIG_BLOBGEN)) {
+ ret = imx_scc_blob_gen_probe(dev);
+ if (ret)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ clk_disable(scc->clk);
+ clk_put(scc->clk);
+ free(scc);
+
+ return ret;
+}
+
+static __maybe_unused struct of_device_id imx_scc_dt_ids[] = {
+ { .compatible = "fsl,imx25-scc", },
+ { /* sentinel */ }
+};
+
+static struct driver_d imx_scc_driver = {
+ .name = "mxc-scc",
+ .probe = imx_scc_probe,
+ .of_compatible = imx_scc_dt_ids,
+};
+device_platform_driver(imx_scc_driver);
diff --git a/drivers/crypto/imx-scc/scc.h b/drivers/crypto/imx-scc/scc.h
new file mode 100644
index 0000000000..5c5c25c4a0
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+
+struct ablkcipher_request;
+
+int imx_scc_cbc_des_encrypt(struct ablkcipher_request *req);
+int imx_scc_cbc_des_decrypt(struct ablkcipher_request *req);
+int imx_scc_blob_gen_probe(struct device_d *dev);
diff --git a/drivers/ddr/fsl/fsl_ddr.h b/drivers/ddr/fsl/fsl_ddr.h
index ee6069d812..ab991a5bf4 100644
--- a/drivers/ddr/fsl/fsl_ddr.h
+++ b/drivers/ddr/fsl/fsl_ddr.h
@@ -227,8 +227,6 @@ unsigned int mclk_to_picos(struct fsl_ddr_controller *c, unsigned int mclk);
unsigned int get_memory_clk_period_ps(struct fsl_ddr_controller *c);
unsigned int picos_to_mclk(struct fsl_ddr_controller *c, unsigned int picos);
-void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step);
-
void erratum_a009942_check_cpo(void);
#endif
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 911cc0cb1e..08c8c84e8c 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -66,6 +66,10 @@ config MCI_BCM283X
bool "MCI support for BCM283X"
depends on ARCH_BCM283X
+config MCI_BCM283X_SDHOST
+ bool "BCM283X sdhost"
+ depends on ARCH_BCM283X
+
config MCI_DOVE
bool "Marvell Dove SDHCI"
depends on ARCH_DOVE
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index f6214c0cbb..25a1d073dc 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_MCI) += mci-core.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
+obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
diff --git a/drivers/mci/bcm2835-sdhost.c b/drivers/mci/bcm2835-sdhost.c
new file mode 100644
index 0000000000..1d3a6c0969
--- /dev/null
+++ b/drivers/mci/bcm2835-sdhost.c
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This code is ported from U-Boot by Lucas Stach <l.stach@pengutronix.de> and
+ * has the following contributors listed in the original license header:
+ * Alexander Graf <agraf@suse.de>
+ * Phil Elwell <phil@raspberrypi.org>
+ * Gellert Weisz
+ * Stephen Warren
+ * Oleksandr Tymoshenko
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/log2.h>
+#include <mci.h>
+
+#define SDCMD 0x00 /* Command to SD card - 16 R/W */
+#define SDARG 0x04 /* Argument to SD card - 32 R/W */
+#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */
+#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */
+#define SDRSP0 0x10 /* SD card response (31:0) - 32 R */
+#define SDRSP1 0x14 /* SD card response (63:32) - 32 R */
+#define SDRSP2 0x18 /* SD card response (95:64) - 32 R */
+#define SDRSP3 0x1c /* SD card response (127:96) - 32 R */
+#define SDHSTS 0x20 /* SD host status - 11 R/W */
+#define SDVDD 0x30 /* SD card power control - 1 R/W */
+#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */
+#define SDHCFG 0x38 /* Host configuration - 2 R/W */
+#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */
+#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */
+#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */
+
+#define SDCMD_NEW_FLAG 0x8000
+#define SDCMD_FAIL_FLAG 0x4000
+#define SDCMD_BUSYWAIT 0x800
+#define SDCMD_NO_RESPONSE 0x400
+#define SDCMD_LONG_RESPONSE 0x200
+#define SDCMD_WRITE_CMD 0x80
+#define SDCMD_READ_CMD 0x40
+#define SDCMD_CMD_MASK 0x3f
+
+#define SDCDIV_MAX_CDIV 0x7ff
+
+#define SDHSTS_BUSY_IRPT 0x400
+#define SDHSTS_BLOCK_IRPT 0x200
+#define SDHSTS_SDIO_IRPT 0x100
+#define SDHSTS_REW_TIME_OUT 0x80
+#define SDHSTS_CMD_TIME_OUT 0x40
+#define SDHSTS_CRC16_ERROR 0x20
+#define SDHSTS_CRC7_ERROR 0x10
+#define SDHSTS_FIFO_ERROR 0x08
+#define SDHSTS_DATA_FLAG 0x01
+
+#define SDHSTS_CLEAR_MASK (SDHSTS_BUSY_IRPT | \
+ SDHSTS_BLOCK_IRPT | \
+ SDHSTS_SDIO_IRPT | \
+ SDHSTS_REW_TIME_OUT | \
+ SDHSTS_CMD_TIME_OUT | \
+ SDHSTS_CRC16_ERROR | \
+ SDHSTS_CRC7_ERROR | \
+ SDHSTS_FIFO_ERROR)
+
+#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC7_ERROR | \
+ SDHSTS_CRC16_ERROR | \
+ SDHSTS_REW_TIME_OUT | \
+ SDHSTS_FIFO_ERROR)
+
+#define SDHSTS_ERROR_MASK (SDHSTS_CMD_TIME_OUT | \
+ SDHSTS_TRANSFER_ERROR_MASK)
+
+#define SDHCFG_BUSY_IRPT_EN BIT(10)
+#define SDHCFG_BLOCK_IRPT_EN BIT(8)
+#define SDHCFG_SDIO_IRPT_EN BIT(5)
+#define SDHCFG_DATA_IRPT_EN BIT(4)
+#define SDHCFG_SLOW_CARD BIT(3)
+#define SDHCFG_WIDE_EXT_BUS BIT(2)
+#define SDHCFG_WIDE_INT_BUS BIT(1)
+#define SDHCFG_REL_CMD_LINE BIT(0)
+
+#define SDVDD_POWER_OFF 0
+#define SDVDD_POWER_ON 1
+
+#define SDEDM_FORCE_DATA_MODE BIT(19)
+#define SDEDM_CLOCK_PULSE BIT(20)
+#define SDEDM_BYPASS BIT(21)
+
+#define SDEDM_FIFO_FILL_SHIFT 4
+#define SDEDM_FIFO_FILL_MASK 0x1f
+static u32 edm_fifo_fill(u32 edm)
+{
+ return (edm >> SDEDM_FIFO_FILL_SHIFT) & SDEDM_FIFO_FILL_MASK;
+}
+
+#define SDEDM_WRITE_THRESHOLD_SHIFT 9
+#define SDEDM_READ_THRESHOLD_SHIFT 14
+#define SDEDM_THRESHOLD_MASK 0x1f
+
+#define SDEDM_FSM_MASK 0xf
+#define SDEDM_FSM_IDENTMODE 0x0
+#define SDEDM_FSM_DATAMODE 0x1
+#define SDEDM_FSM_READDATA 0x2
+#define SDEDM_FSM_WRITEDATA 0x3
+#define SDEDM_FSM_READWAIT 0x4
+#define SDEDM_FSM_READCRC 0x5
+#define SDEDM_FSM_WRITECRC 0x6
+#define SDEDM_FSM_WRITEWAIT1 0x7
+#define SDEDM_FSM_POWERDOWN 0x8
+#define SDEDM_FSM_POWERUP 0x9
+#define SDEDM_FSM_WRITESTART1 0xa
+#define SDEDM_FSM_WRITESTART2 0xb
+#define SDEDM_FSM_GENPULSES 0xc
+#define SDEDM_FSM_WRITEWAIT2 0xd
+#define SDEDM_FSM_STARTPOWDOWN 0xf
+
+#define SDDATA_FIFO_WORDS 16
+
+#define FIFO_READ_THRESHOLD 4
+#define FIFO_WRITE_THRESHOLD 4
+#define SDDATA_FIFO_PIO_BURST 8
+
+#define SDHST_TIMEOUT_MAX_USEC 100000
+
+struct bcm2835_host {
+ struct mci_host mci;
+ void __iomem *regs;
+ struct clk *clk;
+};
+
+static inline struct bcm2835_host *to_bcm2835_host(struct mci_host *mci)
+{
+ return container_of(mci, struct bcm2835_host, mci);
+}
+
+static int bcm2835_sdhost_init(struct mci_host *mci, struct device_d *dev)
+{
+ struct bcm2835_host *host = to_bcm2835_host(mci);
+ u32 temp;
+
+ writel(SDVDD_POWER_OFF, host->regs + SDVDD);
+ writel(0, host->regs + SDCMD);
+ writel(0, host->regs + SDARG);
+ /* Set timeout to a big enough value so we don't hit it */
+ writel(0xf00000, host->regs + SDTOUT);
+ writel(0, host->regs + SDCDIV);
+ /* Clear status register */
+ writel(SDHSTS_CLEAR_MASK, host->regs + SDHSTS);
+ writel(0, host->regs + SDHCFG);
+ writel(0, host->regs + SDHBCT);
+ writel(0, host->regs + SDHBLC);
+
+ /* Limit fifo usage due to silicon bug */
+ temp = readl(host->regs + SDEDM);
+ temp &= ~((SDEDM_THRESHOLD_MASK << SDEDM_READ_THRESHOLD_SHIFT) |
+ (SDEDM_THRESHOLD_MASK << SDEDM_WRITE_THRESHOLD_SHIFT));
+ temp |= (FIFO_READ_THRESHOLD << SDEDM_READ_THRESHOLD_SHIFT) |
+ (FIFO_WRITE_THRESHOLD << SDEDM_WRITE_THRESHOLD_SHIFT);
+ writel(temp, host->regs + SDEDM);
+ /* Wait for FIFO threshold to populate */
+ mdelay(20);
+ writel(SDVDD_POWER_ON, host->regs + SDVDD);
+ /* Wait for all components to go through power on cycle */
+ mdelay(20);
+ writel(0, host->regs + SDHCFG);
+ writel(0, host->regs + SDCDIV);
+
+ return 0;
+}
+
+static int bcm2835_wait_transfer_complete(struct bcm2835_host *host)
+{
+ uint64_t start = get_time_ns();
+
+ while (1) {
+ u32 edm, fsm;
+
+ edm = readl(host->regs + SDEDM);
+ fsm = edm & SDEDM_FSM_MASK;
+
+ if ((fsm == SDEDM_FSM_IDENTMODE) ||
+ (fsm == SDEDM_FSM_DATAMODE))
+ break;
+
+ if ((fsm == SDEDM_FSM_READWAIT) ||
+ (fsm == SDEDM_FSM_WRITESTART1) ||
+ (fsm == SDEDM_FSM_READDATA)) {
+ writel(edm | SDEDM_FORCE_DATA_MODE,
+ host->regs + SDEDM);
+ break;
+ }
+
+ /* Error out after 1 second */
+ if (is_timeout(start, 1 * SECOND)) {
+ dev_err(host->mci.hw_dev,
+ "wait_transfer_complete - still waiting 1s\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm2835_transfer_block_pio(struct bcm2835_host *host,
+ struct mci_data *data, unsigned int block,
+ bool is_read)
+{
+ u32 *buf = is_read ? (u32 *)data->dest : (u32 *)data->src;
+ int copy_words = data->blocksize / sizeof(u32);
+ uint64_t start = get_time_ns();
+
+ if (data->blocksize % sizeof(u32))
+ return -EINVAL;
+
+ buf += (block * data->blocksize / sizeof(u32));
+
+ /* Copy all contents from/to the FIFO as far as it reaches. */
+ while (copy_words) {
+ int fifo_words;
+ u32 edm;
+
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_err(host->mci.hw_dev,
+ "transfer_block_pio timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ edm = readl(host->regs + SDEDM);
+ if (is_read)
+ fifo_words = edm_fifo_fill(edm);
+ else
+ fifo_words = SDDATA_FIFO_WORDS - edm_fifo_fill(edm);
+
+ if (fifo_words > copy_words)
+ fifo_words = copy_words;
+
+ /* Copy current chunk to/from the FIFO */
+ while (fifo_words) {
+ if (is_read)
+ *(buf++) = readl(host->regs + SDDATA);
+ else
+ writel(*(buf++), host->regs + SDDATA);
+ fifo_words--;
+ copy_words--;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm2835_transfer_pio(struct bcm2835_host *host,
+ struct mci_data *data)
+{
+ u32 sdhsts;
+ bool is_read = !!(data->flags & MMC_DATA_READ);
+ unsigned int block = 0;
+ int ret = 0;
+
+ while (block < data->blocks) {
+ ret = bcm2835_transfer_block_pio(host, data, block, is_read);
+ if (ret)
+ return ret;
+
+ sdhsts = readl(host->regs + SDHSTS);
+ if (sdhsts & (SDHSTS_CRC16_ERROR |
+ SDHSTS_CRC7_ERROR |
+ SDHSTS_FIFO_ERROR)) {
+ dev_err(host->mci.hw_dev,
+ "%s transfer error - HSTS %08x\n",
+ is_read ? "read" : "write", sdhsts);
+ ret = -EILSEQ;
+ } else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
+ SDHSTS_REW_TIME_OUT))) {
+ dev_err(host->mci.hw_dev,
+ "%s timeout error - HSTS %08x\n",
+ is_read ? "read" : "write", sdhsts);
+ ret = -ETIMEDOUT;
+ }
+ block++;
+ }
+
+ return ret;
+}
+
+static u32 bcm2835_read_wait_sdcmd(struct bcm2835_host *host)
+{
+ u32 value;
+ int ret;
+ int timeout_us = SDHST_TIMEOUT_MAX_USEC;
+
+ ret = readl_poll_timeout(host->regs + SDCMD, value,
+ !(value & SDCMD_NEW_FLAG), timeout_us);
+ if (ret == -ETIMEDOUT)
+ dev_err(host->mci.hw_dev, "%s: timeout (%d us)\n",
+ __func__, timeout_us);
+
+ return value;
+}
+
+static int bcm2835_send_command(struct bcm2835_host *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ u32 sdcmd, sdhsts;
+
+ if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) {
+ dev_err(host->mci.hw_dev, "unsupported response type!\n");
+ return -EINVAL;
+ }
+
+ sdcmd = bcm2835_read_wait_sdcmd(host);
+ if (sdcmd & SDCMD_NEW_FLAG) {
+ dev_err(host->mci.hw_dev, "previous command never completed.\n");
+ return -EBUSY;
+ }
+
+ /* Clear any error flags */
+ sdhsts = readl(host->regs + SDHSTS);
+ if (sdhsts & SDHSTS_ERROR_MASK)
+ writel(sdhsts, host->regs + SDHSTS);
+
+ if (data) {
+ writel(data->blocksize, host->regs + SDHBCT);
+ writel(data->blocks, host->regs + SDHBLC);
+ }
+
+ writel(cmd->cmdarg, host->regs + SDARG);
+
+ sdcmd = cmd->cmdidx & SDCMD_CMD_MASK;
+
+ if (!(cmd->resp_type & MMC_RSP_PRESENT)) {
+ sdcmd |= SDCMD_NO_RESPONSE;
+ } else {
+ if (cmd->resp_type & MMC_RSP_136)
+ sdcmd |= SDCMD_LONG_RESPONSE;
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ sdcmd |= SDCMD_BUSYWAIT;
+ }
+
+ if (data) {
+ if (data->flags & MMC_DATA_WRITE)
+ sdcmd |= SDCMD_WRITE_CMD;
+ if (data->flags & MMC_DATA_READ)
+ sdcmd |= SDCMD_READ_CMD;
+ }
+
+ writel(sdcmd | SDCMD_NEW_FLAG, host->regs + SDCMD);
+
+ return 0;
+}
+
+static int bcm2835_finish_command(struct bcm2835_host *host,
+ struct mci_cmd *cmd)
+{
+ u32 sdcmd;
+ int ret = 0;
+
+ sdcmd = bcm2835_read_wait_sdcmd(host);
+
+ /* Check for errors */
+ if (sdcmd & SDCMD_NEW_FLAG) {
+ dev_err(host->mci.hw_dev, "command never completed.\n");
+ return -EIO;
+ } else if (sdcmd & SDCMD_FAIL_FLAG) {
+ u32 sdhsts = readl(host->regs + SDHSTS);
+
+ /* Clear the errors */
+ writel(SDHSTS_ERROR_MASK, host->regs + SDHSTS);
+
+ if (!(sdhsts & SDHSTS_CRC7_ERROR) ||
+ (cmd->cmdidx != MMC_CMD_SEND_OP_COND)) {
+ if (sdhsts & SDHSTS_CMD_TIME_OUT) {
+ ret = -ETIMEDOUT;
+ } else {
+ dev_err(host->mci.hw_dev,
+ "unexpected command %d error\n",
+ cmd->cmdidx);
+ ret = -EILSEQ;
+ }
+
+ return ret;
+ }
+ }
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ cmd->response[3 - i] =
+ readl(host->regs + SDRSP0 + i * 4);
+ }
+ } else {
+ cmd->response[0] = readl(host->regs + SDRSP0);
+ }
+ }
+
+ return ret;
+}
+
+static int bcm2835_check_cmd_error(struct bcm2835_host *host, u32 intmask)
+{
+ int ret = -EINVAL;
+
+ if (!(intmask & SDHSTS_ERROR_MASK))
+ return 0;
+
+ dev_err(host->mci.hw_dev, "sdhost_busy_irq: intmask %08x\n", intmask);
+ if (intmask & SDHSTS_CRC7_ERROR) {
+ ret = -EILSEQ;
+ } else if (intmask & (SDHSTS_CRC16_ERROR |
+ SDHSTS_FIFO_ERROR)) {
+ ret = -EILSEQ;
+ } else if (intmask & (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT)) {
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static int bcm2835_check_data_error(struct bcm2835_host *host, u32 intmask)
+{
+ int ret = 0;
+
+ if (intmask & (SDHSTS_CRC16_ERROR | SDHSTS_FIFO_ERROR))
+ ret = -EILSEQ;
+ if (intmask & SDHSTS_REW_TIME_OUT)
+ ret = -ETIMEDOUT;
+
+ if (ret)
+ dev_err(host->mci.hw_dev, "data error %d\n", ret);
+
+ return ret;
+}
+
+static int bcm2835_transmit(struct bcm2835_host *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ u32 intmask = readl(host->regs + SDHSTS);
+ int ret;
+
+ /* Check for errors */
+ if (data) {
+ ret = bcm2835_check_data_error(host, intmask);
+ if (ret)
+ return ret;
+ }
+
+ ret = bcm2835_check_cmd_error(host, intmask);
+ if (ret)
+ return ret;
+
+ /* Handle wait for busy end */
+ if ((cmd->resp_type & MMC_RSP_BUSY) &&
+ (intmask & SDHSTS_BUSY_IRPT)) {
+ writel(SDHSTS_BUSY_IRPT, host->regs + SDHSTS);
+ bcm2835_finish_command(host, cmd);
+ }
+
+ /* Handle PIO data transfer */
+ if (data) {
+ ret = bcm2835_transfer_pio(host, data);
+ if (ret)
+ return ret;
+ /* Transfer successful: wait for command to complete for real */
+ ret = bcm2835_wait_transfer_complete(host);
+ }
+
+ return ret;
+}
+
+static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
+{
+ int div;
+
+ /* The SDCDIV register has 11 bits, and holds (div - 2). But
+ * in data mode the max is 50MHz without a minimum, and only
+ * the bottom 3 bits are used. Since the switch over is
+ * automatic (unless we have marked the card as slow...),
+ * chosen values have to make sense in both modes. Ident mode
+ * must be 100-400KHz, so can range check the requested
+ * clock. CMD15 must be used to return to data mode, so this
+ * can be monitored.
+ *
+ * clock 250MHz -> 0->125MHz, 1->83.3MHz, 2->62.5MHz, 3->50.0MHz
+ * 4->41.7MHz, 5->35.7MHz, 6->31.3MHz, 7->27.8MHz
+ *
+ * 623->400KHz/27.8MHz
+ * reset value (507)->491159/50MHz
+ *
+ * BUT, the 3-bit clock divisor in data mode is too small if
+ * the core clock is higher than 250MHz, so instead use the
+ * SLOW_CARD configuration bit to force the use of the ident
+ * clock divisor at all times.
+ */
+
+ if (clock < 100000) {
+ /* Can't stop the clock, but make it as slow as possible
+ * to show willing
+ */
+ writel(SDCDIV_MAX_CDIV, host->regs + SDCDIV);
+ return;
+ }
+
+ div = host->mci.f_max / clock;
+ if (div < 2)
+ div = 2;
+ if ((host->mci.f_max / div) > clock)
+ div++;
+ div -= 2;
+
+ if (div > SDCDIV_MAX_CDIV)
+ div = SDCDIV_MAX_CDIV;
+
+ clock = host->mci.f_max / (div + 2);
+
+ writel(div, host->regs + SDCDIV);
+
+ /* Set the timeout to 500ms */
+ writel(clock / 2, host->regs + SDTOUT);
+}
+
+static int bcm2835_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct bcm2835_host *host = to_bcm2835_host(mci);
+ u32 edm, fsm;
+ int ret = 0;
+
+ if (data && !is_power_of_2(data->blocksize)) {
+ dev_err(mci->hw_dev, "unsupported block size (%d bytes)\n",
+ data->blocksize);
+ return -EINVAL;
+ }
+
+ edm = readl(host->regs + SDEDM);
+ fsm = edm & SDEDM_FSM_MASK;
+
+ if ((fsm != SDEDM_FSM_IDENTMODE) &&
+ (fsm != SDEDM_FSM_DATAMODE) &&
+ (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)) {
+ dev_err(mci->hw_dev,
+ "previous command (%d) not complete (EDM %08x)\n",
+ readl(host->regs + SDCMD) & SDCMD_CMD_MASK, edm);
+
+ return -EILSEQ;
+ }
+
+ ret = bcm2835_send_command(host, cmd, data);
+ if (ret)
+ return ret;
+
+ if (!(cmd->resp_type & MMC_RSP_BUSY)) {
+ ret = bcm2835_finish_command(host, cmd);
+ if (ret)
+ return ret;
+ }
+
+ /* Wait for completion of busy signal or data transfer */
+ if ((cmd->resp_type & MMC_RSP_BUSY) || data)
+ ret = bcm2835_transmit(host, cmd, data);
+
+ return ret;
+}
+
+static void bcm2835_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct bcm2835_host *host = to_bcm2835_host(mci);
+ u32 hcfg = SDHCFG_WIDE_INT_BUS | SDHCFG_SLOW_CARD;
+
+ if (ios->clock)
+ bcm2835_set_clock(host, ios->clock);
+
+ /* set bus width */
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ hcfg |= SDHCFG_WIDE_EXT_BUS;
+
+ writel(hcfg, host->regs + SDHCFG);
+}
+
+static int bcm2835_sdhost_detect(struct device_d *dev)
+{
+ struct bcm2835_host *host = dev->priv;
+
+ return mci_detect_card(&host->mci);
+}
+
+static int bcm2835_sdhost_probe(struct device_d *dev)
+{
+ struct bcm2835_host *host;
+ struct resource *iores;
+ struct mci_host *mci;
+
+ host = xzalloc(sizeof(*host));
+ mci = &host->mci;
+
+ host->clk = clk_get(dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get iomem region\n");
+ return PTR_ERR(iores);
+ }
+ host->regs = IOMEM(iores->start);
+
+ mci->hw_dev = dev;
+ mci->f_max = clk_get_rate(host->clk);
+ mci->f_min = mci->f_max / SDCDIV_MAX_CDIV;
+ mci->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED_52MHZ |
+ MMC_CAP_SD_HIGHSPEED;
+
+ mci->init = bcm2835_sdhost_init;
+ mci->set_ios = bcm2835_set_ios;
+ mci->send_cmd = bcm2835_send_cmd;
+
+ dev->priv = host;
+ dev->detect = bcm2835_sdhost_detect,
+
+ mci_of_parse(mci);
+
+ return mci_register(mci);
+}
+
+static __maybe_unused struct of_device_id bcm2835_sdhost_compatible[] = {
+ { .compatible = "brcm,bcm2835-sdhost" },
+ { /* sentinel */ }
+};
+
+static struct driver_d bcm2835_sdhost_driver = {
+ .name = "bcm2835-sdhost",
+ .probe = bcm2835_sdhost_probe,
+ .of_compatible = DRV_OF_COMPAT(bcm2835_sdhost_compatible),
+};
+device_platform_driver(bcm2835_sdhost_driver);
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index f7f8c3348d..0251757a2a 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -16,6 +16,7 @@
#include <mci.h>
#include <linux/sizes.h>
#include <asm-generic/sections.h>
+#include <asm/cache.h>
#include <mach/xload.h>
#ifdef CONFIG_ARCH_IMX
#include <mach/atf.h>
@@ -445,7 +446,7 @@ int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long
*/
val = esdhc_read32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
val &= ~0x0000fff0;
- val |= (2 << 8) | (6 << 4);
+ val |= (8 << 8) | (3 << 4);
esdhc_write32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
esdhc_write32(&esdhc, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
@@ -457,6 +458,8 @@ int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long
return ret;
}
+ icache_invalidate();
+
printf("Starting barebox\n");
barebox(r0, r1, r2);
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 9c3925bde1..25db10a9b2 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -23,6 +23,7 @@ config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI
select MTD_SPI_NOR
+ select SPI_MEM
help
This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF,
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 8a67792125..09a8714247 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,12 +27,13 @@
#include <clock.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/spi-nor.h>
+#include <linux/spi/spi-mem.h>
#include <linux/mod_devicetable.h>
#define MAX_CMD_SIZE 6
struct m25p {
- struct spi_device *spi;
+ struct spi_mem *spimem;
struct spi_nor spi_nor;
struct mtd_info mtd;
u8 command[MAX_CMD_SIZE];
@@ -41,71 +42,60 @@ struct m25p {
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(len, val, 1));
int ret;
- ret = spi_write_then_read(spi, &code, 1, val, len);
+ ret = spi_mem_exec_op(flash->spimem, &op);
if (ret < 0)
- dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+ dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+ code);
return ret;
}
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
-{
- /* opcode is in cmd[0] */
- cmd[1] = addr >> (nor->addr_width * 8 - 8);
- cmd[2] = addr >> (nor->addr_width * 8 - 16);
- cmd[3] = addr >> (nor->addr_width * 8 - 24);
- cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor)
-{
- return 1 + nor->addr_width;
-}
-
-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int wr_en)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
-
- flash->command[0] = opcode;
- if (buf)
- memcpy(&flash->command[1], buf, len);
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(len, buf, 1));
- return spi_write(spi, flash->command, len + 1);
+ return spi_mem_exec_op(flash->spimem, &op);
}
static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
- struct spi_transfer t[2] = {};
- struct spi_message m;
- int cmd_sz = m25p_cmdsz(nor);
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(len, buf, 1));
+ int ret;
- spi_message_init(&m);
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
- cmd_sz = 1;
-
- flash->command[0] = nor->program_opcode;
- m25p_addr2cmd(nor, to, flash->command);
+ op.addr.nbytes = 0;
- t[0].tx_buf = flash->command;
- t[0].len = cmd_sz;
- spi_message_add_tail(&t[0], &m);
+ ret = spi_mem_adjust_op_size(flash->spimem, &op);
+ if (ret)
+ return;
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
- spi_sync(spi, &m);
+ ret = spi_mem_exec_op(flash->spimem, &op);
+ if (ret)
+ return;
- *retlen += m.actual_length - cmd_sz;
+ *retlen = op.data.nbytes;
}
/*
@@ -116,46 +106,35 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
- struct spi_transfer t[2];
- struct spi_message m;
- unsigned int dummy = nor->read_dummy;
-
- /* convert the dummy cycles to the number of bytes */
- dummy /= 8;
-
- spi_message_init(&m);
- memset(t, 0, (sizeof t));
-
- flash->command[0] = nor->read_opcode;
- m25p_addr2cmd(nor, from, flash->command);
-
- t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(nor) + dummy;
- spi_message_add_tail(&t[0], &m);
-
- t[1].rx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
-
- spi_sync(spi, &m);
-
- *retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
- return 0;
-}
-
-static int m25p80_erase(struct spi_nor *nor, loff_t offset)
-{
- struct m25p *flash = nor->priv;
-
- dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
- flash->mtd.erasesize / 1024, (u32)offset);
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+ SPI_MEM_OP_DATA_IN(len, buf, 1));
+ size_t remaining = len;
+ int ret;
- /* Set up command buffer. */
- flash->command[0] = nor->erase_opcode;
- m25p_addr2cmd(nor, offset, flash->command);
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+ op.dummy.buswidth = op.addr.buswidth;
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+
+ while (remaining) {
+ op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+ ret = spi_mem_adjust_op_size(flash->spimem, &op);
+ if (ret)
+ return ret;
+ ret = spi_mem_exec_op(flash->spimem, &op);
+ if (ret)
+ return ret;
+ op.addr.val += op.data.nbytes;
+ remaining -= op.data.nbytes;
+ op.data.buf.in += op.data.nbytes;
+ }
- spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
+ *retlen = len;
return 0;
}
@@ -229,10 +208,15 @@ static const struct platform_device_id m25p_ids[] = {
static int m25p_probe(struct device_d *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
+ struct spi_mem *spimem = spi->mem;
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
- enum read_mode mode = SPI_NOR_NORMAL;
+ struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
const char *flash_name = NULL;
int device_id;
bool use_large_blocks;
@@ -247,17 +231,21 @@ static int m25p_probe(struct device_d *dev)
/* install the hooks */
nor->read = m25p80_read;
nor->write = m25p80_write;
- nor->erase = m25p80_erase;
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
- nor->dev = dev;
+ nor->dev = &spimem->spi->dev;
nor->mtd = &flash->mtd;
nor->priv = flash;
flash->mtd.priv = nor;
flash->mtd.parent = &spi->dev;
- flash->spi = spi;
+ flash->spimem = spimem;
+
+ if (spi->mode & SPI_RX_QUAD)
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ else if (spi->mode & SPI_RX_DUAL)
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
dev->priv = (void *)flash;
@@ -276,7 +264,7 @@ static int m25p_probe(struct device_d *dev)
use_large_blocks = of_property_read_bool(dev->device_node,
"use-large-blocks");
- ret = spi_nor_scan(nor, flash_name, mode, use_large_blocks);
+ ret = spi_nor_scan(nor, flash_name, &hwcaps, use_large_blocks);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index ed5377bd4b..11e4d236dd 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -45,6 +45,9 @@ struct cqspi_flash_pdata {
unsigned int tsd2d_ns;
unsigned int tchsh_ns;
unsigned int tslch_ns;
+ u8 inst_width;
+ u8 addr_width;
+ u8 data_width;
};
struct cqspi_st {
@@ -287,9 +290,10 @@ static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, u8 opcode)
f_pdata = &cqspi->f_pdata[cqspi->current_cs];
- if (nor->flash_read == SPI_NOR_QUAD)
- rdreg |= (CQSPI_INST_TYPE_QUAD
- << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB);
+ rdreg |= f_pdata->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
+ rdreg |= f_pdata->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
+ rdreg |= f_pdata->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+
return rdreg;
}
@@ -427,6 +431,7 @@ static int cqspi_command_write_addr(struct spi_nor *nor,
static int cqspi_indirect_read_setup(struct spi_nor *nor,
unsigned int from_addr)
{
+ struct cqspi_flash_pdata *f_pdata;
struct cqspi_st *cqspi = nor->priv;
unsigned int ahb_base = (unsigned int) cqspi->ahb_base;
void __iomem *reg_base = cqspi->iobase;
@@ -437,6 +442,7 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
writel(ahb_base & CQSPI_INDIRECTTRIGGER_ADDR_MASK,
reg_base + CQSPI_REG_INDIRECTTRIGGER);
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+ f_pdata = &cqspi->f_pdata[cqspi->current_cs];
reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
reg |= cqspi_calc_rdreg(nor, nor->read_opcode);
@@ -444,14 +450,10 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
/* Setup dummy clock cycles */
dummy_bytes = nor->read_dummy / 8;
- if (dummy_bytes) {
- struct cqspi_flash_pdata *f_pdata;
-
- f_pdata = &cqspi->f_pdata[cqspi->current_cs];
-
- if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
- dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+ if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
+ dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+ if (dummy_bytes) {
reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
/* Set mode bits high to ensure chip doesn't enter XIP */
writel(0xFF, reg_base + CQSPI_REG_MODE_BIT);
@@ -459,7 +461,8 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
/* Convert to clock cycles. */
dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
/* Need to subtract the mode byte (8 clocks). */
- dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
+ if (f_pdata->inst_width != CQSPI_INST_TYPE_QUAD)
+ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
if (dummy_clk)
reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
@@ -676,51 +679,53 @@ failwr:
return ret;
}
-static void cqspi_write(struct spi_nor *nor, loff_t to,
- size_t len, size_t *retlen, const u_char *buf)
+static void cqspi_controller_enable(struct cqspi_st *cqspi)
{
- int ret;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- ret = cqspi_indirect_write_setup(nor, to);
- if (ret == 0) {
- ret = cqspi_indirect_write_execute(nor, buf, len);
- if (ret == 0)
- *retlen += len;
- }
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
-static int cqspi_read(struct spi_nor *nor, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
+static void cqspi_controller_disable(struct cqspi_st *cqspi)
{
- int ret;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- ret = cqspi_indirect_read_setup(nor, from);
- if (ret == 0) {
- ret = cqspi_indirect_read_execute(nor, buf, len);
- if (ret == 0)
- *retlen += len;
- }
- return ret;
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
-static int cqspi_erase(struct spi_nor *nor, loff_t offs)
+static void cqspi_chipselect(struct cqspi_st *cqspi,
+ unsigned int chip_select,
+ unsigned int decoder_enable)
{
- int ret;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- /* Send write enable, then erase commands. */
- ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
- if (ret)
- return ret;
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ if (decoder_enable) {
+ reg |= CQSPI_REG_CONFIG_DECODE_MASK;
+ } else {
+ reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
- /* Set up command buffer. */
- ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
- if (ret)
- return ret;
+ /* Convert CS if without decoder.
+ * CS0 to 4b'1110
+ * CS1 to 4b'1101
+ * CS2 to 4b'1011
+ * CS3 to 4b'0111
+ */
+ chip_select = 0xF & ~(1 << chip_select);
+ }
- return 0;
+ reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+ reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
static unsigned int calculate_ticks_for_ns(unsigned int ref_clk_hz,
@@ -834,55 +839,6 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi,
writel(reg, reg_base + CQSPI_REG_READCAPTURE);
}
-static void cqspi_chipselect(struct cqspi_st *cqspi,
- unsigned int chip_select,
- unsigned int decoder_enable)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- if (decoder_enable) {
- reg |= CQSPI_REG_CONFIG_DECODE_MASK;
- } else {
- reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
-
- /* Convert CS if without decoder.
- * CS0 to 4b'1110
- * CS1 to 4b'1101
- * CS2 to 4b'1011
- * CS3 to 4b'0111
- */
- chip_select = 0xF & ~(1 << chip_select);
- }
-
- reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
- << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
- reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
- << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_enable(struct cqspi_st *cqspi)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_disable(struct cqspi_st *cqspi)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
{
unsigned int reg;
@@ -908,7 +864,7 @@ static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
cqspi_controller_enable(cqspi);
}
-static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+static int cqspi_configure(struct spi_nor *nor)
{
struct cqspi_st *cqspi = nor->priv;
int cs = cqspi_find_chipselect(nor);
@@ -936,27 +892,119 @@ static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
return 0;
}
+static int cqspi_set_protocol(struct spi_nor *nor, const int read)
+{
+ struct cqspi_st *cqspi = nor->priv;
+ struct cqspi_flash_pdata *f_pdata;
+
+ f_pdata = &cqspi->f_pdata[cqspi->current_cs];
+
+ f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE;
+ f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE;
+ f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+
+ if (read) {
+ switch (nor->read_proto) {
+ case SNOR_PROTO_1_1_1:
+ f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+ break;
+ case SNOR_PROTO_1_1_2:
+ f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
+ break;
+ case SNOR_PROTO_1_1_4:
+ f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ cqspi_configure(nor);
+
+ return 0;
+}
+
+static void cqspi_write(struct spi_nor *nor, loff_t to,
+ size_t len, size_t *retlen, const u_char *buf)
+{
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ return;
+
+ ret = cqspi_set_protocol(nor, 0);
+ if (ret)
+ return;
+
+ ret = cqspi_indirect_write_setup(nor, to);
+ if (ret == 0) {
+ ret = cqspi_indirect_write_execute(nor, buf, len);
+ if (ret == 0)
+ *retlen += len;
+ }
+}
+
+static int cqspi_read(struct spi_nor *nor, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ int ret;
+
+ ret = cqspi_set_protocol(nor, 1);
+ if (ret)
+ return ret;
+
+ ret = cqspi_indirect_read_setup(nor, from);
+ if (ret == 0) {
+ ret = cqspi_indirect_read_execute(nor, buf, len);
+ if (ret == 0)
+ *retlen += len;
+ }
+ return ret;
+}
+
+static int cqspi_erase(struct spi_nor *nor, loff_t offs)
+{
+ int ret;
+
+ ret = cqspi_set_protocol(nor, 0);
+ if (ret)
+ return ret;
+
+ /* Send write enable, then erase commands. */
+ ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
+ if (ret)
+ return ret;
+
+ /* Set up command buffer. */
+ ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
int ret;
- cqspi_prep(nor, SPI_NOR_OPS_READ);
+ ret = cqspi_set_protocol(nor, 0);
+ if (!ret)
+ ret = cqspi_command_read(nor, &opcode, 1, buf, len);
- ret = cqspi_command_read(nor, &opcode, 1, buf, len);
return ret;
}
-static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int write_enable)
+static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
int ret = 0;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- cqspi_prep(nor, SPI_NOR_OPS_WRITE);
+ ret = cqspi_set_protocol(nor, 0);
+ if (!ret)
+ ret = cqspi_command_write(nor, opcode, buf, len);
- ret = cqspi_command_write(nor, opcode, buf, len);
return ret;
}
@@ -1034,6 +1082,13 @@ static int cqspi_setup_flash(struct device_d *dev,
struct cqspi_flash_pdata *f_pdata,
struct device_node *np)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP,
+ };
struct cqspi_st *cqspi = dev->priv;
struct mtd_info *mtd;
struct spi_nor *nor;
@@ -1075,7 +1130,7 @@ static int cqspi_setup_flash(struct device_d *dev,
nor->write = cqspi_write;
nor->erase = cqspi_erase;
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD, false);
+ ret = spi_nor_scan(nor, NULL, &hwcaps, false);
if (ret)
goto probe_failed;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 85b55c6982..43bd402f9f 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -25,6 +25,7 @@
#include <spi/flash.h>
#define SPI_NOR_MAX_ID_LEN 6
+#define SPI_NOR_MAX_ADDR_WIDTH 4
/*
* For everything but full-chip erase; probably could be much smaller, but kept
@@ -57,14 +58,85 @@ struct flash_info {
u16 addr_width;
u16 flags;
-#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
-#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
-#define SST_WRITE 0x04 /* use SST byte programming */
-#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
-#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
-#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
-#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
-#define USE_FSR 0x80 /* use flag status register */
+#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
+#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
+#define SST_WRITE BIT(2) /* use SST byte programming */
+#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
+#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
+#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
+#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
+#define USE_FSR BIT(7) /* use flag status register */
+#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
+#define SPI_NOR_HAS_TB BIT(9) /*
+ * Flash SR has Top/Bottom (TB) protect
+ * bit. Must be used with
+ * SPI_NOR_HAS_LOCK.
+ */
+#define SPI_S3AN BIT(10) /*
+ * Xilinx Spartan 3AN In-System Flash
+ * (MFR cannot be used for probing
+ * because it has the same value as
+ * ATMEL flashes)
+ */
+#define SPI_NOR_4B_OPCODES BIT(11) /*
+ * Use dedicated 4byte address op codes
+ * to support memory size above 128Mib.
+ */
+#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
+#define USE_CLSR BIT(14) /* use CLSR command */
+#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */
+};
+
+enum spi_nor_read_command_index {
+ SNOR_CMD_READ,
+ SNOR_CMD_READ_FAST,
+
+ /* Dual SPI */
+ SNOR_CMD_READ_1_1_2,
+ SNOR_CMD_READ_1_2_2,
+ SNOR_CMD_READ_2_2_2,
+
+ /* Quad SPI */
+ SNOR_CMD_READ_1_1_4,
+ SNOR_CMD_READ_1_4_4,
+ SNOR_CMD_READ_4_4_4,
+
+ SNOR_CMD_READ_MAX
+};
+
+struct spi_nor_read_command {
+ u8 num_mode_clocks;
+ u8 num_wait_states;
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+struct spi_nor_pp_command {
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+enum spi_nor_pp_command_index {
+ SNOR_CMD_PP,
+
+ /* Quad SPI */
+ SNOR_CMD_PP_1_1_4,
+ SNOR_CMD_PP_1_4_4,
+ SNOR_CMD_PP_4_4_4,
+
+ SNOR_CMD_PP_MAX
+};
+
+struct spi_nor_flash_parameter {
+ u64 size;
+ u32 page_size;
+
+ struct spi_nor_hwcaps hwcaps;
+ struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
+ struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
+
+ int (*quad_enable)(struct spi_nor *nor);
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -129,31 +201,13 @@ static int read_cr(struct spi_nor *nor)
}
/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
- return 8;
- case SPI_NOR_NORMAL:
- return 0;
- }
- return 0;
-}
-
-/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
static inline int write_sr(struct spi_nor *nor, u8 val)
{
nor->cmd_buf[0] = val;
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
}
/*
@@ -162,7 +216,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val)
*/
static inline int write_enable(struct spi_nor *nor)
{
- return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
}
/*
@@ -170,7 +224,7 @@ static inline int write_enable(struct spi_nor *nor)
*/
static inline int write_disable(struct spi_nor *nor)
{
- return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
}
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
@@ -178,6 +232,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
return mtd->priv;
}
+static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == opcode)
+ return table[i][1];
+
+ /* No conversion found, keep input op code. */
+ return opcode;
+}
+
+static u8 spi_nor_convert_3to4_read(u8 opcode)
+{
+ static const u8 spi_nor_3to4_read[][2] = {
+ { SPINOR_OP_READ, SPINOR_OP_READ_4B },
+ { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
+ { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
+ { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
+ { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
+ { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
+
+ { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
+ { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
+ { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+ ARRAY_SIZE(spi_nor_3to4_read));
+}
+
+static u8 spi_nor_convert_3to4_program(u8 opcode)
+{
+ static const u8 spi_nor_3to4_program[][2] = {
+ { SPINOR_OP_PP, SPINOR_OP_PP_4B },
+ { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
+ { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+ ARRAY_SIZE(spi_nor_3to4_program));
+}
+
+static u8 spi_nor_convert_3to4_erase(u8 opcode)
+{
+ static const u8 spi_nor_3to4_erase[][2] = {
+ { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
+ { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
+ { SPINOR_OP_SE, SPINOR_OP_SE_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+ ARRAY_SIZE(spi_nor_3to4_erase));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
+{
+ /* Do some manufacturer fixups first */
+ switch (JEDEC_MFR(nor->info)) {
+ case SNOR_MFR_SPANSION:
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd->erasesize = nor->info->sector_size;
+ break;
+
+ default:
+ break;
+ }
+
+ nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+ nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+ nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+}
+
+
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
int enable)
@@ -196,7 +325,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
write_enable(nor);
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
- status = nor->write_reg(nor, cmd, NULL, 0, 0);
+ status = nor->write_reg(nor, cmd, NULL, 0);
if (need_wren)
write_disable(nor);
@@ -204,7 +333,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
default:
/* Spansion style */
nor->cmd_buf[0] = enable << 7;
- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
}
}
static inline int spi_nor_sr_ready(struct spi_nor *nor)
@@ -279,7 +408,7 @@ static int erase_chip(struct spi_nor *nor)
{
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
- return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
}
static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
@@ -307,6 +436,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
}
/*
+ * Initiate the erasure of a single sector
+ */
+static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+{
+ u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+ int i;
+
+ if (nor->erase)
+ return nor->erase(nor, addr);
+
+ /*
+ * Default implementation, if driver doesn't have a specialized HW
+ * control
+ */
+ for (i = nor->addr_width - 1; i >= 0; i--) {
+ buf[i] = addr & 0xff;
+ addr >>= 8;
+ }
+
+ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+}
+
+/*
* Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
@@ -370,10 +522,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) {
write_enable(nor);
- if (nor->erase(nor, addr)) {
- ret = -EIO;
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
goto erase_err;
- }
addr += mtd->erasesize;
len -= mtd->erasesize;
@@ -540,6 +691,9 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+ { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
@@ -646,6 +800,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) },
{ "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) },
{ "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) },
+ { "w25q40bw", INFO(0xef5013, 0, 64 * 1024, 8, SECT_4K) },
{ "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
@@ -784,7 +939,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
- u32 page_offset, page_size, i;
+ size_t page_offset, page_remain, i;
+ size_t retval;
int ret;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
@@ -793,32 +949,23 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
- write_enable(nor);
-
- page_offset = to & (nor->page_size - 1);
-
- /* do all the bytes fit onto one page? */
- if (page_offset + len <= nor->page_size) {
- nor->write(nor, to, len, retlen, buf);
- } else {
- /* the size of data remaining on the first page */
- page_size = nor->page_size - page_offset;
- nor->write(nor, to, page_size, retlen, buf);
+ for (i = 0; i < len; ) {
+ ssize_t written;
- /* write everything in nor->page_size chunks */
- for (i = page_size; i < len; i += page_size) {
- page_size = len - i;
- if (page_size > nor->page_size)
- page_size = nor->page_size;
+ page_offset = (to + i) & (nor->page_size - 1);
+ page_remain = min_t(size_t, nor->page_size - page_offset,
+ len - i);
- ret = spi_nor_wait_till_ready(nor);
- if (ret)
- goto write_err;
+ write_enable(nor);
+ nor->write(nor, to + i, page_remain, &retval, buf + i);
+ written = retval;
- write_enable(nor);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto write_err;
- nor->write(nor, to + i, page_size, retlen, buf + i);
- }
+ *retlen += written;
+ i += written;
}
ret = spi_nor_wait_till_ready(nor);
@@ -827,28 +974,6 @@ write_err:
return ret;
}
-static int macronix_quad_enable(struct spi_nor *nor)
-{
- int ret, val;
-
- val = read_sr(nor);
- write_enable(nor);
-
- nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
- nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
-
- if (spi_nor_wait_till_ready(nor))
- return 1;
-
- ret = read_sr(nor);
- if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
- dev_err(nor->dev, "Macronix Quad bit not set\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
/*
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
@@ -860,7 +985,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val)
nor->cmd_buf[0] = val & 0xff;
nor->cmd_buf[1] = (val >> 8);
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
}
static int spansion_quad_enable(struct spi_nor *nor)
@@ -887,42 +1012,272 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
+static int spi_nor_check(struct spi_nor *nor)
{
- int status;
+ if (!nor->dev || !nor->read || !nor->write ||
+ !nor->read_reg || !nor->write_reg) {
+ pr_err("spi-nor: please fill all the necessary fields!\n");
+ return -EINVAL;
+ }
- switch (JEDEC_MFR(info)) {
- case CFI_MFR_MACRONIX:
- status = macronix_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- default:
- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ return 0;
+}
+
+static void
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ read->num_mode_clocks = num_mode_clocks;
+ read->num_wait_states = num_wait_states;
+ read->opcode = opcode;
+ read->proto = proto;
+}
+
+static void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ pp->opcode = opcode;
+ pp->proto = proto;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+ const struct flash_info *info,
+ struct spi_nor_flash_parameter *params)
+{
+ /* Set legacy flash parameters as default. */
+ memset(params, 0, sizeof(*params));
+
+ /* Set SPI NOR sizes. */
+ params->size = info->sector_size * info->n_sectors;
+ params->page_size = info->page_size;
+
+ /* (Fast) Read settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+ 0, 0, SPINOR_OP_READ,
+ SNOR_PROTO_1_1_1);
+
+ if (!(info->flags & SPI_NOR_NO_FR)) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+ 0, 8, SPINOR_OP_READ_FAST,
+ SNOR_PROTO_1_1_1);
+ }
+
+ if (info->flags & SPI_NOR_DUAL_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2,
+ SNOR_PROTO_1_1_2);
}
+
+ if (info->flags & SPI_NOR_QUAD_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4,
+ SNOR_PROTO_1_1_4);
+ }
+
+ /* Page Program settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_PP;
+ spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+ SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+ /* Select the procedure to set the Quad Enable bit. */
+ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
+ SNOR_HWCAPS_PP_QUAD))
+ params->quad_enable = spansion_quad_enable;
+
+ return 0;
}
-static int spi_nor_check(struct spi_nor *nor)
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
{
- if (!nor->dev || !nor->read || !nor->write ||
- !nor->read_reg || !nor->write_reg || !nor->erase) {
- pr_err("spi-nor: please fill all the necessary fields!\n");
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == (int)hwcaps)
+ return table[i][1];
+
+ return -EINVAL;
+}
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
+{
+ static const int hwcaps_read2cmd[][2] = {
+ { SNOR_HWCAPS_READ, SNOR_CMD_READ },
+ { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
+ { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
+ { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
+ { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
+ { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
+ { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
+ { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
+ ARRAY_SIZE(hwcaps_read2cmd));
+}
+
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+{
+ static const int hwcaps_pp2cmd[][2] = {
+ { SNOR_HWCAPS_PP, SNOR_CMD_PP },
+ { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
+ { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
+ { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
+ ARRAY_SIZE(hwcaps_pp2cmd));
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+ const struct spi_nor_read_command *read;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ read = &params->reads[cmd];
+ nor->read_opcode = read->opcode;
+ nor->read_proto = read->proto;
+
+ /*
+ * In the spi-nor framework, we don't need to make the difference
+ * between mode clock cycles and wait state clock cycles.
+ * Indeed, the value of the mode clock cycles is used by a QSPI
+ * flash memory to know whether it should enter or leave its 0-4-4
+ * (Continuous Read / XIP) mode.
+ * eXecution In Place is out of the scope of the mtd sub-system.
+ * Hence we choose to merge both mode and wait state clock cycles
+ * into the so called dummy clock cycles.
+ */
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+ return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+ const struct spi_nor_pp_command *pp;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
+ if (cmd < 0)
return -EINVAL;
+
+ pp = &params->page_programs[cmd];
+ nor->program_opcode = pp->opcode;
+ nor->write_proto = pp->proto;
+ return 0;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ struct mtd_info *mtd = nor->mtd;
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+ if (info->flags & SECT_4K) {
+ nor->erase_opcode = SPINOR_OP_BE_4K;
+ mtd->erasesize = 4096;
+ } else if (info->flags & SECT_4K_PMC) {
+ nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+ mtd->erasesize = 4096;
+ } else
+#endif
+ {
+ nor->erase_opcode = SPINOR_OP_SE;
+ mtd->erasesize = info->sector_size;
+ }
+ return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_flash_parameter *params,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ u32 ignored_mask, shared_mask;
+ bool enable_quad_io;
+ int err;
+
+ /*
+ * Keep only the hardware capabilities supported by both the SPI
+ * controller and the SPI flash memory.
+ */
+ shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+ /* SPI n-n-n protocols are not supported yet. */
+ ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
+ SNOR_HWCAPS_READ_4_4_4 |
+ SNOR_HWCAPS_PP_4_4_4);
+ if (shared_mask & ignored_mask) {
+ dev_dbg(nor->dev,
+ "SPI n-n-n protocols are not supported yet.\n");
+ shared_mask &= ~ignored_mask;
+ }
+
+ /* Select the (Fast) Read command. */
+ err = spi_nor_select_read(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select read settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Page Program command. */
+ err = spi_nor_select_pp(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select write settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Sector Erase command. */
+ err = spi_nor_select_erase(nor, info);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select erase settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Enable Quad I/O if needed. */
+ enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4);
+ if (enable_quad_io && params->quad_enable) {
+ err = params->quad_enable(nor);
+ if (err) {
+ dev_err(nor->dev, "quad mode not supported\n");
+ return err;
+ }
}
return 0;
}
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_hwcaps *hwcaps,
bool use_large_blocks)
{
+ struct spi_nor_flash_parameter params;
const struct spi_device_id *id = NULL;
struct flash_info *info;
struct device_d *dev = nor->dev;
@@ -935,6 +1290,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
if (ret)
return ret;
+ /* Reset SPI protocol for all commands. */
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+
/* Try to auto-detect if chip name wasn't specified */
if (!name)
id = spi_nor_read_id(nor);
@@ -970,6 +1330,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
}
}
+ nor->info = info;
+
mutex_init(&nor->lock);
/*
@@ -984,12 +1346,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
write_sr(nor, 0);
}
+ /* Parse the Serial Flash Discoverable Parameters table. */
+ ret = spi_nor_init_params(nor, info, &params);
+ if (ret)
+ return ret;
+
if (!mtd->name)
mtd->name = (char *) dev_name(dev);
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
- mtd->size = info->sector_size * info->n_sectors;
+ mtd->size = params.size;
mtd->erase = spi_nor_erase;
mtd->read = spi_nor_read;
@@ -1008,107 +1375,57 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR;
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
- /* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K && !use_large_blocks) {
- nor->erase_opcode = SPINOR_OP_BE_4K;
- mtd->erasesize = 4096;
- } else if (info->flags & SECT_4K_PMC && !use_large_blocks) {
- nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
- mtd->erasesize = 4096;
- } else
-#endif
- {
- nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
- }
-
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
- nor->page_size = info->page_size;
+ nor->page_size = params.page_size;
mtd->writebufsize = nor->page_size;
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
else
- nor->flash_read = SPI_NOR_NORMAL;
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
} else {
/* If we weren't instantiated by DT, default to fast-read */
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & SPI_NOR_NO_FR)
- nor->flash_read = SPI_NOR_NORMAL;
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
- ret = set_quad_mode(nor, info);
- if (ret) {
- dev_err(dev, "quad mode not supported\n");
- return ret;
- }
- nor->flash_read = SPI_NOR_QUAD;
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
- nor->flash_read = SPI_NOR_DUAL;
- }
-
- /* Default commands */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ;
- break;
- default:
- dev_err(dev, "No Read opcode defined\n");
- return -EINVAL;
- }
-
- nor->program_opcode = SPINOR_OP_PP;
+ /*
+ * Configure the SPI memory:
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
+ * - set the number of dummy cycles (mode cycles + wait states).
+ * - set the SPI protocols for register and memory accesses.
+ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+ */
+ ret = spi_nor_setup(nor, info, &params, hwcaps);
+ if (ret)
+ return ret;
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
- if (JEDEC_MFR(info) == CFI_MFR_AMD) {
- /* Dedicated 4-byte command set */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ4_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ4_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ4;
- break;
- }
- nor->program_opcode = SPINOR_OP_PP_4B;
- /* No small sector erase for 4-byte command set */
- nor->erase_opcode = SPINOR_OP_SE_4B;
- mtd->erasesize = info->sector_size;
- } else
+ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+ info->flags & SPI_NOR_4B_OPCODES)
+ spi_nor_set_4byte_opcodes(nor);
+ else
set_4byte(nor, info, 1);
} else {
nor->addr_width = 3;
}
- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+ dev_err(dev, "address width is too large: %u\n",
+ nor->addr_width);
+ return -EINVAL;
+ }
dev_info(dev, "%s (%lld Kbytes)\n", id->name,
(long long)mtd->size >> 10);
diff --git a/drivers/mtd/ubi/ubi-barebox.h b/drivers/mtd/ubi/ubi-barebox.h
index 557ad88316..7ee87ffd3e 100644
--- a/drivers/mtd/ubi/ubi-barebox.h
+++ b/drivers/mtd/ubi/ubi-barebox.h
@@ -30,7 +30,6 @@
#define crc32(seed, data, length) crc32_no_comp(seed, (unsigned char * const)data, length)
/* configurable */
-#define CONFIG_MTD_UBI_WL_THRESHOLD 4096
#define UBI_IO_DEBUG 0
/* upd.c */
diff --git a/drivers/net/fsl-fman.c b/drivers/net/fsl-fman.c
index 1a11ca4926..4e6bb2ecfd 100644
--- a/drivers/net/fsl-fman.c
+++ b/drivers/net/fsl-fman.c
@@ -640,6 +640,8 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
i * MAX_RXBUF_LEN));
buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool +
i * MAX_RXBUF_LEN));
+ dma_sync_single_for_device((unsigned long)rx_buf_pool + i * MAX_RXBUF_LEN,
+ MAX_RXBUF_LEN, DMA_FROM_DEVICE);
muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi);
out_be32(&rxbd->buf_ptr_lo, buf_lo);
rxbd++;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 7d181949ee..9d2c6e614b 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -38,6 +38,12 @@ config RTC_DRV_ABRACON
endif # I2C
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_IMX
+ help
+ Support for Freescale IMX DryIce RTC
+
config RTC_DRV_JZ4740
tristate "Ingenic JZ4740 RTC"
depends on MACH_MIPS_XBURST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 68741c26a1..1308beff38 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_RTC_CLASS) += class.o
obj-$(CONFIG_RTC_DRV_ABRACON) += rtc-abracon.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000000..8fcaf631ff
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <rtc.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/rtc.h>
+#include <linux/nvmem-provider.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TDCHL (1 << 30) /* Tamper-detect configuration hard lock */
+#define DCR_TDCSL (1 << 29) /* Tamper-detect configuration soft lock */
+#define DCR_KSSL (1 << 27) /* Key-select soft lock */
+#define DCR_MCHL (1 << 20) /* Monotonic-counter hard lock */
+#define DCR_MCSL (1 << 19) /* Monotonic-counter soft lock */
+#define DCR_TCHL (1 << 18) /* Timer-counter hard lock */
+#define DCR_TCSL (1 << 17) /* Timer-counter soft lock */
+#define DCR_FSHL (1 << 16) /* Failure state hard lock */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+#define DCR_MCE (1 << 2) /* Monotonic Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WTD (1 << 23) /* Wire-mesh tamper detected */
+#define DSR_ETBD (1 << 22) /* External tamper B detected */
+#define DSR_ETAD (1 << 21) /* External tamper A detected */
+#define DSR_EBD (1 << 20) /* External boot detected */
+#define DSR_SAD (1 << 19) /* SCC alarm detected */
+#define DSR_TTD (1 << 18) /* Temperature tamper detected */
+#define DSR_CTD (1 << 17) /* Clock tamper detected */
+#define DSR_VTD (1 << 16) /* Voltage tamper detected */
+#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */
+#define DSR_WNF (1 << 9) /* Write Next Flag (synchronous) */
+#define DSR_WCF (1 << 8) /* Write Complete Flag (synchronous)*/
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_MCO (1 << 3) /* monotonic counter overflow */
+#define DSR_TCO (1 << 2) /* time counter overflow */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg (synchronous) */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+#define DIER_SVIE (1 << 0) /* Security-violation Interrupt Enable */
+
+#define DMCR 0x1c /* DryIce Monotonic Counter Reg */
+
+#define DTCR 0x28 /* DryIce Tamper Configuration Reg */
+#define DTCR_MOE (1 << 9) /* monotonic overflow enabled */
+#define DTCR_TOE (1 << 8) /* time overflow enabled */
+#define DTCR_WTE (1 << 7) /* wire-mesh tamper enabled */
+#define DTCR_ETBE (1 << 6) /* external B tamper enabled */
+#define DTCR_ETAE (1 << 5) /* external A tamper enabled */
+#define DTCR_EBE (1 << 4) /* external boot tamper enabled */
+#define DTCR_SAIE (1 << 3) /* SCC enabled */
+#define DTCR_TTE (1 << 2) /* temperature tamper enabled */
+#define DTCR_CTE (1 << 1) /* clock tamper enabled */
+#define DTCR_VTE (1 << 0) /* voltage tamper enabled */
+
+#define DGPR 0x3c /* DryIce General Purpose Reg */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @dev: pionter to dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ */
+struct imxdi_dev {
+ struct device_d *dev;
+ struct rtc_device rtc;
+ void __iomem *ioaddr;
+ struct clk *clk;
+ u32 dsr;
+ struct nvmem_device *nvmem;
+};
+
+/* Some background:
+ *
+ * The DryIce unit is a complex security/tamper monitor device. To be able do
+ * its job in a useful manner it runs a bigger statemachine to bring it into
+ * security/tamper failure state and once again to bring it out of this state.
+ *
+ * This unit can be in one of three states:
+ *
+ * - "NON-VALID STATE"
+ * always after the battery power was removed
+ * - "FAILURE STATE"
+ * if one of the enabled security events has happened
+ * - "VALID STATE"
+ * if the unit works as expected
+ *
+ * Everything stops when the unit enters the failure state including the RTC
+ * counter (to be able to detect the time the security event happened).
+ *
+ * The following events (when enabled) let the DryIce unit enter the failure
+ * state:
+ *
+ * - wire-mesh-tamper detect
+ * - external tamper B detect
+ * - external tamper A detect
+ * - temperature tamper detect
+ * - clock tamper detect
+ * - voltage tamper detect
+ * - RTC counter overflow
+ * - monotonic counter overflow
+ * - external boot
+ *
+ * If we find the DryIce unit in "FAILURE STATE" and the TDCHL cleared, we
+ * can only detect this state. In this case the unit is completely locked and
+ * must force a second "SYSTEM POR" to bring the DryIce into the
+ * "NON-VALID STATE" + "FAILURE STATE" where a recovery is possible.
+ * If the TDCHL is set in the "FAILURE STATE" we are out of luck. In this case
+ * a battery power cycle is required.
+ *
+ * In the "NON-VALID STATE" + "FAILURE STATE" we can clear the "FAILURE STATE"
+ * and recover the DryIce unit. By clearing the "NON-VALID STATE" as the last
+ * task, we bring back this unit into life.
+ */
+
+/*
+ * Do a write into the unit without interrupt support.
+ * We do not need to check the WEF here, because the only reason this kind of
+ * write error can happen is if we write to the unit twice within the 122 us
+ * interval. This cannot happen, since we are using this function only while
+ * setting up the unit.
+ */
+static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val,
+ unsigned reg)
+{
+ /* do the register write */
+ writel(val, imxdi->ioaddr + reg);
+
+ /*
+ * now it takes four 32,768 kHz clock cycles to take
+ * the change into effect = 122 us
+ */
+ udelay(130);
+}
+
+static void di_what_is_to_be_done(struct imxdi_dev *imxdi,
+ const char *power_supply)
+{
+ dev_emerg(imxdi->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n",
+ power_supply);
+}
+
+static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr;
+
+ dev_dbg(imxdi->dev, "DSR register reports: %08X\n", dsr);
+
+ dcr = readl(imxdi->ioaddr + DCR);
+
+ if (dcr & DCR_FSHL) {
+ /* we are out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ /*
+ * with the next SYSTEM POR we will transit from the "FAILURE STATE"
+ * into the "NON-VALID STATE" + "FAILURE STATE"
+ */
+ di_what_is_to_be_done(imxdi, "main");
+
+ return -ENODEV;
+}
+
+static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ /* initialize alarm */
+ di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR);
+ di_write_busy_wait(imxdi, 0, DCALR);
+
+ /* clear alarm flag */
+ if (dsr & DSR_CAF)
+ di_write_busy_wait(imxdi, DSR_CAF, DSR);
+
+ return 0;
+}
+
+static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr, sec;
+
+ /*
+ * lets disable all sources which can force the DryIce unit into
+ * the "FAILURE STATE" for now
+ */
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
+ /* and lets protect them at runtime from any change */
+ di_write_busy_wait(imxdi, DCR_TDCSL, DCR);
+
+ sec = readl(imxdi->ioaddr + DTCMR);
+ if (sec != 0)
+ dev_warn(imxdi->dev,
+ "The security violation has happened at %u seconds\n",
+ sec);
+ /*
+ * the timer cannot be set/modified if
+ * - the TCHL or TCSL bit is set in DCR
+ */
+ dcr = readl(imxdi->ioaddr + DCR);
+ if (!(dcr & DCR_TCE)) {
+ if (dcr & DCR_TCHL) {
+ /* we are out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ if (dcr & DCR_TCSL) {
+ di_what_is_to_be_done(imxdi, "main");
+ return -ENODEV;
+ }
+ }
+ /*
+ * - the timer counter stops/is stopped if
+ * - its overflow flag is set (TCO in DSR)
+ * -> clear overflow bit to make it count again
+ * - NVF is set in DSR
+ * -> clear non-valid bit to make it count again
+ * - its TCE (DCR) is cleared
+ * -> set TCE to make it count
+ * - it was never set before
+ * -> write a time into it (required again if the NVF was set)
+ */
+ /* state handled */
+ di_write_busy_wait(imxdi, DSR_NVF, DSR);
+ /* clear overflow flag */
+ di_write_busy_wait(imxdi, DSR_TCO, DSR);
+ /* enable the counter */
+ di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR);
+ /* set and trigger it to make it count */
+ di_write_busy_wait(imxdi, sec, DTCMR);
+
+ /* now prepare for the valid state */
+ return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR));
+}
+
+static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr;
+
+ /*
+ * now we must first remove the tamper sources in order to get the
+ * device out of the "FAILURE STATE"
+ * To disable any of the following sources we need to modify the DTCR
+ */
+ if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD |
+ DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) {
+ dcr = __raw_readl(imxdi->ioaddr + DCR);
+ if (dcr & DCR_TDCHL) {
+ /*
+ * the tamper register is locked. We cannot disable the
+ * tamper detection. The TDCHL can only be reset by a
+ * DRYICE POR, but we cannot force a DRYICE POR in
+ * softwere because we are still in "FAILURE STATE".
+ * We need a DRYICE POR via battery power cycling....
+ */
+ /*
+ * out of luck!
+ * we cannot disable them without a DRYICE POR
+ */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ if (dcr & DCR_TDCSL) {
+ /* a soft lock can be removed by a SYSTEM POR */
+ di_what_is_to_be_done(imxdi, "main");
+ return -ENODEV;
+ }
+ }
+
+ /* disable all sources */
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
+
+ /* clear the status bits now */
+ di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD |
+ DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD |
+ DSR_MCO | DSR_TCO), DSR);
+
+ dsr = readl(imxdi->ioaddr + DSR);
+ if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
+ DSR_WCF | DSR_WEF)) != 0)
+ dev_warn(imxdi->dev,
+ "There are still some sources of pain in DSR: %08x!\n",
+ dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
+ DSR_WCF | DSR_WEF));
+
+ /*
+ * now we are trying to clear the "Security-violation flag" to
+ * get the DryIce out of this state
+ */
+ di_write_busy_wait(imxdi, DSR_SVF, DSR);
+
+ /* success? */
+ dsr = readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_SVF) {
+ dev_crit(imxdi->dev,
+ "Cannot clear the security violation flag. We are ending up in an endless loop!\n");
+ /* last resort */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+
+ /*
+ * now we have left the "FAILURE STATE" and ending up in the
+ * "NON-VALID STATE" time to recover everything
+ */
+ return di_handle_invalid_state(imxdi, dsr);
+}
+
+static int di_handle_state(struct imxdi_dev *imxdi)
+{
+ int rc;
+ u32 dsr;
+
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ switch (dsr & (DSR_NVF | DSR_SVF)) {
+ case DSR_NVF:
+ dev_warn(imxdi->dev, "Invalid stated unit detected\n");
+ rc = di_handle_invalid_state(imxdi, dsr);
+ break;
+ case DSR_SVF:
+ dev_warn(imxdi->dev, "Failure stated unit detected\n");
+ rc = di_handle_failure_state(imxdi, dsr);
+ break;
+ case DSR_NVF | DSR_SVF:
+ dev_warn(imxdi->dev,
+ "Failure+Invalid stated unit detected\n");
+ rc = di_handle_invalid_and_failure_state(imxdi, dsr);
+ break;
+ default:
+ dev_notice(imxdi->dev, "Unlocked unit detected\n");
+ rc = di_handle_valid_state(imxdi, dsr);
+ }
+
+ return rc;
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(imxdi->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(imxdi->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int rc = 0;
+ uint32_t dsr;
+ uint64_t start;
+
+ /* do the register write */
+ writel(val, imxdi->ioaddr + reg);
+
+ start = get_time_ns();
+
+ /* wait for the write to finish */
+ while (1) {
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ if (dsr & (DSR_WCF | DSR_WEF))
+ break;
+ if (is_timeout(start, MSECOND))
+ return -EIO;
+ }
+
+ /* check for write error */
+ if (dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static struct imxdi_dev *to_imxdi_dev(struct rtc_device *rtc)
+{
+ return container_of(rtc, struct imxdi_dev, rtc);
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = to_imxdi_dev(rtc);
+ unsigned long now;
+
+ now = readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = to_imxdi_dev(rtc);
+ u32 dcr, dsr;
+ int ret;
+ unsigned long secs;
+
+ ret = rtc_tm_to_time(tm, &secs);
+ if (ret)
+ return ret;
+
+ dcr = readl(imxdi->ioaddr + DCR);
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ if (!(dcr & DCR_TCE) || (dsr & DSR_SVF)) {
+ if (dcr & DCR_TCHL) {
+ /* we are even more out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -EPERM;
+ }
+ if ((dcr & DCR_TCSL) || (dsr & DSR_SVF)) {
+ /* we are out of luck for now */
+ di_what_is_to_be_done(imxdi, "main");
+ return -EPERM;
+ }
+ }
+
+ /* zero the fractional part first */
+ ret = di_write_wait(imxdi, 0, DTCLR);
+ if (ret)
+ return ret;
+
+ ret = di_write_wait(imxdi, secs, DTCMR);
+ if (ret)
+ return ret;
+
+ return di_write_wait(imxdi, readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR);
+}
+
+static const struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_time = dryice_rtc_set_time,
+};
+
+static int nvstore_write(struct device_d *dev, const int reg, const void *val,
+ int bytes)
+{
+ struct imxdi_dev *imxdi = dev->parent->priv;
+ const u32 *val32 = val;
+
+ if (bytes != 4)
+ return 0;
+
+ writel(*val32, imxdi->ioaddr + DGPR);
+
+ return 0;
+}
+
+static int nvstore_read(struct device_d *dev, const int reg, void *val,
+ int bytes)
+{
+ struct imxdi_dev *imxdi = dev->parent->priv;
+ u32 *val32 = val;
+
+ if (bytes != 4)
+ return 0;
+
+ *val32 = readl(imxdi->ioaddr + DGPR);
+
+ return 0;
+}
+
+static struct nvmem_bus nvstore_nvmem_bus = {
+ .write = nvstore_write,
+ .read = nvstore_read,
+};
+
+static struct nvmem_config nvstore_nvmem_config = {
+ .name = "nvstore",
+ .stride = 4,
+ .word_size = 4,
+ .size = 4,
+ .bus = &nvstore_nvmem_bus,
+};
+
+static int __init dryice_rtc_probe(struct device_d *dev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int ret;
+
+ imxdi = xzalloc(sizeof(*imxdi));
+
+ imxdi->dev = dev;
+ imxdi->rtc.ops = &dryice_rtc_ops;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ imxdi->ioaddr = IOMEM(res->start);
+
+ imxdi->clk = clk_get(dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+
+ ret = clk_enable(imxdi->clk);
+ if (ret)
+ return ret;
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ writel(0, imxdi->ioaddr + DIER);
+
+ ret = di_handle_state(imxdi);
+ if (ret)
+ goto err;
+
+ dev->priv = imxdi;
+
+ nvstore_nvmem_config.dev = dev;
+
+ imxdi->nvmem = nvmem_register(&nvstore_nvmem_config);
+ if (IS_ENABLED(CONFIG_NVMEM) && IS_ERR(imxdi->nvmem)) {
+ ret = PTR_ERR(imxdi->nvmem);
+ goto err;
+ }
+
+ ret = rtc_register(&imxdi->rtc);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ clk_disable(imxdi->clk);
+
+ return ret;
+}
+
+static __maybe_unused const struct of_device_id dryice_dt_ids[] = {
+ { .compatible = "fsl,imx25-rtc" },
+ { /* sentinel */ }
+};
+
+static struct driver_d dryice_rtc_driver = {
+ .name = "imx-di-rtc",
+ .probe = dryice_rtc_probe,
+ .of_compatible = DRV_OF_COMPAT(dryice_dt_ids),
+};
+device_platform_driver(dryice_rtc_driver);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fed628c589..d687105ea4 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -6,6 +6,13 @@ config SPI
if SPI
+config SPI_MEM
+ bool "SPI memory extension"
+ help
+ Enable this option if you want to enable the SPI memory extension.
+ This extension is meant to simplify interaction with SPI memories
+ by providing a high-level interface to send memory-like commands.
+
config DRIVER_SPI_ALTERA
bool "Altera SPI Master driver"
depends on NIOS2
@@ -18,6 +25,17 @@ config DRIVER_SPI_ATMEL
bool "Atmel (AT91) SPI Master driver"
depends on ARCH_AT91
+config DRIVER_SPI_FSL_QUADSPI
+ bool "Freescale QSPI controller"
+ depends on ARCH_IMX25 || ARCH_IMX31 || ARCH_IMX35 || ARCH_IMX50 || ARCH_IMX53 || ARCH_LAYERSCAPE
+ depends on SPI_MEM
+ help
+ This enables support for the Quad SPI controller in master mode.
+ Up to four flash chips can be connected on two buses with two
+ chipselects each.
+ This controller does not support generic SPI messages. It only
+ supports the high-level SPI memory interface.
+
config DRIVER_SPI_GPIO
bool "GPIO SPI Master driver"
depends on GPIOLIB
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 2329cbfb8d..dd8a8cb8b0 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -1,6 +1,8 @@
obj-$(CONFIG_SPI) += spi.o
+obj-$(CONFIG_SPI_MEM) += spi-mem.o
obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o
obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o
+obj-$(CONFIG_DRIVER_SPI_FSL_QUADSPI) += spi-fsl-qspi.o
obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
new file mode 100644
index 0000000000..e22c3099fe
--- /dev/null
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -0,0 +1,869 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Freescale QuadSPI driver.
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2018 Bootlin
+ * Copyright (C) 2018 exceet electronics GmbH
+ * Copyright (C) 2018 Kontron Electronics GmbH
+ *
+ * Transition to SPI MEM interface:
+ * Authors:
+ * Boris Brezillon <bbrezillon@kernel.org>
+ * Frieder Schrempf <frieder.schrempf@kontron.de>
+ * Yogesh Gaur <yogeshnarayan.gaur@nxp.com>
+ * Suresh Gupta <suresh.gupta@nxp.com>
+ *
+ * Based on the original fsl-quadspi.c spi-nor driver:
+ * Author: Freescale Semiconductor, Inc.
+ *
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/mutex.h>
+#include <linux/sizes.h>
+#include <of.h>
+#include <of_device.h>
+
+#include <spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+/*
+ * The driver only uses one single LUT entry, that is updated on
+ * each call of exec_op(). Index 0 is preset at boot with a basic
+ * read operation, so let's use the last entry (15).
+ */
+#define SEQID_LUT 15
+
+/* Registers used by the driver */
+#define QUADSPI_MCR 0x00
+#define QUADSPI_MCR_RESERVED_MASK GENMASK(19, 16)
+#define QUADSPI_MCR_MDIS_MASK BIT(14)
+#define QUADSPI_MCR_CLR_TXF_MASK BIT(11)
+#define QUADSPI_MCR_CLR_RXF_MASK BIT(10)
+#define QUADSPI_MCR_DDR_EN_MASK BIT(7)
+#define QUADSPI_MCR_END_CFG_MASK GENMASK(3, 2)
+#define QUADSPI_MCR_SWRSTHD_MASK BIT(1)
+#define QUADSPI_MCR_SWRSTSD_MASK BIT(0)
+
+#define QUADSPI_IPCR 0x08
+#define QUADSPI_IPCR_SEQID(x) ((x) << 24)
+
+#define QUADSPI_BUF3CR 0x1c
+#define QUADSPI_BUF3CR_ALLMST_MASK BIT(31)
+#define QUADSPI_BUF3CR_ADATSZ(x) ((x) << 8)
+#define QUADSPI_BUF3CR_ADATSZ_MASK GENMASK(15, 8)
+
+#define QUADSPI_BFGENCR 0x20
+#define QUADSPI_BFGENCR_SEQID(x) ((x) << 12)
+
+#define QUADSPI_BUF0IND 0x30
+#define QUADSPI_BUF1IND 0x34
+#define QUADSPI_BUF2IND 0x38
+#define QUADSPI_SFAR 0x100
+
+#define QUADSPI_SMPR 0x108
+#define QUADSPI_SMPR_DDRSMP_MASK GENMASK(18, 16)
+#define QUADSPI_SMPR_FSDLY_MASK BIT(6)
+#define QUADSPI_SMPR_FSPHS_MASK BIT(5)
+#define QUADSPI_SMPR_HSENA_MASK BIT(0)
+
+#define QUADSPI_RBCT 0x110
+#define QUADSPI_RBCT_WMRK_MASK GENMASK(4, 0)
+#define QUADSPI_RBCT_RXBRD_USEIPS BIT(8)
+
+#define QUADSPI_TBSR 0x150
+#define QUADSPI_TBDR 0x154
+
+#define QUADSPI_SR 0x15c
+#define QUADSPI_SR_BUSY_MASK BIT(0)
+#define QUADSPI_SR_IP_ACC_MASK BIT(1)
+#define QUADSPI_SR_AHB_ACC_MASK BIT(2)
+
+#define QUADSPI_FR 0x160
+#define QUADSPI_FR_TFF_MASK BIT(0)
+
+#define QUADSPI_SPTRCLR 0x16c
+#define QUADSPI_SPTRCLR_IPPTRC BIT(8)
+#define QUADSPI_SPTRCLR_BFPTRC BIT(0)
+
+#define QUADSPI_SFA1AD 0x180
+#define QUADSPI_SFA2AD 0x184
+#define QUADSPI_SFB1AD 0x188
+#define QUADSPI_SFB2AD 0x18c
+#define QUADSPI_RBDR(x) (0x200 + ((x) * 4))
+
+#define QUADSPI_LUTKEY 0x300
+#define QUADSPI_LUTKEY_VALUE 0x5AF05AF0
+
+#define QUADSPI_LCKCR 0x304
+#define QUADSPI_LCKER_LOCK BIT(0)
+#define QUADSPI_LCKER_UNLOCK BIT(1)
+
+#define QUADSPI_RSER 0x164
+#define QUADSPI_RSER_TFIE BIT(0)
+
+#define QUADSPI_LUT_BASE 0x310
+#define QUADSPI_LUT_OFFSET (SEQID_LUT * 4 * 4)
+#define QUADSPI_LUT_REG(idx) \
+ (QUADSPI_LUT_BASE + QUADSPI_LUT_OFFSET + (idx) * 4)
+
+/* Instruction set for the LUT register */
+#define LUT_STOP 0
+#define LUT_CMD 1
+#define LUT_ADDR 2
+#define LUT_DUMMY 3
+#define LUT_MODE 4
+#define LUT_MODE2 5
+#define LUT_MODE4 6
+#define LUT_FSL_READ 7
+#define LUT_FSL_WRITE 8
+#define LUT_JMP_ON_CS 9
+#define LUT_ADDR_DDR 10
+#define LUT_MODE_DDR 11
+#define LUT_MODE2_DDR 12
+#define LUT_MODE4_DDR 13
+#define LUT_FSL_READ_DDR 14
+#define LUT_FSL_WRITE_DDR 15
+#define LUT_DATA_LEARN 16
+
+/*
+ * The PAD definitions for LUT register.
+ *
+ * The pad stands for the number of IO lines [0:3].
+ * For example, the quad read needs four IO lines,
+ * so you should use LUT_PAD(4).
+ */
+#define LUT_PAD(x) (fls(x) - 1)
+
+/*
+ * Macro for constructing the LUT entries with the following
+ * register layout:
+ *
+ * ---------------------------------------------------
+ * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
+ * ---------------------------------------------------
+ */
+#define LUT_DEF(idx, ins, pad, opr) \
+ ((((ins) << 10) | ((pad) << 8) | (opr)) << (((idx) % 2) * 16))
+
+/* Controller needs driver to swap endianness */
+#define QUADSPI_QUIRK_SWAP_ENDIAN BIT(0)
+
+/* Controller needs 4x internal clock */
+#define QUADSPI_QUIRK_4X_INT_CLK BIT(1)
+
+/*
+ * TKT253890, the controller needs the driver to fill the txfifo with
+ * 16 bytes at least to trigger a data transfer, even though the extra
+ * data won't be transferred.
+ */
+#define QUADSPI_QUIRK_TKT253890 BIT(2)
+
+/* TKT245618, the controller cannot wake up from wait mode */
+#define QUADSPI_QUIRK_TKT245618 BIT(3)
+
+/*
+ * Controller adds QSPI_AMBA_BASE (base address of the mapped memory)
+ * internally. No need to add it when setting SFXXAD and SFAR registers
+ */
+#define QUADSPI_QUIRK_BASE_INTERNAL BIT(4)
+
+struct fsl_qspi_devtype_data {
+ unsigned int rxfifo;
+ unsigned int txfifo;
+ unsigned int ahb_buf_size;
+ unsigned int quirks;
+ bool little_endian;
+};
+
+static const struct fsl_qspi_devtype_data vybrid_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_64,
+ .ahb_buf_size = SZ_1K,
+ .quirks = QUADSPI_QUIRK_SWAP_ENDIAN,
+ .little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx6sx_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_512,
+ .ahb_buf_size = SZ_1K,
+ .quirks = QUADSPI_QUIRK_4X_INT_CLK | QUADSPI_QUIRK_TKT245618,
+ .little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx7d_data = {
+ .rxfifo = SZ_512,
+ .txfifo = SZ_512,
+ .ahb_buf_size = SZ_1K,
+ .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_4X_INT_CLK,
+ .little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx6ul_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_512,
+ .ahb_buf_size = SZ_1K,
+ .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_4X_INT_CLK,
+ .little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data ls1021a_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_64,
+ .ahb_buf_size = SZ_1K,
+ .quirks = 0,
+ .little_endian = false,
+};
+
+static const struct fsl_qspi_devtype_data ls2080a_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_64,
+ .ahb_buf_size = SZ_1K,
+ .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_BASE_INTERNAL,
+ .little_endian = true,
+};
+
+struct fsl_qspi {
+ void __iomem *iobase;
+ void __iomem *ahb_addr;
+ u32 memmap_phy;
+ struct clk *clk, *clk_en;
+ struct device_d *dev;
+ struct spi_controller ctlr;
+ const struct fsl_qspi_devtype_data *devtype_data;
+ struct mutex lock;
+ int selected;
+};
+
+static inline int needs_swap_endian(struct fsl_qspi *q)
+{
+ return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN;
+}
+
+static inline int needs_4x_clock(struct fsl_qspi *q)
+{
+ return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK;
+}
+
+static inline int needs_fill_txfifo(struct fsl_qspi *q)
+{
+ return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890;
+}
+
+static inline int needs_amba_base_offset(struct fsl_qspi *q)
+{
+ return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL);
+}
+
+/*
+ * An IC bug makes it necessary to rearrange the 32-bit data.
+ * Later chips, such as IMX6SLX, have fixed this bug.
+ */
+static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
+{
+ return needs_swap_endian(q) ? __swab32(a) : a;
+}
+
+/*
+ * R/W functions for big- or little-endian registers:
+ * The QSPI controller's endianness is independent of
+ * the CPU core's endianness. So far, although the CPU
+ * core is little-endian the QSPI controller can use
+ * big-endian or little-endian.
+ */
+static void qspi_writel(struct fsl_qspi *q, u32 val, void __iomem *addr)
+{
+ if (q->devtype_data->little_endian)
+ iowrite32(val, addr);
+ else
+ iowrite32be(val, addr);
+}
+
+static u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr)
+{
+ if (q->devtype_data->little_endian)
+ return ioread32(addr);
+
+ return ioread32be(addr);
+}
+
+static int fsl_qspi_check_buswidth(struct fsl_qspi *q, u8 width)
+{
+ switch (width) {
+ case 1:
+ case 2:
+ case 4:
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static bool fsl_qspi_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+ int ret;
+
+ ret = fsl_qspi_check_buswidth(q, op->cmd.buswidth);
+
+ if (op->addr.nbytes)
+ ret |= fsl_qspi_check_buswidth(q, op->addr.buswidth);
+
+ if (op->dummy.nbytes)
+ ret |= fsl_qspi_check_buswidth(q, op->dummy.buswidth);
+
+ if (op->data.nbytes)
+ ret |= fsl_qspi_check_buswidth(q, op->data.buswidth);
+
+ if (ret)
+ return false;
+
+ /*
+ * The number of instructions needed for the op, needs
+ * to fit into a single LUT entry.
+ */
+ if (op->addr.nbytes +
+ (op->dummy.nbytes ? 1:0) +
+ (op->data.nbytes ? 1:0) > 6)
+ return false;
+
+ /* Max 64 dummy clock cycles supported */
+ if (op->dummy.nbytes &&
+ (op->dummy.nbytes * 8 / op->dummy.buswidth > 64))
+ return false;
+
+ /* Max data length, check controller limits and alignment */
+ if (op->data.dir == SPI_MEM_DATA_IN &&
+ (op->data.nbytes > q->devtype_data->ahb_buf_size ||
+ (op->data.nbytes > q->devtype_data->rxfifo - 4 &&
+ !IS_ALIGNED(op->data.nbytes, 8))))
+ return false;
+
+ if (op->data.dir == SPI_MEM_DATA_OUT &&
+ op->data.nbytes > q->devtype_data->txfifo)
+ return false;
+
+ return true;
+}
+
+static void fsl_qspi_prepare_lut(struct fsl_qspi *q,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = q->iobase;
+ u32 lutval[4] = {};
+ int lutidx = 1, i;
+
+ lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode);
+
+ /*
+ * For some unknown reason, using LUT_ADDR doesn't work in some
+ * cases (at least with only one byte long addresses), so
+ * let's use LUT_MODE to write the address bytes one by one
+ */
+ for (i = 0; i < op->addr.nbytes; i++) {
+ u8 addrbyte = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
+
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_MODE,
+ LUT_PAD(op->addr.buswidth),
+ addrbyte);
+ lutidx++;
+ }
+
+ if (op->dummy.nbytes) {
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
+ LUT_PAD(op->dummy.buswidth),
+ op->dummy.nbytes * 8 /
+ op->dummy.buswidth);
+ lutidx++;
+ }
+
+ if (op->data.nbytes) {
+ lutval[lutidx / 2] |= LUT_DEF(lutidx,
+ op->data.dir == SPI_MEM_DATA_IN ?
+ LUT_FSL_READ : LUT_FSL_WRITE,
+ LUT_PAD(op->data.buswidth),
+ 0);
+ lutidx++;
+ }
+
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_STOP, 0, 0);
+
+ /* unlock LUT */
+ qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+ qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
+
+ /* fill LUT */
+ for (i = 0; i < ARRAY_SIZE(lutval); i++)
+ qspi_writel(q, lutval[i], base + QUADSPI_LUT_REG(i));
+
+ /* lock LUT */
+ qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+ qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
+{
+ int ret;
+
+ ret = clk_enable(q->clk_en);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(q->clk);
+ if (ret) {
+ clk_disable(q->clk_en);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q)
+{
+ clk_disable(q->clk);
+ clk_disable(q->clk_en);
+}
+
+/*
+ * If we have changed the content of the flash by writing or erasing, or if we
+ * read from flash with a different offset into the page buffer, we need to
+ * invalidate the AHB buffer. If we do not do so, we may read out the wrong
+ * data. The spec tells us reset the AHB domain and Serial Flash domain at
+ * the same time.
+ */
+static void fsl_qspi_invalidate(struct fsl_qspi *q)
+{
+ u32 reg;
+
+ reg = qspi_readl(q, q->iobase + QUADSPI_MCR);
+ reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+
+ /*
+ * The minimum delay : 1 AHB + 2 SFCK clocks.
+ * Delay 1 us is enough.
+ */
+ udelay(1);
+
+ reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+}
+
+static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
+{
+ unsigned long rate = spi->max_speed_hz;
+ int ret;
+
+ if (q->selected == spi->chip_select)
+ return;
+
+ if (needs_4x_clock(q))
+ rate *= 4;
+
+ fsl_qspi_clk_disable_unprep(q);
+
+ ret = clk_set_rate(q->clk, rate);
+ if (ret)
+ return;
+
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return;
+
+ q->selected = spi->chip_select;
+
+ fsl_qspi_invalidate(q);
+}
+
+static void fsl_qspi_read_ahb(struct fsl_qspi *q, const struct spi_mem_op *op)
+{
+ memcpy(op->data.buf.in,
+ q->ahb_addr + q->selected * q->devtype_data->ahb_buf_size,
+ op->data.nbytes);
+}
+
+static void fsl_qspi_fill_txfifo(struct fsl_qspi *q,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = q->iobase;
+ int i;
+ u32 val;
+
+ for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) {
+ memcpy(&val, op->data.buf.out + i, 4);
+ val = fsl_qspi_endian_xchg(q, val);
+ qspi_writel(q, val, base + QUADSPI_TBDR);
+ }
+
+ if (i < op->data.nbytes) {
+ memcpy(&val, op->data.buf.out + i, op->data.nbytes - i);
+ val = fsl_qspi_endian_xchg(q, val);
+ qspi_writel(q, val, base + QUADSPI_TBDR);
+ }
+
+ if (needs_fill_txfifo(q)) {
+ for (i = op->data.nbytes; i < 16; i += 4)
+ qspi_writel(q, 0, base + QUADSPI_TBDR);
+ }
+}
+
+static void fsl_qspi_read_rxfifo(struct fsl_qspi *q,
+ const struct spi_mem_op *op)
+{
+ void __iomem *base = q->iobase;
+ int i;
+ u8 *buf = op->data.buf.in;
+ u32 val;
+
+ for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) {
+ val = qspi_readl(q, base + QUADSPI_RBDR(i / 4));
+ val = fsl_qspi_endian_xchg(q, val);
+ memcpy(buf + i, &val, 4);
+ }
+
+ if (i < op->data.nbytes) {
+ val = qspi_readl(q, base + QUADSPI_RBDR(i / 4));
+ val = fsl_qspi_endian_xchg(q, val);
+ memcpy(buf + i, &val, op->data.nbytes - i);
+ }
+}
+
+static int fsl_qspi_do_op(struct fsl_qspi *q, const struct spi_mem_op *op)
+{
+ void __iomem *base = q->iobase;
+ uint64_t timeout = 1000;
+ uint64_t start;
+ u32 reg;
+
+ /*
+ * Always start the sequence at the same index since we update
+ * the LUT at each exec_op() call. And also specify the DATA
+ * length, since it's has not been specified in the LUT.
+ */
+ qspi_writel(q, op->data.nbytes | QUADSPI_IPCR_SEQID(SEQID_LUT),
+ base + QUADSPI_IPCR);
+
+ start = get_time_ns();
+ do {
+ reg = qspi_readl(q, q->iobase + QUADSPI_FR);
+ if (reg & QUADSPI_FR_TFF_MASK) {
+ /* clear interrupt */
+ qspi_writel(q, reg, q->iobase + QUADSPI_FR);
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
+ fsl_qspi_read_rxfifo(q, op);
+ return 0;
+ }
+
+ } while (!is_timeout(start, timeout * MSECOND));
+
+ return -ETIMEDOUT;
+}
+
+static int fsl_qspi_readl_poll_tout(struct fsl_qspi *q, void __iomem *base,
+ u32 mask, u32 delay_us, u32 timeout_us)
+{
+ uint64_t timeout = MSEC_PER_SEC * timeout_us;
+ u32 reg;
+
+ if (!q->devtype_data->little_endian)
+ mask = (u32)cpu_to_be32(mask);
+
+ return readl_poll_timeout(base, reg, !(reg & mask), timeout);
+}
+
+static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+ void __iomem *base;
+ u32 addr_offset = 0;
+ int err = 0;
+
+ base = q->iobase;
+
+ mutex_lock(&q->lock);
+
+ /* wait for the controller being ready */
+ fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
+ QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
+
+ fsl_qspi_select_mem(q, mem->spi);
+
+ if (needs_amba_base_offset(q))
+ addr_offset = q->memmap_phy;
+
+ qspi_writel(q,
+ q->selected * q->devtype_data->ahb_buf_size + addr_offset,
+ base + QUADSPI_SFAR);
+
+ qspi_writel(q, qspi_readl(q, base + QUADSPI_MCR) |
+ QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_CLR_TXF_MASK,
+ base + QUADSPI_MCR);
+
+ qspi_writel(q, QUADSPI_SPTRCLR_BFPTRC | QUADSPI_SPTRCLR_IPPTRC,
+ base + QUADSPI_SPTRCLR);
+
+ fsl_qspi_prepare_lut(q, op);
+
+ /*
+ * If we have large chunks of data, we read them through the AHB bus
+ * by accessing the mapped memory. In all other cases we use
+ * IP commands to access the flash.
+ */
+ if (op->data.nbytes > (q->devtype_data->rxfifo - 4) &&
+ op->data.dir == SPI_MEM_DATA_IN) {
+ fsl_qspi_read_ahb(q, op);
+ } else {
+ qspi_writel(q, QUADSPI_RBCT_WMRK_MASK |
+ QUADSPI_RBCT_RXBRD_USEIPS, base + QUADSPI_RBCT);
+
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
+ fsl_qspi_fill_txfifo(q, op);
+
+ err = fsl_qspi_do_op(q, op);
+ }
+
+ /* Invalidate the data in the AHB buffer. */
+ fsl_qspi_invalidate(q);
+
+ mutex_unlock(&q->lock);
+
+ return err;
+}
+
+static int fsl_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ if (op->data.nbytes > q->devtype_data->txfifo)
+ op->data.nbytes = q->devtype_data->txfifo;
+ } else {
+ if (op->data.nbytes > q->devtype_data->ahb_buf_size)
+ op->data.nbytes = q->devtype_data->ahb_buf_size;
+ else if (op->data.nbytes > (q->devtype_data->rxfifo - 4))
+ op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
+ }
+
+ return 0;
+}
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+ struct fsl_qspi *q = container_of(spi->controller, struct fsl_qspi, ctlr);
+ void __iomem *base = q->iobase;
+ u32 reg, addr_offset = 0;
+ int ret;
+
+ /* disable and unprepare clock to avoid glitch pass to controller */
+ fsl_qspi_clk_disable_unprep(q);
+
+ /* the default frequency, we will change it later if necessary. */
+ ret = clk_set_rate(q->clk, 66000000);
+ if (ret)
+ return ret;
+
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
+ /* Reset the module */
+ qspi_writel(q, QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
+ base + QUADSPI_MCR);
+ udelay(1);
+
+ /* Disable the module */
+ qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK,
+ base + QUADSPI_MCR);
+
+ reg = qspi_readl(q, base + QUADSPI_SMPR);
+ qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK
+ | QUADSPI_SMPR_FSPHS_MASK
+ | QUADSPI_SMPR_HSENA_MASK
+ | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR);
+
+ /* We only use the buffer3 for AHB read */
+ qspi_writel(q, 0, base + QUADSPI_BUF0IND);
+ qspi_writel(q, 0, base + QUADSPI_BUF1IND);
+ qspi_writel(q, 0, base + QUADSPI_BUF2IND);
+
+ qspi_writel(q, QUADSPI_BFGENCR_SEQID(SEQID_LUT),
+ q->iobase + QUADSPI_BFGENCR);
+ qspi_writel(q, QUADSPI_RBCT_WMRK_MASK, base + QUADSPI_RBCT);
+ qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
+ QUADSPI_BUF3CR_ADATSZ(q->devtype_data->ahb_buf_size / 8),
+ base + QUADSPI_BUF3CR);
+
+ if (needs_amba_base_offset(q))
+ addr_offset = q->memmap_phy;
+
+ /*
+ * In HW there can be a maximum of four chips on two buses with
+ * two chip selects on each bus. We use four chip selects in SW
+ * to differentiate between the four chips.
+ * We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD,
+ * SFB2AD accordingly.
+ */
+ qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset,
+ base + QUADSPI_SFA1AD);
+ qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset,
+ base + QUADSPI_SFA2AD);
+ qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset,
+ base + QUADSPI_SFB1AD);
+ qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset,
+ base + QUADSPI_SFB2AD);
+
+ q->selected = -1;
+
+ /* Enable the module */
+ qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
+ base + QUADSPI_MCR);
+
+ /* clear all interrupt status */
+ qspi_writel(q, 0xffffffff, q->iobase + QUADSPI_FR);
+
+ /* enable the interrupt */
+ qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
+
+ return 0;
+}
+
+static const char *fsl_qspi_get_name(struct spi_mem *mem)
+{
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+ struct device_d *dev = &mem->spi->dev;
+ const char *name;
+
+ /*
+ * In order to keep mtdparts compatible with the old MTD driver at
+ * mtd/spi-nor/fsl-quadspi.c, we set a custom name derived from the
+ * platform_device of the controller.
+ */
+ if (of_get_available_child_count(q->dev->device_node) == 1)
+ return dev_name(q->dev);
+
+ name = basprintf("%s-%d", dev_name(q->dev), mem->spi->chip_select);
+ if (!name) {
+ dev_err(dev, "failed to get memory for custom flash name\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return name;
+}
+
+static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
+ .adjust_op_size = fsl_qspi_adjust_op_size,
+ .supports_op = fsl_qspi_supports_op,
+ .exec_op = fsl_qspi_exec_op,
+ .get_name = fsl_qspi_get_name,
+};
+
+static int fsl_qspi_probe(struct device_d *dev)
+{
+ struct spi_controller *ctlr;
+ struct resource *res;
+ struct fsl_qspi *q;
+ int ret;
+
+ q = xzalloc(sizeof(*q));
+
+ ctlr = &q->ctlr;
+
+ /* /\* ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | *\/ */
+ /* /\* SPI_TX_DUAL | SPI_TX_QUAD; *\/ */
+
+ q->dev = dev;
+ q->devtype_data = of_device_get_match_data(dev);
+ if (!q->devtype_data) {
+ ret = -ENODEV;
+ goto err_put_ctrl;
+ }
+
+ ctlr->dev = dev;
+ ctlr->bus_num = dev->id;
+ ctlr->setup = fsl_qspi_setup;
+ ctlr->num_chipselect = 4;
+ ctlr->mem_ops = &fsl_qspi_mem_ops;
+
+ spi_controller_set_devdata(ctlr, q);
+
+ /* find the resources */
+ res = dev_request_mem_resource(dev, 0);
+ q->iobase = IOMEM(res->start);
+ if (IS_ERR(q->iobase)) {
+ ret = PTR_ERR(q->iobase);
+ goto err_put_ctrl;
+ }
+
+ res = dev_request_mem_resource(dev, 1);
+ q->ahb_addr = IOMEM(res->start);
+ if (IS_ERR(q->ahb_addr)) {
+ ret = PTR_ERR(q->ahb_addr);
+ goto err_put_ctrl;
+ }
+
+ q->memmap_phy = res->start;
+
+ /* find the clocks */
+ q->clk_en = clk_get(dev, "qspi_en");
+ if (IS_ERR(q->clk_en)) {
+ ret = PTR_ERR(q->clk_en);
+ goto err_put_ctrl;
+ }
+
+ q->clk = clk_get(dev, "qspi");
+ if (IS_ERR(q->clk)) {
+ ret = PTR_ERR(q->clk);
+ goto err_put_ctrl;
+ }
+
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret) {
+ dev_err(dev, "can not enable the clock\n");
+ goto err_put_ctrl;
+ }
+
+ mutex_init(&q->lock);
+
+ ret = spi_register_controller(ctlr);
+ if (ret)
+ goto err_disable_clk;
+
+ return 0;
+
+err_disable_clk:
+ fsl_qspi_clk_disable_unprep(q);
+
+err_put_ctrl:
+ dev_err(dev, "Freescale QuadSPI probe failed\n");
+ return ret;
+}
+
+static const struct of_device_id fsl_qspi_dt_ids[] = {
+ { .compatible = "fsl,vf610-qspi", .data = &vybrid_data, },
+ { .compatible = "fsl,imx6sx-qspi", .data = &imx6sx_data, },
+ { .compatible = "fsl,imx7d-qspi", .data = &imx7d_data, },
+ { .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
+ { .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, },
+ { .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
+ { /* sentinel */ }
+};
+
+static struct driver_d fsl_qspi_driver = {
+ .name = "fsl-quadspi",
+ .probe = fsl_qspi_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_qspi_dt_ids),
+};
+device_platform_driver(fsl_qspi_driver);
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
new file mode 100644
index 0000000000..b438ed3dcc
--- /dev/null
+++ b/drivers/spi/spi-mem.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Exceet Electronics GmbH
+ * Copyright (C) 2018 Bootlin
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+#include <common.h>
+#include <module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi-mem.h>
+#include <spi/spi.h>
+
+#define SPI_MEM_MAX_BUSWIDTH 8
+
+static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
+{
+ u32 mode = mem->spi->mode;
+
+ switch (buswidth) {
+ case 1:
+ return 0;
+
+ case 2:
+ if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
+ (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+ return 0;
+
+ break;
+
+ case 4:
+ if ((tx && (mode & SPI_TX_QUAD)) ||
+ (!tx && (mode & SPI_RX_QUAD)))
+ return 0;
+
+ break;
+
+ case 8:
+ if ((tx && (mode & SPI_TX_OCTAL)) ||
+ (!tx && (mode & SPI_RX_OCTAL)))
+ return 0;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+static bool spi_mem_default_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
+ return false;
+
+ if (op->addr.nbytes &&
+ spi_check_buswidth_req(mem, op->addr.buswidth, true))
+ return false;
+
+ if (op->dummy.nbytes &&
+ spi_check_buswidth_req(mem, op->dummy.buswidth, true))
+ return false;
+
+ if (op->data.dir != SPI_MEM_NO_DATA &&
+ spi_check_buswidth_req(mem, op->data.buswidth,
+ op->data.dir == SPI_MEM_DATA_OUT))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+
+static bool spi_mem_buswidth_is_valid(u8 buswidth)
+{
+ if (hweight8(buswidth) > 1 || buswidth > SPI_MEM_MAX_BUSWIDTH)
+ return false;
+
+ return true;
+}
+
+static int spi_mem_check_op(const struct spi_mem_op *op)
+{
+ if (!op->cmd.buswidth)
+ return -EINVAL;
+
+ if ((op->addr.nbytes && !op->addr.buswidth) ||
+ (op->dummy.nbytes && !op->dummy.buswidth) ||
+ (op->data.nbytes && !op->data.buswidth))
+ return -EINVAL;
+
+ if (!spi_mem_buswidth_is_valid(op->cmd.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->addr.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->dummy.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->data.buswidth))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool spi_mem_internal_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+
+ if (ctlr->mem_ops && ctlr->mem_ops->supports_op)
+ return ctlr->mem_ops->supports_op(mem, op);
+
+ return spi_mem_default_supports_op(mem, op);
+}
+
+/**
+ * spi_mem_supports_op() - Check if a memory device and the controller it is
+ * connected to support a specific memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to check
+ *
+ * Some controllers are only supporting Single or Dual IOs, others might only
+ * support specific opcodes, or it can even be that the controller and device
+ * both support Quad IOs but the hardware prevents you from using it because
+ * only 2 IO lines are connected.
+ *
+ * This function checks whether a specific operation is supported.
+ *
+ * Return: true if @op is supported, false otherwise.
+ */
+bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ if (spi_mem_check_op(op))
+ return false;
+
+ return spi_mem_internal_supports_op(mem, op);
+}
+EXPORT_SYMBOL_GPL(spi_mem_supports_op);
+
+static int spi_mem_access_start(struct spi_mem *mem)
+{
+ return 0;
+}
+
+static void spi_mem_access_end(struct spi_mem *mem)
+{
+ return;
+}
+
+/**
+ * spi_mem_exec_op() - Execute a memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first checks that @op is supported and then tries to execute
+ * it.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
+ struct spi_controller *ctlr = mem->spi->controller;
+ struct spi_transfer xfers[4] = { };
+ struct spi_message msg;
+ u8 *tmpbuf;
+ int ret;
+
+ ret = spi_mem_check_op(op);
+ if (ret)
+ return ret;
+
+ if (!spi_mem_internal_supports_op(mem, op))
+ return -ENOTSUPP;
+
+ if (ctlr->mem_ops) {
+ ret = spi_mem_access_start(mem);
+ if (ret)
+ return ret;
+
+ ret = ctlr->mem_ops->exec_op(mem, op);
+
+ spi_mem_access_end(mem);
+
+ /*
+ * Some controllers only optimize specific paths (typically the
+ * read path) and expect the core to use the regular SPI
+ * interface in other cases.
+ */
+ if (!ret || ret != -ENOTSUPP)
+ return ret;
+ }
+
+ tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
+ op->dummy.nbytes;
+
+ /*
+ * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
+ * we're guaranteed that this buffer is DMA-able, as required by the
+ * SPI layer.
+ */
+ tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL);
+ if (!tmpbuf)
+ return -ENOMEM;
+
+ spi_message_init(&msg);
+
+ tmpbuf[0] = op->cmd.opcode;
+ xfers[xferpos].tx_buf = tmpbuf;
+ xfers[xferpos].len = sizeof(op->cmd.opcode);
+ spi_message_add_tail(&xfers[xferpos], &msg);
+ xferpos++;
+ totalxferlen++;
+
+ if (op->addr.nbytes) {
+ int i;
+
+ for (i = 0; i < op->addr.nbytes; i++)
+ tmpbuf[i + 1] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+
+ xfers[xferpos].tx_buf = tmpbuf + 1;
+ xfers[xferpos].len = op->addr.nbytes;
+ spi_message_add_tail(&xfers[xferpos], &msg);
+ xferpos++;
+ totalxferlen += op->addr.nbytes;
+ }
+
+ if (op->dummy.nbytes) {
+ memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
+ xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
+ xfers[xferpos].len = op->dummy.nbytes;
+ spi_message_add_tail(&xfers[xferpos], &msg);
+ xferpos++;
+ totalxferlen += op->dummy.nbytes;
+ }
+
+ if (op->data.nbytes) {
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ xfers[xferpos].rx_buf = op->data.buf.in;
+ else
+ xfers[xferpos].tx_buf = op->data.buf.out;
+
+ xfers[xferpos].len = op->data.nbytes;
+ spi_message_add_tail(&xfers[xferpos], &msg);
+ xferpos++;
+ totalxferlen += op->data.nbytes;
+ }
+
+ ret = spi_sync(mem->spi, &msg);
+
+ kfree(tmpbuf);
+
+ if (ret)
+ return ret;
+
+ if (msg.actual_length != totalxferlen)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_exec_op);
+
+/**
+ * spi_mem_get_name() - Return the SPI mem device name to be used by the
+ * upper layer if necessary
+ * @mem: the SPI memory
+ *
+ * This function allows SPI mem users to retrieve the SPI mem device name.
+ * It is useful if the upper layer needs to expose a custom name for
+ * compatibility reasons.
+ *
+ * Return: a string containing the name of the memory device to be used
+ * by the SPI mem user
+ */
+const char *spi_mem_get_name(struct spi_mem *mem)
+{
+ return mem->name;
+}
+EXPORT_SYMBOL_GPL(spi_mem_get_name);
+
+/**
+ * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
+ * match controller limitations
+ * @mem: the SPI memory
+ * @op: the operation to adjust
+ *
+ * Some controllers have FIFO limitations and must split a data transfer
+ * operation into multiple ones, others require a specific alignment for
+ * optimized accesses. This function allows SPI mem drivers to split a single
+ * operation into multiple sub-operations when required.
+ *
+ * Return: a negative error code if the controller can't properly adjust @op,
+ * 0 otherwise. Note that @op->data.nbytes will be updated if @op
+ * can't be handled in a single step.
+ */
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+ size_t len;
+
+ len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
+
+ if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
+ return ctlr->mem_ops->adjust_op_size(mem, op);
+
+ if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
+ if (len > spi_max_transfer_size(mem->spi))
+ return -EINVAL;
+
+ op->data.nbytes = min3((size_t)op->data.nbytes,
+ spi_max_transfer_size(mem->spi),
+ spi_max_message_size(mem->spi) -
+ len);
+ if (!op->data.nbytes)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+
+static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct spi_mem_op op = desc->info.op_tmpl;
+ int ret;
+
+ op.addr.val = desc->info.offset + offs;
+ op.data.buf.in = buf;
+ op.data.nbytes = len;
+ ret = spi_mem_adjust_op_size(desc->mem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(desc->mem, &op);
+ if (ret)
+ return ret;
+
+ return op.data.nbytes;
+}
+
+static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+{
+ struct spi_mem_op op = desc->info.op_tmpl;
+ int ret;
+
+ op.addr.val = desc->info.offset + offs;
+ op.data.buf.out = buf;
+ op.data.nbytes = len;
+ ret = spi_mem_adjust_op_size(desc->mem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(desc->mem, &op);
+ if (ret)
+ return ret;
+
+ return op.data.nbytes;
+}
+
+/**
+ * spi_mem_dirmap_create() - Create a direct mapping descriptor
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * This function is creating a direct mapping descriptor which can then be used
+ * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
+ * If the SPI controller driver does not support direct mapping, this function
+ * fallback to an implementation using spi_mem_exec_op(), so that the caller
+ * doesn't have to bother implementing a fallback on his own.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_mem *mem,
+ const struct spi_mem_dirmap_info *info)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+ struct spi_mem_dirmap_desc *desc;
+ int ret = -ENOTSUPP;
+
+ /* Make sure the number of address cycles is between 1 and 8 bytes. */
+ if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+ return ERR_PTR(-EINVAL);
+
+ /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
+ if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+ return ERR_PTR(-EINVAL);
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return ERR_PTR(-ENOMEM);
+
+ desc->mem = mem;
+ desc->info = *info;
+ if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
+ ret = ctlr->mem_ops->dirmap_create(desc);
+
+ if (ret) {
+ desc->nodirmap = true;
+ if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
+ ret = -ENOTSUPP;
+ else
+ ret = 0;
+ }
+
+ if (ret) {
+ kfree(desc);
+ return ERR_PTR(ret);
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
+
+/**
+ * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
+ * @desc: the direct mapping descriptor to destroy
+ * @info: direct mapping information
+ *
+ * This function destroys a direct mapping descriptor previously created by
+ * spi_mem_dirmap_create().
+ */
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+ struct spi_controller *ctlr = desc->mem->spi->controller;
+
+ if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy)
+ ctlr->mem_ops->dirmap_destroy(desc);
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
+
+/**
+ * spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start reading from. Note that this is not an absolute
+ * offset, but the offset within the direct mapping which already has
+ * its own offset
+ * @len: length in bytes
+ * @buf: destination buffer. This buffer must be DMA-able
+ *
+ * This function reads data from a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data read from the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_read() again when that happens.
+ */
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct spi_controller *ctlr = desc->mem->spi->controller;
+ ssize_t ret;
+
+ if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (desc->nodirmap) {
+ ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
+ } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) {
+ ret = spi_mem_access_start(desc->mem);
+ if (ret)
+ return ret;
+
+ ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf);
+
+ spi_mem_access_end(desc->mem);
+ } else {
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
+
+/**
+ * spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start writing from. Note that this is not an absolute
+ * offset, but the offset within the direct mapping which already has
+ * its own offset
+ * @len: length in bytes
+ * @buf: source buffer. This buffer must be DMA-able
+ *
+ * This function writes data to a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data written to the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_write() again when that happens.
+ */
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+{
+ struct spi_controller *ctlr = desc->mem->spi->controller;
+ ssize_t ret;
+
+ if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (desc->nodirmap) {
+ ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
+ } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) {
+ ret = spi_mem_access_start(desc->mem);
+ if (ret)
+ return ret;
+
+ ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf);
+
+ spi_mem_access_end(desc->mem);
+ } else {
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 25bb988794..d9311d4af5 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -19,6 +19,7 @@
*/
#include <common.h>
+#include <linux/spi/spi-mem.h>
#include <spi/spi.h>
#include <xfuncs.h>
#include <malloc.h>
@@ -54,22 +55,23 @@ static LIST_HEAD(board_list);
*
* Returns the new device, or NULL.
*/
-struct spi_device *spi_new_device(struct spi_master *master,
+struct spi_device *spi_new_device(struct spi_controller *ctrl,
struct spi_board_info *chip)
{
struct spi_device *proxy;
+ struct spi_mem *mem;
int status;
/* Chipselects are numbered 0..max; validate. */
- if (chip->chip_select >= master->num_chipselect) {
+ if (chip->chip_select >= ctrl->num_chipselect) {
debug("cs%d > max %d\n",
chip->chip_select,
- master->num_chipselect);
+ ctrl->num_chipselect);
return NULL;
}
proxy = xzalloc(sizeof *proxy);
- proxy->master = master;
+ proxy->master = ctrl;
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
@@ -81,10 +83,20 @@ struct spi_device *spi_new_device(struct spi_master *master,
proxy->dev.id = DEVICE_ID_DYNAMIC;
proxy->dev.type_data = proxy;
proxy->dev.device_node = chip->device_node;
- proxy->dev.parent = master->dev;
+ proxy->dev.parent = ctrl->dev;
+ proxy->master = proxy->controller = ctrl;
+
+ mem = xzalloc(sizeof *mem);
+ mem->spi = proxy;
+
+ if (ctrl->mem_ops && ctrl->mem_ops->get_name)
+ mem->name = ctrl->mem_ops->get_name(mem);
+ else
+ mem->name = dev_name(&proxy->dev);
+ proxy->mem = mem;
/* drivers may modify this initial i/o setup */
- status = master->setup(proxy);
+ status = ctrl->setup(proxy);
if (status < 0) {
printf("can't setup %s, status %d\n",
proxy->dev.name, status);
@@ -100,12 +112,12 @@ fail:
}
EXPORT_SYMBOL(spi_new_device);
-static void spi_of_register_slaves(struct spi_master *master)
+static void spi_of_register_slaves(struct spi_controller *ctrl)
{
struct device_node *n;
struct spi_board_info chip;
struct property *reg;
- struct device_node *node = master->dev->device_node;
+ struct device_node *node = ctrl->dev->device_node;
if (!IS_ENABLED(CONFIG_OFDEVICE))
return;
@@ -116,7 +128,7 @@ static void spi_of_register_slaves(struct spi_master *master)
for_each_available_child_of_node(node, n) {
memset(&chip, 0, sizeof(chip));
chip.name = xstrdup(n->name);
- chip.bus_num = master->bus_num;
+ chip.bus_num = ctrl->bus_num;
/* Mode (clock phase/polarity/etc.) */
if (of_property_read_bool(n, "spi-cpha"))
chip.mode |= SPI_CPHA;
@@ -171,7 +183,7 @@ spi_register_board_info(struct spi_board_info const *info, int n)
return 0;
}
-static void scan_boardinfo(struct spi_master *master)
+static void scan_boardinfo(struct spi_controller *ctrl)
{
struct boardinfo *bi;
@@ -180,27 +192,47 @@ static void scan_boardinfo(struct spi_master *master)
unsigned n;
for (n = bi->n_board_info; n > 0; n--, chip++) {
- debug("%s %d %d\n", __FUNCTION__, chip->bus_num, master->bus_num);
- if (chip->bus_num != master->bus_num)
+ debug("%s %d %d\n", __FUNCTION__, chip->bus_num, ctrl->bus_num);
+ if (chip->bus_num != ctrl->bus_num)
continue;
/* NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
- (void) spi_new_device(master, chip);
+ (void) spi_new_device(ctrl, chip);
}
}
}
-static LIST_HEAD(spi_master_list);
+static LIST_HEAD(spi_controller_list);
+
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+ /*
+ * The controller may implement only the high-level SPI-memory like
+ * operations if it does not support regular SPI transfers, and this is
+ * valid use case.
+ * If ->mem_ops is NULL, we request that at least one of the
+ * ->transfer_xxx() method be implemented.
+ */
+ if (ctlr->mem_ops) {
+ if (!ctlr->mem_ops->exec_op)
+ return -EINVAL;
+ } else if (!ctlr->transfer) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
- * spi_register_master - register SPI master controller
- * @master: initialized master, originally from spi_alloc_master()
+ * spi_register_ctrl - register SPI ctrl controller
+ * @ctrl: initialized ctrl, originally from spi_alloc_ctrl()
* Context: can sleep
*
- * SPI master controllers connect to their drivers using some non-SPI bus,
+ * SPI controllers connect to their drivers using some non-SPI bus,
* such as the platform bus. The final stage of probe() in that code
- * includes calling spi_register_master() to hook up to this SPI bus glue.
+ * includes calling spi_register_ctrl() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
@@ -209,47 +241,55 @@ static LIST_HEAD(spi_master_list);
* chip is at which address.
*
* This must be called from context that can sleep. It returns zero on
- * success, else a negative error code (dropping the master's refcount).
+ * success, else a negative error code (dropping the ctrl's refcount).
* After a successful return, the caller is responsible for calling
- * spi_unregister_master().
+ * spi_unregister_ctrl().
*/
-int spi_register_master(struct spi_master *master)
+int spi_register_controller(struct spi_controller *ctrl)
{
static int dyn_bus_id = (1 << 15) - 1;
int status = -ENODEV;
- debug("%s: %s:%d\n", __func__, master->dev->name, master->dev->id);
+ debug("%s: %s:%d\n", __func__, ctrl->dev->name, ctrl->dev->id);
+
+ /*
+ * Make sure all necessary hooks are implemented before registering
+ * the SPI controller.
+ */
+ status = spi_controller_check_ops(ctrl);
+ if (status)
+ return status;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
- if (master->num_chipselect == 0)
+ if (ctrl->num_chipselect == 0)
return -EINVAL;
- if ((master->bus_num < 0) && master->dev->device_node)
- master->bus_num = of_alias_get_id(master->dev->device_node, "spi");
+ if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
+ ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
/* convention: dynamically assigned bus IDs count down from the max */
- if (master->bus_num < 0)
- master->bus_num = dyn_bus_id--;
+ if (ctrl->bus_num < 0)
+ ctrl->bus_num = dyn_bus_id--;
- list_add_tail(&master->list, &spi_master_list);
+ list_add_tail(&ctrl->list, &spi_controller_list);
- spi_of_register_slaves(master);
+ spi_of_register_slaves(ctrl);
/* populate children from any spi device tables */
- scan_boardinfo(master);
+ scan_boardinfo(ctrl);
status = 0;
return status;
}
-EXPORT_SYMBOL(spi_register_master);
+EXPORT_SYMBOL(spi_register_ctrl);
-struct spi_master *spi_get_master(int bus)
+struct spi_controller *spi_get_controller(int bus)
{
- struct spi_master* m;
+ struct spi_controller* m;
- list_for_each_entry(m, &spi_master_list, list) {
+ list_for_each_entry(m, &spi_controller_list, list) {
if (m->bus_num == bus)
return m;
}
@@ -259,7 +299,7 @@ struct spi_master *spi_get_master(int bus)
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
- return spi->master->transfer(spi, message);
+ return spi->controller->transfer(spi, message);
}
/**
diff --git a/dts/Bindings/arm/cpus.yaml b/dts/Bindings/arm/cpus.yaml
index 365dcf384d..82dd7582e9 100644
--- a/dts/Bindings/arm/cpus.yaml
+++ b/dts/Bindings/arm/cpus.yaml
@@ -228,7 +228,7 @@ patternProperties:
- renesas,r9a06g032-smp
- rockchip,rk3036-smp
- rockchip,rk3066-smp
- - socionext,milbeaut-m10v-smp
+ - socionext,milbeaut-m10v-smp
- ste,dbx500-smp
cpu-release-addr:
diff --git a/dts/Bindings/hwmon/adc128d818.txt b/dts/Bindings/hwmon/adc128d818.txt
index 08bab0e94d..d0ae46d7ba 100644
--- a/dts/Bindings/hwmon/adc128d818.txt
+++ b/dts/Bindings/hwmon/adc128d818.txt
@@ -26,7 +26,7 @@ Required node properties:
Optional node properties:
- - ti,mode: Operation mode (see above).
+ - ti,mode: Operation mode (u8) (see above).
Example (operation mode 2):
@@ -34,5 +34,5 @@ Example (operation mode 2):
adc128d818@1d {
compatible = "ti,adc128d818";
reg = <0x1d>;
- ti,mode = <2>;
+ ti,mode = /bits/ 8 <2>;
};
diff --git a/dts/Bindings/net/davinci_emac.txt b/dts/Bindings/net/davinci_emac.txt
index 24c5cdaba8..ca83dcc84f 100644
--- a/dts/Bindings/net/davinci_emac.txt
+++ b/dts/Bindings/net/davinci_emac.txt
@@ -20,6 +20,8 @@ Required properties:
Optional properties:
- phy-handle: See ethernet.txt file in the same directory.
If absent, davinci_emac driver defaults to 100/FULL.
+- nvmem-cells: phandle, reference to an nvmem node for the MAC address
+- nvmem-cell-names: string, should be "mac-address" if nvmem is to be used
- ti,davinci-rmii-en: 1 byte, 1 means use RMII
- ti,davinci-no-bd-ram: boolean, does EMAC have BD RAM?
diff --git a/dts/Bindings/net/ethernet.txt b/dts/Bindings/net/ethernet.txt
index cfc376bc97..a686215805 100644
--- a/dts/Bindings/net/ethernet.txt
+++ b/dts/Bindings/net/ethernet.txt
@@ -10,15 +10,14 @@ Documentation/devicetree/bindings/phy/phy-bindings.txt.
the boot program; should be used in cases where the MAC address assigned to
the device by the boot program is different from the "local-mac-address"
property;
-- nvmem-cells: phandle, reference to an nvmem node for the MAC address;
-- nvmem-cell-names: string, should be "mac-address" if nvmem is to be used;
- max-speed: number, specifies maximum speed in Mbit/s supported by the device;
- max-frame-size: number, maximum transfer unit (IEEE defined MTU), rather than
the maximum frame size (there's contradiction in the Devicetree
Specification).
- phy-mode: string, operation mode of the PHY interface. This is now a de-facto
standard property; supported values are:
- * "internal"
+ * "internal" (Internal means there is not a standard bus between the MAC and
+ the PHY, something proprietary is being used to embed the PHY in the MAC.)
* "mii"
* "gmii"
* "sgmii"
diff --git a/dts/Bindings/net/macb.txt b/dts/Bindings/net/macb.txt
index 174f292d8a..8b80515729 100644
--- a/dts/Bindings/net/macb.txt
+++ b/dts/Bindings/net/macb.txt
@@ -26,6 +26,10 @@ Required properties:
Optional elements: 'tsu_clk'
- clocks: Phandles to input clocks.
+Optional properties:
+- nvmem-cells: phandle, reference to an nvmem node for the MAC address
+- nvmem-cell-names: string, should be "mac-address" if nvmem is to be used
+
Optional properties for PHY child node:
- reset-gpios : Should specify the gpio for phy reset
- magic-packet : If present, indicates that the hardware supports waking
diff --git a/dts/include/dt-bindings/clock/sifive-fu540-prci.h b/dts/include/dt-bindings/clock/sifive-fu540-prci.h
new file mode 100644
index 0000000000..6a0b70a37d
--- /dev/null
+++ b/dts/include/dt-bindings/clock/sifive-fu540-prci.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+ * Wesley Terpstra
+ * Paul Walmsley
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_SIFIVE_FU540_PRCI_H
+#define __DT_BINDINGS_CLOCK_SIFIVE_FU540_PRCI_H
+
+/* Clock indexes for use by Device Tree data and the PRCI driver */
+
+#define PRCI_CLK_COREPLL 0
+#define PRCI_CLK_DDRPLL 1
+#define PRCI_CLK_GEMGXLPLL 2
+#define PRCI_CLK_TLCLK 3
+
+#endif
diff --git a/dts/include/dt-bindings/input/linux-event-codes.h b/dts/include/dt-bindings/input/linux-event-codes.h
index 7f14d4a66c..64cee11692 100644
--- a/dts/include/dt-bindings/input/linux-event-codes.h
+++ b/dts/include/dt-bindings/input/linux-event-codes.h
@@ -439,10 +439,12 @@
#define KEY_TITLE 0x171
#define KEY_SUBTITLE 0x172
#define KEY_ANGLE 0x173
-#define KEY_ZOOM 0x174
+#define KEY_FULL_SCREEN 0x174 /* AC View Toggle */
+#define KEY_ZOOM KEY_FULL_SCREEN
#define KEY_MODE 0x175
#define KEY_KEYBOARD 0x176
-#define KEY_SCREEN 0x177
+#define KEY_ASPECT_RATIO 0x177 /* HUTRR37: Aspect */
+#define KEY_SCREEN KEY_ASPECT_RATIO
#define KEY_PC 0x178 /* Media Select Computer */
#define KEY_TV 0x179 /* Media Select TV */
#define KEY_TV2 0x17a /* Media Select Cable */
diff --git a/dts/include/dt-bindings/reset/amlogic,meson-g12a-reset.h b/dts/include/dt-bindings/reset/amlogic,meson-g12a-reset.h
index 8063e8314e..6d487c5eba 100644
--- a/dts/include/dt-bindings/reset/amlogic,meson-g12a-reset.h
+++ b/dts/include/dt-bindings/reset/amlogic,meson-g12a-reset.h
@@ -51,7 +51,10 @@
#define RESET_SD_EMMC_A 44
#define RESET_SD_EMMC_B 45
#define RESET_SD_EMMC_C 46
-/* 47-60 */
+/* 47 */
+#define RESET_USB_PHY20 48
+#define RESET_USB_PHY21 49
+/* 50-60 */
#define RESET_AUDIO_CODEC 61
/* 62-63 */
/* RESET2 */
diff --git a/dts/src/arc/hsdk.dts b/dts/src/arc/hsdk.dts
index 69bc1c9e8e..7425bb0f2d 100644
--- a/dts/src/arc/hsdk.dts
+++ b/dts/src/arc/hsdk.dts
@@ -18,8 +18,8 @@
model = "snps,hsdk";
compatible = "snps,hsdk";
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
chosen {
bootargs = "earlycon=uart8250,mmio32,0xf0005000,115200n8 console=ttyS0,115200n8 debug print-fatal-signals=1";
@@ -105,7 +105,7 @@
#size-cells = <1>;
interrupt-parent = <&idu_intc>;
- ranges = <0x00000000 0xf0000000 0x10000000>;
+ ranges = <0x00000000 0x0 0xf0000000 0x10000000>;
cgu_rst: reset-controller@8a0 {
compatible = "snps,hsdk-reset";
@@ -269,9 +269,10 @@
};
memory@80000000 {
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
device_type = "memory";
- reg = <0x80000000 0x40000000>; /* 1 GiB */
+ reg = <0x0 0x80000000 0x0 0x40000000>; /* 1 GB lowmem */
+ /* 0x1 0x00000000 0x0 0x40000000>; 1 GB highmem */
};
};
diff --git a/dts/src/arm/am335x-evm.dts b/dts/src/arm/am335x-evm.dts
index dce5be5df9..edcff79879 100644
--- a/dts/src/arm/am335x-evm.dts
+++ b/dts/src/arm/am335x-evm.dts
@@ -57,6 +57,24 @@
enable-active-high;
};
+ /* TPS79501 */
+ v1_8d_reg: fixedregulator-v1_8d {
+ compatible = "regulator-fixed";
+ regulator-name = "v1_8d";
+ vin-supply = <&vbat>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ /* TPS79501 */
+ v3_3d_reg: fixedregulator-v3_3d {
+ compatible = "regulator-fixed";
+ regulator-name = "v3_3d";
+ vin-supply = <&vbat>;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
matrix_keypad: matrix_keypad0 {
compatible = "gpio-matrix-keypad";
debounce-delay-ms = <5>;
@@ -499,10 +517,10 @@
status = "okay";
/* Regulators */
- AVDD-supply = <&vaux2_reg>;
- IOVDD-supply = <&vaux2_reg>;
- DRVDD-supply = <&vaux2_reg>;
- DVDD-supply = <&vbat>;
+ AVDD-supply = <&v3_3d_reg>;
+ IOVDD-supply = <&v3_3d_reg>;
+ DRVDD-supply = <&v3_3d_reg>;
+ DVDD-supply = <&v1_8d_reg>;
};
};
diff --git a/dts/src/arm/am335x-evmsk.dts b/dts/src/arm/am335x-evmsk.dts
index b128998097..2c2d8b5b8c 100644
--- a/dts/src/arm/am335x-evmsk.dts
+++ b/dts/src/arm/am335x-evmsk.dts
@@ -73,6 +73,24 @@
enable-active-high;
};
+ /* TPS79518 */
+ v1_8d_reg: fixedregulator-v1_8d {
+ compatible = "regulator-fixed";
+ regulator-name = "v1_8d";
+ vin-supply = <&vbat>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ /* TPS78633 */
+ v3_3d_reg: fixedregulator-v3_3d {
+ compatible = "regulator-fixed";
+ regulator-name = "v3_3d";
+ vin-supply = <&vbat>;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
leds {
pinctrl-names = "default";
pinctrl-0 = <&user_leds_s0>;
@@ -501,10 +519,10 @@
status = "okay";
/* Regulators */
- AVDD-supply = <&vaux2_reg>;
- IOVDD-supply = <&vaux2_reg>;
- DRVDD-supply = <&vaux2_reg>;
- DVDD-supply = <&vbat>;
+ AVDD-supply = <&v3_3d_reg>;
+ IOVDD-supply = <&v3_3d_reg>;
+ DRVDD-supply = <&v3_3d_reg>;
+ DVDD-supply = <&v1_8d_reg>;
};
};
diff --git a/dts/src/arm/am33xx-l4.dtsi b/dts/src/arm/am33xx-l4.dtsi
index f459ec316a..ca6d9f02a8 100644
--- a/dts/src/arm/am33xx-l4.dtsi
+++ b/dts/src/arm/am33xx-l4.dtsi
@@ -1762,7 +1762,7 @@
reg = <0xcc000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */
- clocks = <&l4ls_clkctrl AM3_D_CAN0_CLKCTRL 0>;
+ clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN0_CLKCTRL 0>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <1>;
@@ -1785,7 +1785,7 @@
reg = <0xd0000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */
- clocks = <&l4ls_clkctrl AM3_D_CAN1_CLKCTRL 0>;
+ clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN1_CLKCTRL 0>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/dts/src/arm/rk3288-tinker.dtsi b/dts/src/arm/rk3288-tinker.dtsi
index aa107ee41b..ef653c3209 100644
--- a/dts/src/arm/rk3288-tinker.dtsi
+++ b/dts/src/arm/rk3288-tinker.dtsi
@@ -254,6 +254,7 @@
};
vccio_sd: LDO_REG5 {
+ regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-name = "vccio_sd";
@@ -430,7 +431,7 @@
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
- card-detect-delay = <200>;
+ broken-cd;
disable-wp; /* wp not hooked up */
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
diff --git a/dts/src/arm/rk3288-veyron.dtsi b/dts/src/arm/rk3288-veyron.dtsi
index 0bc2409f69..192dbc089a 100644
--- a/dts/src/arm/rk3288-veyron.dtsi
+++ b/dts/src/arm/rk3288-veyron.dtsi
@@ -25,8 +25,6 @@
gpio_keys: gpio-keys {
compatible = "gpio-keys";
- #address-cells = <1>;
- #size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pwr_key_l>;
diff --git a/dts/src/arm/rk3288.dtsi b/dts/src/arm/rk3288.dtsi
index ca7d52daa8..a024d1e7e7 100644
--- a/dts/src/arm/rk3288.dtsi
+++ b/dts/src/arm/rk3288.dtsi
@@ -70,7 +70,7 @@
compatible = "arm,cortex-a12";
reg = <0x501>;
resets = <&cru SRST_CORE1>;
- operating-points = <&cpu_opp_table>;
+ operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>; /* min followed by max */
clock-latency = <40000>;
clocks = <&cru ARMCLK>;
@@ -80,7 +80,7 @@
compatible = "arm,cortex-a12";
reg = <0x502>;
resets = <&cru SRST_CORE2>;
- operating-points = <&cpu_opp_table>;
+ operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>; /* min followed by max */
clock-latency = <40000>;
clocks = <&cru ARMCLK>;
@@ -90,7 +90,7 @@
compatible = "arm,cortex-a12";
reg = <0x503>;
resets = <&cru SRST_CORE3>;
- operating-points = <&cpu_opp_table>;
+ operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>; /* min followed by max */
clock-latency = <40000>;
clocks = <&cru ARMCLK>;
@@ -1119,8 +1119,6 @@
clock-names = "ref", "pclk";
power-domains = <&power RK3288_PD_VIO>;
rockchip,grf = <&grf>;
- #address-cells = <1>;
- #size-cells = <0>;
status = "disabled";
ports {
@@ -1282,27 +1280,27 @@
gpu_opp_table: gpu-opp-table {
compatible = "operating-points-v2";
- opp@100000000 {
+ opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <950000>;
};
- opp@200000000 {
+ opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <950000>;
};
- opp@300000000 {
+ opp-300000000 {
opp-hz = /bits/ 64 <300000000>;
opp-microvolt = <1000000>;
};
- opp@400000000 {
+ opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <1100000>;
};
- opp@500000000 {
+ opp-500000000 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <1200000>;
};
- opp@600000000 {
+ opp-600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <1250000>;
};
diff --git a/dts/src/arm/sama5d2-pinfunc.h b/dts/src/arm/sama5d2-pinfunc.h
index 1c01a6f843..28a2e45752 100644
--- a/dts/src/arm/sama5d2-pinfunc.h
+++ b/dts/src/arm/sama5d2-pinfunc.h
@@ -518,7 +518,7 @@
#define PIN_PC9__GPIO PINMUX_PIN(PIN_PC9, 0, 0)
#define PIN_PC9__FIQ PINMUX_PIN(PIN_PC9, 1, 3)
#define PIN_PC9__GTSUCOMP PINMUX_PIN(PIN_PC9, 2, 1)
-#define PIN_PC9__ISC_D0 PINMUX_PIN(PIN_PC9, 2, 1)
+#define PIN_PC9__ISC_D0 PINMUX_PIN(PIN_PC9, 3, 1)
#define PIN_PC9__TIOA4 PINMUX_PIN(PIN_PC9, 4, 2)
#define PIN_PC10 74
#define PIN_PC10__GPIO PINMUX_PIN(PIN_PC10, 0, 0)
diff --git a/dts/src/arm/ste-nomadik-nhk15.dts b/dts/src/arm/ste-nomadik-nhk15.dts
index f2f6558a00..04066f9cb8 100644
--- a/dts/src/arm/ste-nomadik-nhk15.dts
+++ b/dts/src/arm/ste-nomadik-nhk15.dts
@@ -213,13 +213,12 @@
gpio-sck = <&gpio0 5 GPIO_ACTIVE_HIGH>;
gpio-mosi = <&gpio0 4 GPIO_ACTIVE_HIGH>;
/*
- * This chipselect is active high. Just setting the flags
- * to GPIO_ACTIVE_HIGH is not enough for the SPI DT bindings,
- * it will be ignored, only the special "spi-cs-high" flag
- * really counts.
+ * It's not actually active high, but the frameworks assume
+ * the polarity of the passed-in GPIO is "normal" (active
+ * high) then actively drives the line low to select the
+ * chip.
*/
cs-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
- spi-cs-high;
num-chipselects = <1>;
/*
diff --git a/dts/src/arm64/altera/socfpga_stratix10.dtsi b/dts/src/arm64/altera/socfpga_stratix10.dtsi
index 7c649f6b14..cd7c76e58b 100644
--- a/dts/src/arm64/altera/socfpga_stratix10.dtsi
+++ b/dts/src/arm64/altera/socfpga_stratix10.dtsi
@@ -162,6 +162,7 @@
rx-fifo-depth = <16384>;
snps,multicast-filter-bins = <256>;
iommus = <&smmu 1>;
+ altr,sysmgr-syscon = <&sysmgr 0x44 0>;
status = "disabled";
};
@@ -179,6 +180,7 @@
rx-fifo-depth = <16384>;
snps,multicast-filter-bins = <256>;
iommus = <&smmu 2>;
+ altr,sysmgr-syscon = <&sysmgr 0x48 0>;
status = "disabled";
};
@@ -196,6 +198,7 @@
rx-fifo-depth = <16384>;
snps,multicast-filter-bins = <256>;
iommus = <&smmu 3>;
+ altr,sysmgr-syscon = <&sysmgr 0x4c 0>;
status = "disabled";
};
diff --git a/dts/src/arm64/rockchip/rk3328-roc-cc.dts b/dts/src/arm64/rockchip/rk3328-roc-cc.dts
index 33c44e8572..0e34354b20 100644
--- a/dts/src/arm64/rockchip/rk3328-roc-cc.dts
+++ b/dts/src/arm64/rockchip/rk3328-roc-cc.dts
@@ -108,8 +108,8 @@
snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
snps,reset-delays-us = <0 10000 50000>;
- tx_delay = <0x25>;
- rx_delay = <0x11>;
+ tx_delay = <0x24>;
+ rx_delay = <0x18>;
status = "okay";
};
diff --git a/dts/src/arm64/rockchip/rk3328-rock64.dts b/dts/src/arm64/rockchip/rk3328-rock64.dts
index 2157a52827..79b4d1d4b5 100644
--- a/dts/src/arm64/rockchip/rk3328-rock64.dts
+++ b/dts/src/arm64/rockchip/rk3328-rock64.dts
@@ -46,8 +46,7 @@
vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator {
compatible = "regulator-fixed";
- enable-active-high;
- gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>;
+ gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&usb20_host_drv>;
regulator-name = "vcc_host1_5v";
diff --git a/dts/src/arm64/rockchip/rk3328.dtsi b/dts/src/arm64/rockchip/rk3328.dtsi
index 84f14b132e..dabef1a216 100644
--- a/dts/src/arm64/rockchip/rk3328.dtsi
+++ b/dts/src/arm64/rockchip/rk3328.dtsi
@@ -1445,11 +1445,11 @@
sdmmc0 {
sdmmc0_clk: sdmmc0-clk {
- rockchip,pins = <1 RK_PA6 1 &pcfg_pull_none_4ma>;
+ rockchip,pins = <1 RK_PA6 1 &pcfg_pull_none_8ma>;
};
sdmmc0_cmd: sdmmc0-cmd {
- rockchip,pins = <1 RK_PA4 1 &pcfg_pull_up_4ma>;
+ rockchip,pins = <1 RK_PA4 1 &pcfg_pull_up_8ma>;
};
sdmmc0_dectn: sdmmc0-dectn {
@@ -1461,14 +1461,14 @@
};
sdmmc0_bus1: sdmmc0-bus1 {
- rockchip,pins = <1 RK_PA0 1 &pcfg_pull_up_4ma>;
+ rockchip,pins = <1 RK_PA0 1 &pcfg_pull_up_8ma>;
};
sdmmc0_bus4: sdmmc0-bus4 {
- rockchip,pins = <1 RK_PA0 1 &pcfg_pull_up_4ma>,
- <1 RK_PA1 1 &pcfg_pull_up_4ma>,
- <1 RK_PA2 1 &pcfg_pull_up_4ma>,
- <1 RK_PA3 1 &pcfg_pull_up_4ma>;
+ rockchip,pins = <1 RK_PA0 1 &pcfg_pull_up_8ma>,
+ <1 RK_PA1 1 &pcfg_pull_up_8ma>,
+ <1 RK_PA2 1 &pcfg_pull_up_8ma>,
+ <1 RK_PA3 1 &pcfg_pull_up_8ma>;
};
sdmmc0_gpio: sdmmc0-gpio {
@@ -1642,50 +1642,50 @@
rgmiim1_pins: rgmiim1-pins {
rockchip,pins =
/* mac_txclk */
- <1 RK_PB4 2 &pcfg_pull_none_12ma>,
+ <1 RK_PB4 2 &pcfg_pull_none_8ma>,
/* mac_rxclk */
- <1 RK_PB5 2 &pcfg_pull_none_2ma>,
+ <1 RK_PB5 2 &pcfg_pull_none_4ma>,
/* mac_mdio */
- <1 RK_PC3 2 &pcfg_pull_none_2ma>,
+ <1 RK_PC3 2 &pcfg_pull_none_4ma>,
/* mac_txen */
- <1 RK_PD1 2 &pcfg_pull_none_12ma>,
+ <1 RK_PD1 2 &pcfg_pull_none_8ma>,
/* mac_clk */
- <1 RK_PC5 2 &pcfg_pull_none_2ma>,
+ <1 RK_PC5 2 &pcfg_pull_none_4ma>,
/* mac_rxdv */
- <1 RK_PC6 2 &pcfg_pull_none_2ma>,
+ <1 RK_PC6 2 &pcfg_pull_none_4ma>,
/* mac_mdc */
- <1 RK_PC7 2 &pcfg_pull_none_2ma>,
+ <1 RK_PC7 2 &pcfg_pull_none_4ma>,
/* mac_rxd1 */
- <1 RK_PB2 2 &pcfg_pull_none_2ma>,
+ <1 RK_PB2 2 &pcfg_pull_none_4ma>,
/* mac_rxd0 */
- <1 RK_PB3 2 &pcfg_pull_none_2ma>,
+ <1 RK_PB3 2 &pcfg_pull_none_4ma>,
/* mac_txd1 */
- <1 RK_PB0 2 &pcfg_pull_none_12ma>,
+ <1 RK_PB0 2 &pcfg_pull_none_8ma>,
/* mac_txd0 */
- <1 RK_PB1 2 &pcfg_pull_none_12ma>,
+ <1 RK_PB1 2 &pcfg_pull_none_8ma>,
/* mac_rxd3 */
- <1 RK_PB6 2 &pcfg_pull_none_2ma>,
+ <1 RK_PB6 2 &pcfg_pull_none_4ma>,
/* mac_rxd2 */
- <1 RK_PB7 2 &pcfg_pull_none_2ma>,
+ <1 RK_PB7 2 &pcfg_pull_none_4ma>,
/* mac_txd3 */
- <1 RK_PC0 2 &pcfg_pull_none_12ma>,
+ <1 RK_PC0 2 &pcfg_pull_none_8ma>,
/* mac_txd2 */
- <1 RK_PC1 2 &pcfg_pull_none_12ma>,
+ <1 RK_PC1 2 &pcfg_pull_none_8ma>,
/* mac_txclk */
- <0 RK_PB0 1 &pcfg_pull_none>,
+ <0 RK_PB0 1 &pcfg_pull_none_8ma>,
/* mac_txen */
- <0 RK_PB4 1 &pcfg_pull_none>,
+ <0 RK_PB4 1 &pcfg_pull_none_8ma>,
/* mac_clk */
- <0 RK_PD0 1 &pcfg_pull_none>,
+ <0 RK_PD0 1 &pcfg_pull_none_4ma>,
/* mac_txd1 */
- <0 RK_PC0 1 &pcfg_pull_none>,
+ <0 RK_PC0 1 &pcfg_pull_none_8ma>,
/* mac_txd0 */
- <0 RK_PC1 1 &pcfg_pull_none>,
+ <0 RK_PC1 1 &pcfg_pull_none_8ma>,
/* mac_txd3 */
- <0 RK_PC7 1 &pcfg_pull_none>,
+ <0 RK_PC7 1 &pcfg_pull_none_8ma>,
/* mac_txd2 */
- <0 RK_PC6 1 &pcfg_pull_none>;
+ <0 RK_PC6 1 &pcfg_pull_none_8ma>;
};
rmiim1_pins: rmiim1-pins {
diff --git a/dts/src/arm64/rockchip/rk3399-rock-pi-4.dts b/dts/src/arm64/rockchip/rk3399-rock-pi-4.dts
index 4a543f2117..844eac939a 100644
--- a/dts/src/arm64/rockchip/rk3399-rock-pi-4.dts
+++ b/dts/src/arm64/rockchip/rk3399-rock-pi-4.dts
@@ -158,6 +158,7 @@
};
&hdmi {
+ ddc-i2c-bus = <&i2c3>;
pinctrl-names = "default";
pinctrl-0 = <&hdmi_cec>;
status = "okay";
diff --git a/fs/nfs.c b/fs/nfs.c
index 574fb85fb6..d606ccd1e9 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -436,9 +436,20 @@ static struct packet *rpc_req(struct nfs_priv *npriv, int rpc_prog,
npriv->con->udp->uh_dport = hton16(dport);
+ nfs_timer_start = get_time_ns();
+
again:
ret = net_udp_send(npriv->con,
sizeof(pkt) + datalen * sizeof(uint32_t));
+ if (ret) {
+ if (is_timeout(nfs_timer_start, NFS_TIMEOUT)) {
+ tries++;
+ if (tries == NFS_MAX_RESEND)
+ return ERR_PTR(-ETIMEDOUT);
+ }
+
+ goto again;
+ }
nfs_timer_start = get_time_ns();
diff --git a/images/Makefile.layerscape b/images/Makefile.layerscape
index 47df3777f0..d20cc6a37e 100644
--- a/images/Makefile.layerscape
+++ b/images/Makefile.layerscape
@@ -14,6 +14,12 @@ quiet_cmd_lspbl_image = LSPBL-IMG $@
$(objtree)/scripts/pblimage -o $@ -r $(lspbl-rcw-tmp) \
-m $($(patsubst $(obj)/%.pblb,PBL_CODE_SIZE_%,$<)) -p $(lspbl-pbi-tmp) -i $<
+quiet_cmd_lspbl_spi_image = LSPBL-SPI-IMG $@
+ cmd_lspbl_spi_image = $(CPP) $(lspbl_cfg_cpp_flags) -o $(lspbl-rcw-tmp) $(word 2,$^) ; \
+ $(CPP) $(lspbl_cfg_cpp_flags) -o $(lspbl-pbi-tmp) $(word 3,$^) ; \
+ $(objtree)/scripts/pblimage -o $@ -r $(lspbl-rcw-tmp) -s \
+ -m $($(patsubst $(obj)/%.pblb,PBL_CODE_SIZE_%,$<)) -p $(lspbl-pbi-tmp) -i $<
+
pbl-$(CONFIG_MACH_LS1046ARDB) += start_ls1046ardb.pbl
$(obj)/barebox-ls1046ardb-2nd.image: $(obj)/start_ls1046ardb.pblb
$(call if_changed,shipped)
@@ -31,7 +37,7 @@ $(obj)/barebox-ls1046ardb-emmc.image: $(obj)/start_ls1046ardb.pblb \
$(obj)/barebox-ls1046ardb-qspi.image: $(obj)/start_ls1046ardb.pblb \
$(board)/ls1046ardb/ls1046ardb_rcw_qspi.cfg \
$(board)/ls1046ardb/ls1046ardb_pbi.cfg
- $(call if_changed,lspbl_image)
+ $(call if_changed,lspbl_spi_image)
image-$(CONFIG_MACH_LS1046ARDB) += barebox-ls1046ardb-sd.image barebox-ls1046ardb-qspi.image \
barebox-ls1046ardb-emmc.image barebox-ls1046ardb-2nd.image
@@ -42,18 +48,13 @@ $(obj)/barebox-tqmls1046a-2nd.image: $(obj)/start_tqmls1046a.pblb
$(obj)/barebox-tqmls1046a-sd.image: $(obj)/start_tqmls1046a.pblb \
$(board)/tqmls1046a/tqmls1046a_rcw_sd_3333_5559.cfg \
- $(board)/tqmls1046a/tqmls1046a_pbi_sd.cfg
- $(call if_changed,lspbl_image)
-
-$(obj)/barebox-tqmls1046a-emmc.image: $(obj)/start_tqmls1046a.pblb \
- $(board)/tqmls1046a/tqmls1046a_rcw_emmc_3333_5559.cfg \
- $(board)/tqmls1046a/tqmls1046a_pbi_sd.cfg
+ $(board)/tqmls1046a/tqmls1046a_pbi.cfg
$(call if_changed,lspbl_image)
$(obj)/barebox-tqmls1046a-qspi.image: $(obj)/start_tqmls1046a.pblb \
$(board)/tqmls1046a/tqmls1046a_rcw_qspi_3333_5559.cfg \
- $(board)/tqmls1046a/tqmls1046a_pbi_qspi.cfg
- $(call if_changed,lspbl_image)
+ $(board)/tqmls1046a/tqmls1046a_pbi.cfg
+ $(call if_changed,lspbl_spi_image)
-image-$(CONFIG_MACH_TQMLS1046A) += barebox-tqmls1046a-sd.image barebox-tqmls1046a-emmc.image \
+image-$(CONFIG_MACH_TQMLS1046A) += barebox-tqmls1046a-sd.image \
barebox-tqmls1046a-qspi.image barebox-tqmls1046a-2nd.image
diff --git a/images/Makefile.mvebu b/images/Makefile.mvebu
index 8d6a5bd0bb..846dd46095 100644
--- a/images/Makefile.mvebu
+++ b/images/Makefile.mvebu
@@ -43,6 +43,14 @@ pblb-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += start_marvell_armada_xp_gp
image-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += barebox-marvell-armada-xp-gp.img
image-$(CONFIG_MACH_MARVELL_ARMADA_XP_GP) += barebox-marvell-armada-xp-gp-2nd.img
+MARVELL_ARMADA_XP_DB_KWBOPTS = ${KWBOPTS} -i $(board)/marvell-armada-xp-db/kwbimage.cfg
+OPTS_start_marvell_armada_xp_db.pblb.kwbimg = $(MARVELL_ARMADA_XP_DB_KWBOPTS)
+FILE_barebox-marvell-armada-xp-db.img = start_marvell_armada_xp_db.pblb.kwbimg
+FILE_barebox-marvell-armada-xp-db-2nd.img = start_marvell_armada_xp_db.pblb
+pblb-$(CONFIG_MACH_MARVELL_ARMADA_XP_DB) += start_marvell_armada_xp_db
+image-$(CONFIG_MACH_MARVELL_ARMADA_XP_DB) += barebox-marvell-armada-xp-db.img
+image-$(CONFIG_MACH_MARVELL_ARMADA_XP_DB) += barebox-marvell-armada-xp-db-2nd.img
+
NETGEAR_RN2120_KWBOPTS = ${KWBOPTS} -i $(board)/netgear-rn2120/kwbimage.cfg
OPTS_start_netgear_rn2120.pblb.kwbimg = $(NETGEAR_RN2120_KWBOPTS)
FILE_barebox-netgear-rn2120.img = start_netgear_rn2120.pblb.kwbimg
diff --git a/include/base64.h b/include/base64.h
new file mode 100644
index 0000000000..0df510281d
--- /dev/null
+++ b/include/base64.h
@@ -0,0 +1,9 @@
+#ifndef __BASE64_H
+#define __BASE64_H
+
+void uuencode(char *p, const char *src, int length);
+int decode_base64(char *dst, int dst_len, const char *src);
+
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+
+#endif /* __BASE64_H */
diff --git a/include/bbu.h b/include/bbu.h
index 0ed355b539..9da6785d2e 100644
--- a/include/bbu.h
+++ b/include/bbu.h
@@ -54,7 +54,7 @@ int bbu_handlers_iterate(int (*fn)(struct bbu_handler *, void *), void *);
int bbu_register_handler(struct bbu_handler *);
int bbu_register_std_file_update(const char *name, unsigned long flags,
- char *devicefile, enum filetype imagetype);
+ const char *devicefile, enum filetype imagetype);
#else
@@ -64,7 +64,7 @@ static inline int bbu_register_handler(struct bbu_handler *unused)
}
static inline int bbu_register_std_file_update(const char *name, unsigned long flags,
- char *devicefile, enum filetype imagetype)
+ const char *devicefile, enum filetype imagetype)
{
return -ENOSYS;
}
diff --git a/include/blobgen.h b/include/blobgen.h
new file mode 100644
index 0000000000..09a6637b77
--- /dev/null
+++ b/include/blobgen.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __BLOBGEN_H__
+#define __BLOBGEN_H__
+
+#include <common.h>
+
+enum access_rights {
+ KERNEL,
+ KERNEL_EVM,
+ USERSPACE,
+};
+
+#define KEYMOD_LENGTH 16
+#define MAX_BLOB_LEN 4096
+#define BLOCKSIZE_BYTES 8
+
+struct blobgen {
+ struct device_d dev;
+ int (*encrypt)(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize);
+ int (*decrypt)(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize);
+
+ enum access_rights access;
+ unsigned int max_payload_size;
+
+ struct list_head list;
+};
+
+int blob_gen_register(struct device_d *dev, struct blobgen *bg);
+
+struct blobgen *blobgen_get(const char *name);
+
+int blob_encrypt(struct blobgen *blg, const char *modifier, const void *plain,
+ int plainsize, void **blob, int *blobsize);
+int blob_encrypt_to_env(struct blobgen *blg, const char *modifier,
+ const void *plain, int plainsize, const char *varname);
+int blob_decrypt(struct blobgen *bg, const char *modifier, const void *blob,
+ int blobsize, void **plain, int *plainsize);
+int blob_decrypt_from_base64(struct blobgen *blg, const char *modifier,
+ const char *encrypted, void **plain, int *plainsize);
+
+#endif
diff --git a/include/common.h b/include/common.h
index 11d26cb3db..723b9c706c 100644
--- a/include/common.h
+++ b/include/common.h
@@ -68,7 +68,9 @@ int readline (const char *prompt, char *buf, int len);
long get_ram_size (volatile long *, long);
/* common/console.c */
-int ctrlc (void);
+int ctrlc(void);
+int arch_ctrlc(void);
+void ctrlc_handled(void);
#ifdef ARCH_HAS_STACK_DUMP
void dump_stack(void);
diff --git a/include/console.h b/include/console.h
index 673921331d..4062e5abf6 100644
--- a/include/console.h
+++ b/include/console.h
@@ -207,4 +207,7 @@ static inline void pbl_set_putc(void (*putcf)(void *ctx, int c), void *ctx) {}
bool console_allow_color(void);
+void console_ctrlc_allow(void);
+void console_ctrlc_forbid(void);
+
#endif
diff --git a/include/console_countdown.h b/include/console_countdown.h
index c6c2d5c00e..88cadf11ec 100644
--- a/include/console_countdown.h
+++ b/include/console_countdown.h
@@ -7,7 +7,7 @@
#define CONSOLE_COUNTDOWN_CTRLC (1 << 4)
#define CONSOLE_COUNTDOWN_EXTERN (1 << 5)
-int console_countdown(int timeout_s, unsigned flags, char *out_key);
+int console_countdown(int timeout_s, unsigned flags, const char *keys, char *out_key);
void console_countdown_abort(void);
#endif /* __CONSOLE_COUNTDOWN_H */
diff --git a/include/crypto.h b/include/crypto.h
new file mode 100644
index 0000000000..ac70111cab
--- /dev/null
+++ b/include/crypto.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+struct ablkcipher_request {
+ unsigned int nbytes;
+
+ void __iomem *info;
+
+ void __iomem *dst;
+ void __iomem *src;
+};
+
+#endif
diff --git a/include/crypto/des.h b/include/crypto/des.h
new file mode 100644
index 0000000000..58fdaaa99d
--- /dev/null
+++ b/include/crypto/des.h
@@ -0,0 +1,16 @@
+/*
+ * DES & Triple DES EDE Cipher Algorithms.
+ */
+
+#ifndef __CRYPTO_DES_H
+#define __CRYPTO_DES_H
+
+#define DES_KEY_SIZE 8
+#define DES_EXPKEY_WORDS 32
+#define DES_BLOCK_SIZE 8
+
+#define DES3_EDE_KEY_SIZE (3 * DES_KEY_SIZE)
+#define DES3_EDE_EXPKEY_WORDS (3 * DES_EXPKEY_WORDS)
+#define DES3_EDE_BLOCK_SIZE DES_BLOCK_SIZE
+
+#endif /* __CRYPTO_DES_H */
diff --git a/include/filetype.h b/include/filetype.h
index 395053dd59..dcb331a6c9 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -45,6 +45,8 @@ enum filetype {
filetype_elf,
filetype_imx_image_v1,
filetype_imx_image_v2,
+ filetype_layerscape_image,
+ filetype_layerscape_qspi_image,
filetype_max,
};
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index de9ac08ef1..33413ff955 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -10,9 +10,26 @@
#ifndef __LINUX_MTD_SPI_NOR_H
#define __LINUX_MTD_SPI_NOR_H
+#include <linux/bitops.h>
#include <linux/mutex.h>
/*
+ * Manufacturer IDs
+ *
+ * The first byte returned from the flash after sending opcode SPINOR_OP_RDID.
+ * Sometimes these are the same as CFI IDs, but sometimes they aren't.
+ */
+#define SNOR_MFR_ATMEL CFI_MFR_ATMEL
+#define SNOR_MFR_GIGADEVICE 0xc8
+#define SNOR_MFR_INTEL CFI_MFR_INTEL
+#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro */
+#define SNOR_MFR_MICRON CFI_MFR_MICRON /* Micron */
+#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX
+#define SNOR_MFR_SPANSION CFI_MFR_AMD
+#define SNOR_MFR_SST CFI_MFR_SST
+#define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */
+
+/*
* Note on opcode nomenclature: some opcodes have a format like
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
* of I/O lines used for the opcode, address, and data (respectively). The
@@ -24,28 +41,53 @@
#define SPINOR_OP_WREN 0x06 /* Write enable */
#define SPINOR_OP_RDSR 0x05 /* Read status register */
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
+#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
+#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
+#define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
+#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
+#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
+#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
-#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
+#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
+#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
+#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
+/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
+#define SPINOR_OP_READ_1_1_1_DTR 0x0d
+#define SPINOR_OP_READ_1_2_2_DTR 0xbd
+#define SPINOR_OP_READ_1_4_4_DTR 0xed
+
+#define SPINOR_OP_READ_1_1_1_DTR_4B 0x0e
+#define SPINOR_OP_READ_1_2_2_DTR_4B 0xbe
+#define SPINOR_OP_READ_1_4_4_DTR_4B 0xee
+
/* Used for SST flashes only. */
#define SPINOR_OP_BP 0x02 /* Byte program */
#define SPINOR_OP_WRDI 0x04 /* Write disable */
@@ -59,56 +101,79 @@
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
/* Status Register bits. */
-#define SR_WIP 1 /* Write in progress */
-#define SR_WEL 2 /* Write enable latch */
+#define SR_WIP BIT(0) /* Write in progress */
+#define SR_WEL BIT(1) /* Write enable latch */
/* meaning of other SR_* bits may differ between vendors */
-#define SR_BP0 4 /* Block protect 0 */
-#define SR_BP1 8 /* Block protect 1 */
-#define SR_BP2 0x10 /* Block protect 2 */
-#define SR_SRWD 0x80 /* SR write protect */
+#define SR_BP0 BIT(2) /* Block protect 0 */
+#define SR_BP1 BIT(3) /* Block protect 1 */
+#define SR_BP2 BIT(4) /* Block protect 2 */
+#define SR_SRWD BIT(7) /* SR write protect */
-#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
+#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
/* Flag Status Register bits */
-#define FSR_READY 0x80
+#define FSR_READY BIT(7)
/* Configuration Register bits. */
-#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
+#define CR_QUAD_EN_SPAN BIT(2) /* Spansion Quad I/O */
-enum read_mode {
- SPI_NOR_NORMAL = 0,
- SPI_NOR_FAST,
- SPI_NOR_DUAL,
- SPI_NOR_QUAD,
-};
+/* Supported SPI protocols */
+#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
+#define SNOR_PROTO_INST_SHIFT 16
+#define SNOR_PROTO_INST(_nbits) \
+ ((((unsigned long)(_nbits)) << SNOR_PROTO_INST_SHIFT) & \
+ SNOR_PROTO_INST_MASK)
-/**
- * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
- * @wren: command for "Write Enable", or 0x00 for not required
- * @cmd: command for operation
- * @cmd_pins: number of pins to send @cmd (1, 2, 4)
- * @addr: address for operation
- * @addr_pins: number of pins to send @addr (1, 2, 4)
- * @addr_width: number of address bytes
- * (3,4, or 0 for address not required)
- * @mode: mode data
- * @mode_pins: number of pins to send @mode (1, 2, 4)
- * @mode_cycles: number of mode cycles (0 for mode not required)
- * @dummy_cycles: number of dummy cycles (0 for dummy not required)
- */
-struct spi_nor_xfer_cfg {
- u8 wren;
- u8 cmd;
- u8 cmd_pins;
- u32 addr;
- u8 addr_pins;
- u8 addr_width;
- u8 mode;
- u8 mode_pins;
- u8 mode_cycles;
- u8 dummy_cycles;
+#define SNOR_PROTO_ADDR_MASK GENMASK(15, 8)
+#define SNOR_PROTO_ADDR_SHIFT 8
+#define SNOR_PROTO_ADDR(_nbits) \
+ ((((unsigned long)(_nbits)) << SNOR_PROTO_ADDR_SHIFT) & \
+ SNOR_PROTO_ADDR_MASK)
+
+#define SNOR_PROTO_DATA_MASK GENMASK(7, 0)
+#define SNOR_PROTO_DATA_SHIFT 0
+#define SNOR_PROTO_DATA(_nbits) \
+ ((((unsigned long)(_nbits)) << SNOR_PROTO_DATA_SHIFT) & \
+ SNOR_PROTO_DATA_MASK)
+
+#define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits) \
+ (SNOR_PROTO_INST(_inst_nbits) | \
+ SNOR_PROTO_ADDR(_addr_nbits) | \
+ SNOR_PROTO_DATA(_data_nbits))
+
+enum spi_nor_protocol {
+ SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),
+ SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2),
+ SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(1, 1, 4),
+ SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(1, 2, 2),
+ SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(1, 4, 4),
+ SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(2, 2, 2),
+ SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(4, 4, 4),
};
+static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto)
+{
+ return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >>
+ SNOR_PROTO_INST_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_addr_nbits(enum spi_nor_protocol proto)
+{
+ return ((unsigned long)(proto & SNOR_PROTO_ADDR_MASK)) >>
+ SNOR_PROTO_ADDR_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_data_nbits(enum spi_nor_protocol proto)
+{
+ return ((unsigned long)(proto & SNOR_PROTO_DATA_MASK)) >>
+ SNOR_PROTO_DATA_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
+{
+ return spi_nor_get_protocol_data_nbits(proto);
+}
+
#define SPI_NOR_MAX_CMD_SIZE 8
enum spi_nor_ops {
SPI_NOR_OPS_READ = 0,
@@ -133,50 +198,48 @@ enum spi_nor_option_flags {
* @read_opcode: the read opcode
* @read_dummy: the dummy needed by the read operation
* @program_opcode: the program opcode
- * @flash_read: the mode of the read
* @sst_write_second: used by the SST write operation
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
+ * @read_proto: the SPI protocol for read operations
+ * @write_proto: the SPI protocol for write operations
+ * @reg_proto the SPI protocol for read_reg/write_reg/erase operations
* @cfg: used by the read_xfer/write_xfer
* @cmd_buf: used by the write_reg
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the
* read/write/erase/lock/unlock operations
- * @read_xfer: [OPTIONAL] the read fundamental primitive
- * @write_xfer: [OPTIONAL] the writefundamental primitive
* @read_reg: [DRIVER-SPECIFIC] read out the register
* @write_reg: [DRIVER-SPECIFIC] write data to the register
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
- * at the offset @offs
+ * at the offset @offs; if not provided by the driver,
+ * spi-nor will send the erase opcode via write_reg()
* @priv: the private data
*/
struct spi_nor {
struct mtd_info *mtd;
struct mutex lock;
struct device_d *dev;
+ const struct flash_info *info;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
- enum read_mode flash_read;
+ enum spi_nor_protocol read_proto;
+ enum spi_nor_protocol write_proto;
+ enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
- struct spi_nor_xfer_cfg cfg;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
- int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
- u8 *buf, size_t len);
- int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
- u8 *buf, size_t len);
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
- int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int write_enable);
+ int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*read)(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *read_buf);
@@ -188,10 +251,56 @@ struct spi_nor {
};
/**
+ * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
+ * supported by the SPI controller (bus master).
+ * @mask: the bitmask listing all the supported hw capabilies
+ */
+struct spi_nor_hwcaps {
+ u32 mask;
+};
+
+/*
+ *(Fast) Read capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * As a matter of performances, it is relevant to use Quad SPI protocols first,
+ * then Dual SPI protocols before Fast Read and lastly (Slow) Read.
+ */
+#define SNOR_HWCAPS_READ_MASK GENMASK(7, 0)
+#define SNOR_HWCAPS_READ BIT(0)
+#define SNOR_HWCAPS_READ_FAST BIT(1)
+
+#define SNOR_HWCAPS_READ_DUAL GENMASK(4, 2)
+#define SNOR_HWCAPS_READ_1_1_2 BIT(2)
+#define SNOR_HWCAPS_READ_1_2_2 BIT(3)
+#define SNOR_HWCAPS_READ_2_2_2 BIT(4)
+
+#define SNOR_HWCAPS_READ_QUAD GENMASK(7, 5)
+#define SNOR_HWCAPS_READ_1_1_4 BIT(5)
+#define SNOR_HWCAPS_READ_1_4_4 BIT(6)
+#define SNOR_HWCAPS_READ_4_4_4 BIT(7)
+
+/*
+ * Page Program capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * Like (Fast) Read capabilities, Quad SPI protocols are preferred to the
+ * legacy SPI 1-1-1 protocol.
+ * Note that Dual Page Programs are not supported because there is no existing
+ * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
+ * implements such commands.
+ */
+#define SNOR_HWCAPS_PP_MASK GENMASK(19, 16)
+#define SNOR_HWCAPS_PP BIT(16)
+
+#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17)
+#define SNOR_HWCAPS_PP_1_1_4 BIT(17)
+#define SNOR_HWCAPS_PP_1_4_4 BIT(18)
+#define SNOR_HWCAPS_PP_4_4_4 BIT(19)
+
+/**
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure
* @name: the chip type name
- * @mode: the read mode supported by the driver
+ * @hwcaps: the hardware capabilities supported by the controller driver
* @use_large_blocks: prefer large blocks even if 4k blocks are supported
*
* The drivers can use this fuction to scan the SPI NOR.
@@ -202,7 +311,8 @@ struct spi_nor {
*
* Return: 0 for success, others for failure.
*/
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_hwcaps *hwcaps,
bool use_large_blocks);
#endif
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
new file mode 100644
index 0000000000..f65104d2d1
--- /dev/null
+++ b/include/linux/spi/spi-mem.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Exceet Electronics GmbH
+ * Copyright (C) 2018 Bootlin
+ *
+ * Author:
+ * Peter Pan <peterpandong@micron.com>
+ * Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#ifndef __LINUX_SPI_MEM_H
+#define __LINUX_SPI_MEM_H
+
+#include <spi/spi.h>
+
+#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
+ { \
+ .buswidth = __buswidth, \
+ .opcode = __opcode, \
+ }
+
+#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
+ { \
+ .nbytes = __nbytes, \
+ .val = __val, \
+ .buswidth = __buswidth, \
+ }
+
+#define SPI_MEM_OP_NO_ADDR { }
+
+#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \
+ { \
+ .nbytes = __nbytes, \
+ .buswidth = __buswidth, \
+ }
+
+#define SPI_MEM_OP_NO_DUMMY { }
+
+#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \
+ { \
+ .dir = SPI_MEM_DATA_IN, \
+ .nbytes = __nbytes, \
+ .buf.in = __buf, \
+ .buswidth = __buswidth, \
+ }
+
+#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
+ { \
+ .dir = SPI_MEM_DATA_OUT, \
+ .nbytes = __nbytes, \
+ .buf.out = __buf, \
+ .buswidth = __buswidth, \
+ }
+
+#define SPI_MEM_OP_NO_DATA { }
+
+/**
+ * enum spi_mem_data_dir - describes the direction of a SPI memory data
+ * transfer from the controller perspective
+ * @SPI_MEM_NO_DATA: no data transferred
+ * @SPI_MEM_DATA_IN: data coming from the SPI memory
+ * @SPI_MEM_DATA_OUT: data sent to the SPI memory
+ */
+enum spi_mem_data_dir {
+ SPI_MEM_NO_DATA,
+ SPI_MEM_DATA_IN,
+ SPI_MEM_DATA_OUT,
+};
+
+/**
+ * struct spi_mem_op - describes a SPI memory operation
+ * @cmd.buswidth: number of IO lines used to transmit the command
+ * @cmd.opcode: operation opcode
+ * @addr.nbytes: number of address bytes to send. Can be zero if the operation
+ * does not need to send an address
+ * @addr.buswidth: number of IO lines used to transmit the address cycles
+ * @addr.val: address value. This value is always sent MSB first on the bus.
+ * Note that only @addr.nbytes are taken into account in this
+ * address value, so users should make sure the value fits in the
+ * assigned number of bytes.
+ * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
+ * be zero if the operation does not require dummy bytes
+ * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
+ * @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dir: direction of the transfer
+ * @data.nbytes: number of data bytes to send/receive. Can be zero if the
+ * operation does not involve transferring data
+ * @data.buf.in: input buffer (must be DMA-able)
+ * @data.buf.out: output buffer (must be DMA-able)
+ */
+struct spi_mem_op {
+ struct {
+ u8 buswidth;
+ u8 opcode;
+ } cmd;
+
+ struct {
+ u8 nbytes;
+ u8 buswidth;
+ u64 val;
+ } addr;
+
+ struct {
+ u8 nbytes;
+ u8 buswidth;
+ } dummy;
+
+ struct {
+ u8 buswidth;
+ enum spi_mem_data_dir dir;
+ unsigned int nbytes;
+ union {
+ void *in;
+ const void *out;
+ } buf;
+ } data;
+};
+
+#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \
+ { \
+ .cmd = __cmd, \
+ .addr = __addr, \
+ .dummy = __dummy, \
+ .data = __data, \
+ }
+
+/**
+ * struct spi_mem_dirmap_info - Direct mapping information
+ * @op_tmpl: operation template that should be used by the direct mapping when
+ * the memory device is accessed
+ * @offset: absolute offset this direct mapping is pointing to
+ * @length: length in byte of this direct mapping
+ *
+ * These information are used by the controller specific implementation to know
+ * the portion of memory that is directly mapped and the spi_mem_op that should
+ * be used to access the device.
+ * A direct mapping is only valid for one direction (read or write) and this
+ * direction is directly encoded in the ->op_tmpl.data.dir field.
+ */
+struct spi_mem_dirmap_info {
+ struct spi_mem_op op_tmpl;
+ u64 offset;
+ u64 length;
+};
+
+/**
+ * struct spi_mem_dirmap_desc - Direct mapping descriptor
+ * @mem: the SPI memory device this direct mapping is attached to
+ * @info: information passed at direct mapping creation time
+ * @nodirmap: set to 1 if the SPI controller does not implement
+ * ->mem_ops->dirmap_create() or when this function returned an
+ * error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
+ * calls will use spi_mem_exec_op() to access the memory. This is a
+ * degraded mode that allows spi_mem drivers to use the same code
+ * no matter whether the controller supports direct mapping or not
+ * @priv: field pointing to controller specific data
+ *
+ * Common part of a direct mapping descriptor. This object is created by
+ * spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
+ * can create/attach direct mapping resources to the descriptor in the ->priv
+ * field.
+ */
+struct spi_mem_dirmap_desc {
+ struct spi_mem *mem;
+ struct spi_mem_dirmap_info info;
+ unsigned int nodirmap;
+ void *priv;
+};
+
+/**
+ * struct spi_mem - describes a SPI memory device
+ * @spi: the underlying SPI device
+ * @drvpriv: spi_mem_driver private data
+ * @name: name of the SPI memory device
+ *
+ * Extra information that describe the SPI memory device and may be needed by
+ * the controller to properly handle this device should be placed here.
+ *
+ * One example would be the device size since some controller expose their SPI
+ * mem devices through a io-mapped region.
+ */
+struct spi_mem {
+ struct spi_device *spi;
+ void *drvpriv;
+ const char *name;
+};
+
+/**
+ * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
+ * device
+ * @mem: memory device
+ * @data: data to attach to the memory device
+ */
+static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
+{
+ mem->drvpriv = data;
+}
+
+/**
+ * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
+ * device
+ * @mem: memory device
+ *
+ * Return: the data attached to the mem device.
+ */
+static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
+{
+ return mem->drvpriv;
+}
+
+/**
+ * struct spi_controller_mem_ops - SPI memory operations
+ * @adjust_op_size: shrink the data xfer of an operation to match controller's
+ * limitations (can be alignment of max RX/TX size
+ * limitations)
+ * @supports_op: check if an operation is supported by the controller
+ * @exec_op: execute a SPI memory operation
+ * @get_name: get a custom name for the SPI mem device from the controller.
+ * This might be needed if the controller driver has been ported
+ * to use the SPI mem layer and a custom name is used to keep
+ * mtdparts compatible.
+ * Note that if the implementation of this function allocates memory
+ * dynamically, then it should do so with devm_xxx(), as we don't
+ * have a ->free_name() function.
+ * @dirmap_create: create a direct mapping descriptor that can later be used to
+ * access the memory device. This method is optional
+ * @dirmap_destroy: destroy a memory descriptor previous created by
+ * ->dirmap_create()
+ * @dirmap_read: read data from the memory device using the direct mapping
+ * created by ->dirmap_create(). The function can return less
+ * data than requested (for example when the request is crossing
+ * the currently mapped area), and the caller of
+ * spi_mem_dirmap_read() is responsible for calling it again in
+ * this case.
+ * @dirmap_write: write data to the memory device using the direct mapping
+ * created by ->dirmap_create(). The function can return less
+ * data than requested (for example when the request is crossing
+ * the currently mapped area), and the caller of
+ * spi_mem_dirmap_write() is responsible for calling it again in
+ * this case.
+ *
+ * This interface should be implemented by SPI controllers providing an
+ * high-level interface to execute SPI memory operation, which is usually the
+ * case for QSPI controllers.
+ *
+ * Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
+ * mapping from the CPU because doing that can stall the CPU waiting for the
+ * SPI mem transaction to finish, and this will make real-time maintainers
+ * unhappy and might make your system less reactive. Instead, drivers should
+ * use DMA to access this direct mapping.
+ */
+struct spi_controller_mem_ops {
+ int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
+ bool (*supports_op)(struct spi_mem *mem,
+ const struct spi_mem_op *op);
+ int (*exec_op)(struct spi_mem *mem,
+ const struct spi_mem_op *op);
+ const char *(*get_name)(struct spi_mem *mem);
+ int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
+ void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
+ ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf);
+ ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf);
+};
+
+/**
+ * struct spi_mem_driver - SPI memory driver
+ * @spidrv: inherit from a SPI driver
+ * @probe: probe a SPI memory. Usually where detection/initialization takes
+ * place
+ * @remove: remove a SPI memory
+ *
+ * This is just a thin wrapper around a spi_driver. The core takes care of
+ * allocating the spi_mem object and forwarding the probe/remove
+ * request to the spi_mem_driver. The reason we use this wrapper is because
+ * we might have to stuff more information into the spi_mem struct to let
+ * SPI controllers know more about the SPI memory they interact with, and
+ * having this intermediate layer allows us to do that without adding more
+ * useless fields to the spi_device object.
+ */
+struct spi_mem_driver {
+ struct driver_d spidrv;
+ int (*probe)(struct spi_mem *mem);
+ int (*remove)(struct spi_mem *mem);
+};
+
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+
+bool spi_mem_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op);
+
+int spi_mem_exec_op(struct spi_mem *mem,
+ const struct spi_mem_op *op);
+
+const char *spi_mem_get_name(struct spi_mem *mem);
+
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_mem *mem,
+ const struct spi_mem_dirmap_info *info);
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf);
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf);
+
+#endif /* __LINUX_SPI_MEM_H */
diff --git a/include/soc/fsl/fsl_ddr_sdram.h b/include/soc/fsl/fsl_ddr_sdram.h
index 07d0af96fc..80508ef5d5 100644
--- a/include/soc/fsl/fsl_ddr_sdram.h
+++ b/include/soc/fsl/fsl_ddr_sdram.h
@@ -540,6 +540,7 @@ struct fsl_ddr_info {
};
phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo);
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step);
#ifdef CONFIG_SYS_FSL_DDR_LE
#define ddr_in32(a) in_le32(a)
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 620e5e57b4..bdcda0bb60 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -2,8 +2,12 @@
#define __INCLUDE_SPI_H
#include <driver.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
#include <linux/string.h>
+struct spi_controller_mem_ops;
+
struct spi_board_info {
char *name;
int max_speed_hz;
@@ -20,13 +24,14 @@ struct spi_board_info {
};
/**
- * struct spi_device - Master side proxy for an SPI slave device
+ * struct spi_device - Controller side proxy for an SPI slave device
* @dev: Driver model representation of the device.
- * @master: SPI controller used with the device.
+ * @controller: SPI controller used with the device.
+ * @master: Copy of controller, for backwards compatibility
* @max_speed_hz: Maximum clock rate to be used with this chip
* (on this board); may be changed by the device's driver.
* The spi_transfer.speed_hz can override this for each transfer.
- * @chip_select: Chipselect, distinguishing chips handled by @master.
+ * @chip_select: Chipselect, distinguishing chips handled by @controller.
* @mode: The spi mode defines how data is clocked out and in.
* This may be changed by the device's driver.
* The "active low" default for chipselect mode can be overridden
@@ -59,7 +64,9 @@ struct spi_board_info {
*/
struct spi_device {
struct device_d dev;
- struct spi_master *master;
+ struct spi_controller *controller;
+ struct spi_controller *master; /* compatibility layer */
+ struct spi_mem *mem;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
@@ -73,6 +80,16 @@ struct spi_device {
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
+#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
+#define SPI_READY 0x80 /* slave pulls low to pause */
+#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
+#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
+#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
+#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
+#define SPI_CS_WORD 0x1000 /* toggle cs after each word */
+#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */
+#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */
+#define SPI_3WIRE_HIZ 0x8000 /* high impedance turnaround */
u8 bits_per_word;
int irq;
void *controller_state;
@@ -93,10 +110,17 @@ struct spi_device {
struct spi_message;
/**
- * struct spi_master - interface to SPI master controller
+ * struct spi_controller - interface to SPI master or slave controller
* @dev: device interface to this driver
* @bus_num: board-specific (and often SOC-specific) identifier for a
* given SPI controller.
+ * @mem_ops: optimized/dedicated operations for interactions with SPI
+ * memory. This field is optional and should only be implemented
+ * if the controller has native support for memory like operations.
+ * @max_transfer_size: function that returns the max transfer size for
+ * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
+ * @max_message_size: function that returns the max message size for
+ * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
* @num_chipselect: chipselects are used to distinguish individual
* SPI slaves, and are numbered from zero to num_chipselects.
* each slave has a chipselect signal, but it's common that not
@@ -108,8 +132,9 @@ struct spi_message;
* the device whose settings are being modified.
* @transfer: adds a message to the controller's transfer queue.
* @cleanup: frees controller-specific state
+ * @list: link with the global spi_controller list
*
- * Each SPI master controller can communicate with one or more @spi_device
+ * Each SPI controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
* but not chip select signals. Each device may be configured to use a
* different clock rate, since those shared signals are ignored unless
@@ -120,7 +145,7 @@ struct spi_message;
* an SPI slave device. For each such message it queues, it calls the
* message's completion function when the transaction completes.
*/
-struct spi_master {
+struct spi_controller {
struct device_d *dev;
/* other than negative (== assign one dynamically), bus_num is fully
@@ -131,6 +156,15 @@ struct spi_master {
*/
s16 bus_num;
+ /* Optimized handlers for SPI memory-like operations */
+ const struct spi_controller_mem_ops *mem_ops;
+ /*
+ * on some hardware transfer size may be constrained
+ * the limit may depend on device transfer settings
+ */
+ size_t (*max_transfer_size)(struct spi_device *spi);
+ size_t (*max_message_size)(struct spi_device *spi);
+
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
@@ -147,7 +181,7 @@ struct spi_master {
* any other request management
* + To a given spi_device, message queueing is pure fifo
*
- * + The master's main job is to process its message queue,
+ * + The controller's main job is to process its message queue,
* selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
* arbitration algorithm is unspecified (round robin, fifo,
@@ -161,12 +195,49 @@ struct spi_master {
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
- /* called on release() to free memory provided by spi_master */
+ /* called on release() to free memory provided by spi_controller */
void (*cleanup)(struct spi_device *spi);
struct list_head list;
};
+static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
+{
+ if (ctlr->dev->platform_data)
+ return ctlr->dev->platform_data;
+ else
+ return ERR_PTR(-EINVAL);
+}
+
+static inline void spi_controller_set_devdata(struct spi_controller *ctlr,
+ void *data)
+{
+ ctlr->dev->platform_data = data;
+}
+
+static inline size_t spi_max_message_size(struct spi_device *spi)
+{
+ struct spi_controller *ctrl = spi->controller;
+ if (!ctrl->max_transfer_size)
+ return SIZE_MAX;
+ return ctrl->max_transfer_size(spi);
+}
+
+static inline size_t spi_max_transfer_size(struct spi_device *spi)
+{
+ struct spi_controller *ctrl = spi->controller;
+ size_t tr_max = SIZE_MAX;
+ size_t msg_max = spi_max_message_size(spi);
+
+ if (ctrl->max_transfer_size)
+ tr_max = ctrl->max_transfer_size(spi);
+
+ return min(tr_max, msg_max);
+}
+
+#define spi_master spi_controller
+#define spi_register_master(_ctrl) spi_register_controller(_ctrl)
+
/*---------------------------------------------------------------------------*/
/*
@@ -341,9 +412,9 @@ spi_transfer_del(struct spi_transfer *t)
int spi_sync(struct spi_device *spi, struct spi_message *message);
-struct spi_device *spi_new_device(struct spi_master *master,
+struct spi_device *spi_new_device(struct spi_controller *ctrl,
struct spi_board_info *chip);
-int spi_register_master(struct spi_master *master);
+int spi_register_controller(struct spi_controller *ctrl);
#ifdef CONFIG_SPI
int spi_register_board_info(struct spi_board_info const *info, int num);
@@ -431,7 +502,7 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
extern struct bus_type spi_bus;
-struct spi_master *spi_get_master(int bus);
+struct spi_controller *spi_get_controller(int bus);
static inline int spi_driver_register(struct driver_d *drv)
{
diff --git a/lib/Kconfig b/lib/Kconfig
index 27f62461f0..7cf6975bcc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -57,6 +57,9 @@ config XZ_DEC_SPARC
config REED_SOLOMON
bool
+config BASE64
+ bool "include base64 encode/decode support"
+
config GENERIC_FIND_NEXT_BIT
def_bool n
@@ -155,4 +158,7 @@ config GENERIC_LIB_MULDI3
config NLS
bool "Native language support"
+config BLOBGEN
+ bool "include blob encode/decode support"
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 8dabf4ae77..161d3a756e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -18,6 +18,7 @@ obj-y += readkey.o
obj-y += kfifo.o
obj-y += libbb.o
obj-y += libgen.o
+obj-$(CONFIG_BLOBGEN) += blobgen.o
obj-y += stringlist.o
obj-y += cmdlinepart.o
obj-y += recursive_action.o
@@ -44,6 +45,7 @@ obj-$(CONFIG_LZ4_DECOMPRESS) += decompress_unlz4.o
obj-$(CONFIG_PROCESS_ESCAPE_SEQUENCE) += process_escape_sequence.o
obj-$(CONFIG_UNCOMPRESS) += uncompress.o
obj-$(CONFIG_BCH) += bch.o
+obj-$(CONFIG_BASE64) += base64.o
obj-$(CONFIG_BITREV) += bitrev.o
obj-$(CONFIG_QSORT) += qsort.o
obj-$(CONFIG_LIBSCAN) += libscan.o
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 0000000000..ac165ab168
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,154 @@
+/*
+ * Code based on busybox-1.23.2
+ *
+ * Copyright 2003, Glenn McGrath
+ * Copyright 2006, Rob Landley <rob@landley.net>
+ * Copyright 2010, Denys Vlasenko
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <common.h>
+#include <base64.h>
+
+/* Conversion table. for base 64 */
+static const char uuenc_tbl_base64[65 + 1] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ '=' /* termination character */,
+ '\0' /* needed for uudecode.c only */
+};
+
+/*
+ * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
+ * to STORE. STORE will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes.
+ * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
+ */
+void uuencode(char *p, const char *src, int length)
+{
+ const unsigned char *s = src;
+ const char *tbl = uuenc_tbl_base64;
+
+ /* Transform the 3x8 bits to 4x6 bits */
+ while (length > 0) {
+ unsigned s1, s2;
+
+ /* Are s[1], s[2] valid or should be assumed 0? */
+ s1 = s2 = 0;
+ length -= 3; /* can be >=0, -1, -2 */
+ if (length >= -1) {
+ s1 = s[1];
+ if (length >= 0)
+ s2 = s[2];
+ }
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
+ *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
+ *p++ = tbl[s2 & 0x3f];
+ s += 3;
+ }
+ /* Zero-terminate */
+ *p = '\0';
+ /* If length is -2 or -1, pad last char or two */
+ while (length) {
+ *--p = tbl[64];
+ length++;
+ }
+}
+EXPORT_SYMBOL(uuencode);
+
+/*
+ * Decode base64 encoded string. Stops on '\0'.
+ *
+ */
+int decode_base64(char *p_dst, int dst_len, const char *src)
+{
+ const char *src_tail;
+ char *dst = p_dst;
+ int length = 0;
+
+ while (dst_len > 0) {
+ unsigned char six_bit[4];
+ int count = 0;
+
+ /* Fetch up to four 6-bit values */
+ src_tail = src;
+ while (count < 4) {
+ const char *table_ptr;
+ int ch;
+
+ /*
+ * Get next _valid_ character.
+ * uuenc_tbl_base64[] contains this string:
+ * 0 1 2 3 4 5 6
+ * 01234567890123456789012345678901234567890123456789012345678901234
+ * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+ */
+ do {
+ ch = *src;
+ if (ch == '\0') {
+ /*
+ * Example:
+ * If we decode "QUJD <NUL>", we want
+ * to return ptr to NUL, not to ' ',
+ * because we did fully decode
+ * the string (to "ABC").
+ */
+ if (count == 0)
+ src_tail = src;
+ goto ret;
+ }
+ src++;
+ table_ptr = strchr(uuenc_tbl_base64, ch);
+ } while (!table_ptr);
+
+ /* Convert encoded character to decimal */
+ ch = table_ptr - uuenc_tbl_base64;
+
+ /* ch is 64 if char was '=', otherwise 0..63 */
+ if (ch == 64)
+ break;
+
+ six_bit[count] = ch;
+ count++;
+ }
+
+ /*
+ * Transform 6-bit values to 8-bit ones.
+ * count can be < 4 when we decode the tail:
+ * "eQ==" -> "y", not "y NUL NUL".
+ * Note that (count > 1) is always true,
+ * "x===" encoding is not valid:
+ * even a single zero byte encodes as "AA==".
+ * However, with current logic we come here with count == 1
+ * when we decode "==" tail.
+ */
+ if (count > 1)
+ *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
+ if (count > 2)
+ *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
+ if (count > 3)
+ *dst++ = six_bit[2] << 6 | six_bit[3];
+ /*
+ * Note that if we decode "AA==" and ate first '=',
+ * we just decoded one char (count == 2) and now we'll
+ * do the loop once more to decode second '='.
+ */
+ dst_len -= count-1;
+ /* last character was "=" */
+ if (count != 0)
+ length += count - 1;
+ }
+ret:
+ p_dst = dst;
+
+ return length;
+}
+EXPORT_SYMBOL(decode_base64);
diff --git a/lib/blobgen.c b/lib/blobgen.c
new file mode 100644
index 0000000000..5a556a68ce
--- /dev/null
+++ b/lib/blobgen.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+#include <blobgen.h>
+#include <base64.h>
+#include <malloc.h>
+#include <crypto.h>
+#include <dma.h>
+#include <environment.h>
+
+static LIST_HEAD(blobs);
+static struct blobgen *bg_default;
+
+/**
+ * blob_gen_register - register a blob device
+ * @dev: The parent device
+ * @bg: The blobgen device
+ *
+ * This registers a blob device. Returns 0 for success or a negative error
+ * code otherwise.
+ */
+int blob_gen_register(struct device_d *dev, struct blobgen *bg)
+{
+ int ret;
+
+ dev_set_name(&bg->dev, "blob");
+ bg->dev.parent = dev;
+
+ ret = register_device(&bg->dev);
+ if (ret)
+ return ret;
+
+ list_add_tail(&bg->list, &blobs);
+
+ if (!bg_default)
+ bg_default = bg;
+
+ return 0;
+}
+
+/**
+ * blobgen_get - get a blob generator of given name
+ * @name: The name of the blob generator to look for
+ *
+ * Finds a blob generator by name and returns it. Returns NULL if none is found.
+ */
+struct blobgen *blobgen_get(const char *name)
+{
+ struct device_d *dev;
+ struct blobgen *bg;
+
+ if (!name)
+ return bg_default;
+
+ dev = get_device_by_name(name);
+ if (!dev)
+ return NULL;
+
+ list_for_each_entry(bg, &blobs, list) {
+ if (dev == &bg->dev)
+ return bg;
+ }
+
+ return NULL;
+}
+
+/**
+ * blob_encrypt - encrypt a data blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @plain: The plaintext input
+ * @plainsize: Length of the plain data in bytes
+ * @retblob: The encrypted blob is returned here
+ * @blobsize: The returned length of the encrypted blob
+ *
+ * This encrypts a blob passed in @plain to an allocated buffer returned in
+ * @retblob. Returns 0 for success or a negative error code otherwise. @retblob
+ * is valid when the function returns successfully. The caller must free the
+ * buffer after use.
+ */
+int blob_encrypt(struct blobgen *bg, const char *modifier, const void *plain,
+ int plainsize, void **retblob, int *blobsize)
+{
+ void *blob;
+ int ret;
+
+ if (plainsize > bg->max_payload_size)
+ return -EINVAL;
+
+ pr_debug("%s plain:\n", __func__);
+ pr_memory_display(MSG_DEBUG, plain, 0, plainsize, 1, 0);
+
+ blob = dma_alloc(MAX_BLOB_LEN);
+ if (!blob)
+ return -ENOMEM;
+
+ ret = bg->encrypt(bg, modifier, plain, plainsize, blob, blobsize);
+
+ if (ret) {
+ free(blob);
+ } else {
+ pr_debug("%s encrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, blob, 0, *blobsize, 1, 0);
+ *retblob = blob;
+ }
+
+ return ret;
+}
+
+/**
+ * blob_encrypt_to_env - encrypt blob to environment variable
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @plain: The plaintext input
+ * @plainsize: Length of the plain data in bytes
+ * @varname: Name of the variable to set with the output blob
+ *
+ * This uses blob_encrypt to encrypt a blob. The result is base64 encoded and
+ * written to the environment variable @varname. Returns 0 for success or a
+ * negative error code otherwise.
+ */
+int blob_encrypt_to_env(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, const char *varname)
+{
+ int ret;
+ int blobsize;
+ void *blob;
+ char *value;
+
+ ret = blob_encrypt(bg, modifier, plain, plainsize, &blob, &blobsize);
+ if (ret)
+ return ret;
+
+ value = malloc(BASE64_LENGTH(blobsize) + 1);
+ if (!value)
+ return -ENOMEM;
+
+ uuencode(value, blob, blobsize);
+
+ pr_debug("%s encrypted base64: \"%s\"\n", __func__, value);
+
+ ret = setenv(varname, value);
+
+ free(value);
+ free(blob);
+
+ return ret;
+}
+
+/**
+ * blob_decrypt - decrypt a blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @blob: The encrypted blob
+ * @blobsize: Size of the encrypted blob
+ * @plain: Plaintext is returned here
+ * @plainsize: Size of the data returned in bytes
+ *
+ * This function decrypts a blob generated with blob_encrypt. @modifier must match
+ * the modifier used to encrypt the data. Returns 0 when the data could be
+ * decrypted successfully or a negative error code otherwise.
+ */
+int blob_decrypt(struct blobgen *bg, const char *modifier, const void *blob,
+ int blobsize, void **plain, int *plainsize)
+{
+ int ret;
+
+ pr_debug("%s encrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, blob, 0, blobsize, 1, 0);
+
+ ret = bg->decrypt(bg, modifier, blob, blobsize, plain, plainsize);
+
+ if (!ret) {
+ pr_debug("%s decrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, *plain, 0, *plainsize, 1, 0);
+ }
+
+ return ret;
+}
+
+/**
+ * blob_decrypt_from_base64 - decrypt a base64 encoded blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @encrypted: base64 encoded encrypted data
+ * @plain: Plaintext is returned here
+ * @plainsize: Size of the data returned in bytes
+ *
+ * like blob_decrypt, but takes the encrypted data as a base64 encoded string.
+ * Returns 0 when the data could be decrypted successfully or a negative error
+ * code otherwise.
+ */
+int blob_decrypt_from_base64(struct blobgen *bg, const char *modifier,
+ const char *encrypted, void **plain,
+ int *plainsize)
+{
+ char *data;
+ int ret, len;
+
+ data = dma_alloc(MAX_BLOB_LEN);
+ if (!data)
+ return -ENOMEM;
+
+ pr_debug("encrypted base64: \"%s\"\n", encrypted);
+
+ len = decode_base64(data, MAX_BLOB_LEN, encrypted);
+
+ ret = blob_decrypt(bg, modifier, data, len, plain, plainsize);
+
+ free(data);
+
+ return ret;
+}
diff --git a/scripts/Makefile b/scripts/Makefile
index 712cf99217..1af5f9fc98 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -12,6 +12,7 @@ hostprogs-y += bareboxcrc32
hostprogs-y += kernel-install
hostprogs-$(CONFIG_IMD) += bareboximd
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
+hostprogs-$(CONFIG_MIPS) += mips-relocs
hostprogs-$(CONFIG_MVEBU_HOSTTOOLS) += kwbimage kwboot
hostprogs-$(CONFIG_ARCH_NETX) += gen_netx_image
hostprogs-$(CONFIG_ARCH_OMAP) += omap_signGP mk-omap-image
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 8c07a54d05..95eaf522ab 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -470,7 +470,7 @@ cmd_imximage_S_dcd= \
overwrite-hab-env = $(shell set -e; \
test -n "$(CONFIG_HAB_CERTS_ENV)"; \
test -n "$$$(1)"; \
- echo -D$(1)=\"$(shell echo $$$(1))\")
+ echo -D$(1)=\\\"$(shell echo $$$(1))\\\")
imxcfg_cpp_flags = -Wp,-MD,$(depfile) -nostdinc -x assembler-with-cpp \
-I $(srctree)/include -I $(srctree)/arch/arm/mach-imx/include \
diff --git a/scripts/mips-relocs.c b/scripts/mips-relocs.c
new file mode 100644
index 0000000000..7916159581
--- /dev/null
+++ b/scripts/mips-relocs.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPS Relocation Data Generator
+ *
+ * Copyright (c) 2017 Imagination Technologies Ltd.
+ */
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <../../arch/mips/include/asm/relocs.h>
+
+#define hdr_field(pfx, idx, field) ({ \
+ uint64_t _val; \
+ unsigned int _size; \
+ \
+ if (is_64) { \
+ _val = pfx##hdr64[idx].field; \
+ _size = sizeof(pfx##hdr64[0].field); \
+ } else { \
+ _val = pfx##hdr32[idx].field; \
+ _size = sizeof(pfx##hdr32[0].field); \
+ } \
+ \
+ switch (_size) { \
+ case 1: \
+ break; \
+ case 2: \
+ _val = is_be ? be16toh(_val) : le16toh(_val); \
+ break; \
+ case 4: \
+ _val = is_be ? be32toh(_val) : le32toh(_val); \
+ break; \
+ case 8: \
+ _val = is_be ? be64toh(_val) : le64toh(_val); \
+ break; \
+ } \
+ \
+ _val; \
+})
+
+#define set_hdr_field(pfx, idx, field, val) ({ \
+ uint64_t _val; \
+ unsigned int _size; \
+ \
+ if (is_64) \
+ _size = sizeof(pfx##hdr64[0].field); \
+ else \
+ _size = sizeof(pfx##hdr32[0].field); \
+ \
+ switch (_size) { \
+ case 1: \
+ _val = val; \
+ break; \
+ case 2: \
+ _val = is_be ? htobe16(val) : htole16(val); \
+ break; \
+ case 4: \
+ _val = is_be ? htobe32(val) : htole32(val); \
+ break; \
+ case 8: \
+ _val = is_be ? htobe64(val) : htole64(val); \
+ break; \
+ default: \
+ /* We should never reach here */ \
+ _val = 0; \
+ assert(0); \
+ break; \
+ } \
+ \
+ if (is_64) \
+ pfx##hdr64[idx].field = _val; \
+ else \
+ pfx##hdr32[idx].field = _val; \
+})
+
+#define ehdr_field(field) \
+ hdr_field(e, 0, field)
+#define phdr_field(idx, field) \
+ hdr_field(p, idx, field)
+#define shdr_field(idx, field) \
+ hdr_field(s, idx, field)
+
+#define set_phdr_field(idx, field, val) \
+ set_hdr_field(p, idx, field, val)
+#define set_shdr_field(idx, field, val) \
+ set_hdr_field(s, idx, field, val)
+
+#define shstr(idx) (&shstrtab[idx])
+
+bool is_64, is_be;
+uint64_t text_base;
+
+struct mips_reloc {
+ uint8_t type;
+ uint64_t offset;
+} *relocs;
+size_t relocs_sz, relocs_idx;
+
+static int add_reloc(unsigned int type, uint64_t off)
+{
+ struct mips_reloc *new;
+ size_t new_sz;
+
+ switch (type) {
+ case R_MIPS_NONE:
+ case R_MIPS_LO16:
+ case R_MIPS_PC16:
+ case R_MIPS_HIGHER:
+ case R_MIPS_HIGHEST:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC26_S2:
+ /* Skip these relocs */
+ return 0;
+
+ default:
+ break;
+ }
+
+ if (relocs_idx == relocs_sz) {
+ new_sz = relocs_sz ? relocs_sz * 2 : 128;
+ new = realloc(relocs, new_sz * sizeof(*relocs));
+ if (!new) {
+ fprintf(stderr, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ relocs = new;
+ relocs_sz = new_sz;
+ }
+
+ relocs[relocs_idx++] = (struct mips_reloc){
+ .type = type,
+ .offset = off,
+ };
+
+ return 0;
+}
+
+static int parse_mips32_rel(const void *_rel)
+{
+ const Elf32_Rel *rel = _rel;
+ uint32_t off, type;
+
+ off = is_be ? be32toh(rel->r_offset) : le32toh(rel->r_offset);
+ off -= text_base;
+
+ type = is_be ? be32toh(rel->r_info) : le32toh(rel->r_info);
+ type = ELF32_R_TYPE(type);
+
+ return add_reloc(type, off);
+}
+
+static int parse_mips64_rela(const void *_rel)
+{
+ const Elf64_Rela *rel = _rel;
+ uint64_t off, type;
+
+ off = is_be ? be64toh(rel->r_offset) : le64toh(rel->r_offset);
+ off -= text_base;
+
+ type = rel->r_info >> (64 - 8);
+
+ return add_reloc(type, off);
+}
+
+static void output_uint(uint8_t **buf, uint64_t val)
+{
+ uint64_t tmp;
+
+ do {
+ tmp = val & 0x7f;
+ val >>= 7;
+ tmp |= !!val << 7;
+ *(*buf)++ = tmp;
+ } while (val);
+}
+
+static int compare_relocs(const void *a, const void *b)
+{
+ const struct mips_reloc *ra = a, *rb = b;
+
+ return ra->offset - rb->offset;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, j, i_rel_shdr, sh_type, sh_entsize, sh_entries;
+ size_t rel_size, rel_actual_size;
+ const char *shstrtab, *sh_name, *rel_pfx;
+ int (*parse_fn)(const void *rel);
+ uint8_t *buf_start, *buf;
+ const Elf32_Ehdr *ehdr32;
+ const Elf64_Ehdr *ehdr64;
+ uintptr_t sh_offset;
+ Elf32_Shdr *shdr32;
+ Elf64_Shdr *shdr64;
+ struct stat st;
+ int err, fd;
+ void *elf;
+ bool skip;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to open input file %s\n", argv[1]);
+ err = errno;
+ goto out_ret;
+ }
+
+ err = fstat(fd, &st);
+ if (err) {
+ fprintf(stderr, "Unable to fstat() input file\n");
+ goto out_close_fd;
+ }
+
+ elf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (elf == MAP_FAILED) {
+ fprintf(stderr, "Unable to mmap() input file\n");
+ err = errno;
+ goto out_close_fd;
+ }
+
+ ehdr32 = elf;
+ ehdr64 = elf;
+
+ if (memcmp(&ehdr32->e_ident[EI_MAG0], ELFMAG, SELFMAG)) {
+ fprintf(stderr, "Input file is not an ELF\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ if (ehdr32->e_ident[EI_VERSION] != EV_CURRENT) {
+ fprintf(stderr, "Unrecognised ELF version\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ switch (ehdr32->e_ident[EI_CLASS]) {
+ case ELFCLASS32:
+ is_64 = false;
+ break;
+ case ELFCLASS64:
+ is_64 = true;
+ break;
+ default:
+ fprintf(stderr, "Unrecognised ELF class\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ switch (ehdr32->e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ is_be = false;
+ break;
+ case ELFDATA2MSB:
+ is_be = true;
+ break;
+ default:
+ fprintf(stderr, "Unrecognised ELF data encoding\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ if (ehdr_field(e_type) != ET_EXEC) {
+ fprintf(stderr, "Input ELF is not an executable\n");
+ printf("type 0x%lx\n", ehdr_field(e_type));
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ if (ehdr_field(e_machine) != EM_MIPS) {
+ fprintf(stderr, "Input ELF does not target MIPS\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ shdr32 = elf + ehdr_field(e_shoff);
+ shdr64 = elf + ehdr_field(e_shoff);
+ shstrtab = elf + shdr_field(ehdr_field(e_shstrndx), sh_offset);
+
+ i_rel_shdr = UINT_MAX;
+ for (i = 0; i < ehdr_field(e_shnum); i++) {
+ sh_name = shstr(shdr_field(i, sh_name));
+
+ if (!strcmp(sh_name, ".data.reloc")) {
+ i_rel_shdr = i;
+ continue;
+ }
+
+ if (!strcmp(sh_name, ".text")) {
+ text_base = shdr_field(i, sh_addr);
+ continue;
+ }
+ }
+ if (i_rel_shdr == UINT_MAX) {
+ fprintf(stderr, "Unable to find .rel section\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+ if (!text_base) {
+ fprintf(stderr, "Unable to find .text base address\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ rel_pfx = is_64 ? ".rela." : ".rel.";
+
+ for (i = 0; i < ehdr_field(e_shnum); i++) {
+ sh_type = shdr_field(i, sh_type);
+ if ((sh_type != SHT_REL) && (sh_type != SHT_RELA))
+ continue;
+
+ sh_name = shstr(shdr_field(i, sh_name));
+ if (strncmp(sh_name, rel_pfx, strlen(rel_pfx))) {
+ if (strcmp(sh_name, ".rel") && strcmp(sh_name, ".rel.dyn"))
+ fprintf(stderr, "WARNING: Unexpected reloc section name '%s'\n", sh_name);
+ continue;
+ }
+
+ /*
+ * Skip reloc sections which either don't correspond to another
+ * section in the ELF, or whose corresponding section isn't
+ * loaded as part of the U-Boot binary (ie. doesn't have the
+ * alloc flags set).
+ */
+ skip = true;
+ for (j = 0; j < ehdr_field(e_shnum); j++) {
+ if (strcmp(&sh_name[strlen(rel_pfx) - 1], shstr(shdr_field(j, sh_name))))
+ continue;
+
+ skip = !(shdr_field(j, sh_flags) & SHF_ALLOC);
+ break;
+ }
+ if (skip)
+ continue;
+
+ sh_offset = shdr_field(i, sh_offset);
+ sh_entsize = shdr_field(i, sh_entsize);
+ sh_entries = shdr_field(i, sh_size) / sh_entsize;
+
+ if (sh_type == SHT_REL) {
+ if (is_64) {
+ fprintf(stderr, "REL-style reloc in MIPS64 ELF?\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ } else {
+ parse_fn = parse_mips32_rel;
+ }
+ } else {
+ if (is_64) {
+ parse_fn = parse_mips64_rela;
+ } else {
+ fprintf(stderr, "RELA-style reloc in MIPS32 ELF?\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+ }
+
+ for (j = 0; j < sh_entries; j++) {
+ err = parse_fn(elf + sh_offset + (j * sh_entsize));
+ if (err)
+ goto out_free_relocs;
+ }
+ }
+
+ if (!relocs) {
+ fprintf(stderr, "No relocs was found. Try to configure linker with --emit-relocs\n");
+ err = -EINVAL;
+ goto out_free_relocs;
+ }
+
+ /* Sort relocs in ascending order of offset */
+ qsort(relocs, relocs_idx, sizeof(*relocs), compare_relocs);
+
+ /* Make reloc offsets relative to their predecessor */
+ for (i = relocs_idx - 1; i > 0; i--) {
+ relocs[i].offset -= relocs[i - 1].offset;
+ }
+
+ /* Write the relocations to the .rel section */
+ buf = buf_start = elf + shdr_field(i_rel_shdr, sh_offset);
+ for (i = 0; i < relocs_idx; i++) {
+ output_uint(&buf, relocs[i].type);
+ output_uint(&buf, relocs[i].offset >> 2);
+ }
+
+ /* Write a terminating R_MIPS_NONE (0) */
+ output_uint(&buf, R_MIPS_NONE);
+
+ /* Ensure the relocs didn't overflow the .rel section */
+ rel_size = shdr_field(i_rel_shdr, sh_size);
+ rel_actual_size = buf - buf_start;
+ if (rel_actual_size > rel_size) {
+ fprintf(stderr, "Relocations overflow available space of 0x%lx (required 0x%lx)!\n",
+ rel_size, rel_actual_size);
+ fprintf(stderr, "Please adjust CONFIG_MIPS_RELOCATION_TABLE_SIZE to at least 0x%lx\n",
+ (rel_actual_size + 0x100) & ~0xFF);
+ err = -ENOMEM;
+ goto out_free_relocs;
+ }
+
+ /* Make sure data is written back to the file */
+ err = msync(elf, st.st_size, MS_SYNC);
+ if (err) {
+ fprintf(stderr, "Failed to msync: %d\n", errno);
+ goto out_free_relocs;
+ }
+
+out_free_relocs:
+ free(relocs);
+ munmap(elf, st.st_size);
+out_close_fd:
+ close(fd);
+out_ret:
+ return err;
+}
diff --git a/scripts/pblimage.c b/scripts/pblimage.c
index 6e83c523e5..73c0169ac1 100644
--- a/scripts/pblimage.c
+++ b/scripts/pblimage.c
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <getopt.h>
#include <endian.h>
+#include <byteswap.h>
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define PBL_ACS_CONT_CMD 0x81000000
@@ -49,11 +50,11 @@ static int pbl_end;
static int image_size;
static int out_fd;
static int in_fd;
+static int spiimage;
static uint32_t pbl_cmd_initaddr;
static uint32_t pbi_crc_cmd1;
static uint32_t pbi_crc_cmd2;
-static uint32_t pbl_end_cmd[4];
enum arch {
ARCH_ARM,
@@ -210,16 +211,6 @@ static void pbl_parser(char *name)
static void add_end_cmd(void)
{
uint32_t crc32_pbl;
- int i;
- unsigned char *p = (unsigned char *)&pbl_end_cmd;
-
- for (i = 0; i < 4; i++)
- pbl_end_cmd[i] = htobe32(pbl_end_cmd[i]);
-
- for (i = 0; i < 16; i++) {
- *pmem_buf++ = *p++;
- pbl_size++;
- }
/* Add PBI CRC command. */
*pmem_buf++ = 0x08;
@@ -240,6 +231,7 @@ static void add_end_cmd(void)
static void pbl_load_image(void)
{
int size;
+ uint64_t *buf64 = (void *)mem_buf;
/* parse the rcw.cfg file. */
pbl_parser(rcwfile);
@@ -256,6 +248,15 @@ static void pbl_load_image(void)
add_end_cmd();
+ if (spiimage) {
+ int i;
+
+ pbl_size = roundup(pbl_size, 8);
+
+ for (i = 0; i < pbl_size / 8; i++)
+ buf64[i] = bswap_64(buf64[i]);
+ }
+
size = pbl_size;
if (write(out_fd, (const void *)&mem_buf, size) != size) {
@@ -297,18 +298,10 @@ static int pblimage_check_params(void)
pbl_cmd_initaddr = loadaddr & PBL_ADDR_24BIT_MASK;
pbl_cmd_initaddr |= PBL_ACS_CONT_CMD;
pbl_cmd_initaddr += image_size;
- pbl_end_cmd[0] = 0x09610000;
- pbl_end_cmd[1] = 0x00000000;
- pbl_end_cmd[2] = 0x096100c0;
- pbl_end_cmd[3] = 0x00000000;
} else {
pbi_crc_cmd1 = 0x13;
pbi_crc_cmd2 = 0x80;
pbl_cmd_initaddr = 0x82000000;
- pbl_end_cmd[0] = 0x091380c0;
- pbl_end_cmd[1] = 0x00000000;
- pbl_end_cmd[2] = 0x091380c0;
- pbl_end_cmd[3] = 0x00000000;
}
next_pbl_cmd = pbl_cmd_initaddr;
@@ -357,7 +350,7 @@ int main(int argc, char *argv[])
int opt, ret;
off_t pos;
- while ((opt = getopt(argc, argv, "i:r:p:o:m:")) != -1) {
+ while ((opt = getopt(argc, argv, "i:r:p:o:m:s")) != -1) {
switch (opt) {
case 'i':
infile = optarg;
@@ -374,6 +367,9 @@ int main(int argc, char *argv[])
case 'm':
pbl_end = atoi(optarg);
break;
+ case 's':
+ spiimage = 1;
+ break;
default:
exit(EXIT_FAILURE);
}