Browse Source

Merge branch '2021-07-14-platform-updates'

- Assorted platform updates
Tom Rini 2 years ago
parent
commit
eae8c7c338
41 changed files with 1682 additions and 254 deletions
  1. 2 2
      MAINTAINERS
  2. 1 0
      arch/arm/dts/Makefile
  3. 189 0
      arch/arm/dts/uniphier-ld20-akebi96.dts
  4. 1 2
      arch/arm/mach-snapdragon/pinctrl-apq8016.c
  5. 1 1
      arch/arm/mach-snapdragon/pinctrl-apq8096.c
  6. 2 0
      arch/arm/mach-u8500/Kconfig
  7. 1 0
      board/ste/stemmy/README
  8. 148 1
      board/ste/stemmy/stemmy.c
  9. 7 3
      common/board_info.c
  10. 1 1
      configs/stemmy_defconfig
  11. 1 2
      configs/total_compute_defconfig
  12. 2 1
      doc/git-mailrc
  13. 2 0
      drivers/Kconfig
  14. 1 0
      drivers/Makefile
  15. 3 0
      drivers/clk/uniphier/clk-uniphier-sys.c
  16. 8 0
      drivers/gpio/Kconfig
  17. 1 1
      drivers/gpio/Makefile
  18. 0 221
      drivers/gpio/db8500_gpio.c
  19. 125 0
      drivers/gpio/nmk_gpio.c
  20. 1 0
      drivers/misc/i2c_eeprom.c
  21. 10 0
      drivers/pci/Kconfig
  22. 1 0
      drivers/pci/Makefile
  23. 424 0
      drivers/pci/pcie_uniphier.c
  24. 6 0
      drivers/phy/Kconfig
  25. 1 0
      drivers/phy/Makefile
  26. 52 0
      drivers/phy/phy-ab8500-usb.c
  27. 12 0
      drivers/phy/socionext/Kconfig
  28. 6 0
      drivers/phy/socionext/Makefile
  29. 59 0
      drivers/phy/socionext/phy-uniphier-pcie.c
  30. 10 0
      drivers/power/pmic/Kconfig
  31. 1 0
      drivers/power/pmic/Makefile
  32. 268 0
      drivers/power/pmic/ab8500.c
  33. 3 0
      drivers/reset/reset-uniphier.c
  34. 2 5
      drivers/timer/nomadik-mtu-timer.c
  35. 10 1
      drivers/usb/musb-new/Kconfig
  36. 1 0
      drivers/usb/musb-new/Makefile
  37. 1 1
      drivers/usb/musb-new/musb_core.c
  38. 179 0
      drivers/usb/musb-new/ux500.c
  39. 12 12
      include/configs/stemmy.h
  40. 2 0
      include/configs/uniphier.h
  41. 125 0
      include/power/ab8500.h

+ 2 - 2
MAINTAINERS

@@ -118,8 +118,8 @@ F:	cmd/arm/
 ARM ALTERA SOCFPGA
 M:	Marek Vasut <marex@denx.de>
 M:	Simon Goldschmidt <simon.k.r.goldschmidt@gmail.com>
-M:	Ley Foon Tan <lftan.linux@gmail.com>
-S:	Maintainted
+M:	Tien Fong Chee <tien.fong.chee@intel.com>
+S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-socfpga.git
 F:	arch/arm/mach-socfpga/
 F:	drivers/sysreset/sysreset_socfpga*

+ 1 - 0
arch/arm/dts/Makefile

@@ -254,6 +254,7 @@ dtb-$(CONFIG_ARCH_UNIPHIER_LD11) += \
 	uniphier-ld11-global.dtb \
 	uniphier-ld11-ref.dtb
 dtb-$(CONFIG_ARCH_UNIPHIER_LD20) += \
+	uniphier-ld20-akebi96.dtb \
 	uniphier-ld20-global.dtb \
 	uniphier-ld20-ref.dtb
 dtb-$(CONFIG_ARCH_UNIPHIER_LD4) += \

+ 189 - 0
arch/arm/dts/uniphier-ld20-akebi96.dts

@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+ OR MIT
+//
+// Device Tree Source for Akebi96 Development Board
+//
+// Derived from uniphier-ld20-global.dts.
+//
+// Copyright (C) 2015-2017 Socionext Inc.
+// Copyright (C) 2019-2020 Linaro Ltd.
+
+/dts-v1/;
+#include <dt-bindings/gpio/uniphier-gpio.h>
+#include "uniphier-ld20.dtsi"
+
+/ {
+	model = "Akebi96";
+	compatible = "socionext,uniphier-ld20-akebi96",
+		     "socionext,uniphier-ld20";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	aliases {
+		serial0 = &serial0;
+		serial1 = &serial1;
+		serial2 = &serial2;
+		serial3 = &serial3;
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c2 = &i2c2;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		spi2 = &spi2;
+		spi3 = &spi3;
+		ethernet0 = &eth;
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0xc0000000>;
+	};
+
+	framebuffer@c0000000 {
+		compatible = "simple-framebuffer";
+		reg = <0 0xc0000000 0 0x02000000>;
+		width = <1920>;
+		height = <1080>;
+		stride = <7680>;
+		format = "a8r8g8b8";
+	};
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		memory@c0000000 {
+			reg = <0 0xc0000000 0 0x02000000>;
+			no-map;
+		};
+	};
+
+	sound {
+		compatible = "audio-graph-card";
+		label = "UniPhier LD20";
+		dais = <&spdif_port0
+			&comp_spdif_port0>;
+	};
+
+	spdif-out {
+		compatible = "linux,spdif-dit";
+		#sound-dai-cells = <0>;
+
+		port@0 {
+			spdif_tx: endpoint {
+				remote-endpoint = <&spdif_hiecout1>;
+			};
+		};
+	};
+
+	comp-spdif-out {
+		compatible = "linux,spdif-dit";
+		#sound-dai-cells = <0>;
+
+		port@0 {
+			comp_spdif_tx: endpoint {
+				remote-endpoint = <&comp_spdif_hiecout1>;
+			};
+		};
+	};
+
+	firmware {
+		optee {
+			compatible = "linaro,optee-tz";
+			method = "smc";
+		};
+	};
+};
+
+&spi3 {
+	status = "okay";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	usb-over-spi@0 {
+		compatible = "maxim,max3421-udc";
+		reg = <0>;
+		spi-max-frequency = <12500000>;
+		interrupt-parent = <&gpio>;
+		interrupt-names = "udc";
+		interrupts = <0 2>;
+	};
+};
+
+&serial0 {
+	/* Onboard USB-UART */
+	status = "okay";
+};
+
+&serial2 {
+	/* LS connector UART1 */
+	status = "okay";
+};
+
+&serial3 {
+	/* LS connector UART0 */
+	status = "okay";
+};
+
+&spdif_hiecout1 {
+	remote-endpoint = <&spdif_tx>;
+};
+
+&comp_spdif_hiecout1 {
+	remote-endpoint = <&comp_spdif_tx>;
+};
+
+&i2c0 {
+	/* LS connector I2C0 */
+	status = "okay";
+};
+
+&i2c1 {
+	/* LS connector I2C1 */
+	status = "okay";
+};
+
+&eth {
+	status = "okay";
+	phy-handle = <&ethphy>;
+};
+
+&mdio {
+	ethphy: ethernet-phy@0 {
+		reg = <0>;
+	};
+};
+
+&usb {
+	status = "okay";
+};
+
+&pcie {
+	status = "okay";
+};
+
+&gpio {
+	/* IRQs for Max3421 */
+	xirq0 {
+		gpio-hog;
+		gpios = <UNIPHIER_GPIO_IRQ(0) 1>;
+		input;
+	};
+	xirq10 {
+		gpio-hog;
+		gpios = <UNIPHIER_GPIO_IRQ(10) 1>;
+		input;
+	};
+};
+
+&pinctrl_aout1 {
+	groups = "aout1b";
+};
+
+&pinctrl_uart3 {
+	groups = "uart3", "uart3_ctsrts";
+};

+ 1 - 2
arch/arm/mach-snapdragon/pinctrl-apq8016.c

@@ -10,7 +10,7 @@
 #include <common.h>
 
 #define MAX_PIN_NAME_LEN 32
-static char pin_name[MAX_PIN_NAME_LEN];
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
 static const char * const msm_pinctrl_pins[] = {
 	"SDC1_CLK",
 	"SDC1_CMD",
@@ -59,4 +59,3 @@ struct msm_pinctrl_data apq8016_data = {
 	.get_function_mux = apq8016_get_function_mux,
 	.get_pin_name = apq8016_get_pin_name,
 };
-

+ 1 - 1
arch/arm/mach-snapdragon/pinctrl-apq8096.c

@@ -10,7 +10,7 @@
 #include <common.h>
 
 #define MAX_PIN_NAME_LEN 32
-static char pin_name[MAX_PIN_NAME_LEN];
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
 static const char * const msm_pinctrl_pins[] = {
 	"SDC1_CLK",
 	"SDC1_CMD",

+ 2 - 0
arch/arm/mach-u8500/Kconfig

@@ -8,6 +8,7 @@ choice
 
 config TARGET_STEMMY
 	bool "Samsung (stemmy) board"
+	select MISC_INIT_R
 	help
 	  The Samsung "stemmy" board supports Samsung smartphones released with
 	  the ST-Ericsson NovaThor U8500 SoC, e.g.
@@ -15,6 +16,7 @@ config TARGET_STEMMY
 	      - Samsung Galaxy S III mini (GT-I8190)	"golden"
 	      - Samsung Galaxy S Advance (GT-I9070)	"janice"
 	      - Samsung Galaxy Xcover 2 (GT-S7710)	"skomer"
+	      - Samsung Galaxy Ace 2 (GT-I8160)		"codina"
 
 	  and likely others as well (untested).
 

+ 1 - 0
board/ste/stemmy/README

@@ -7,6 +7,7 @@ the ST-Ericsson NovaThor U8500 SoC, e.g.
 	- Samsung Galaxy S III mini (GT-I8190)	"golden"
 	- Samsung Galaxy S Advance (GT-I9070)	"janice"
 	- Samsung Galaxy Xcover 2 (GT-S7710)	"skomer"
+	- Samsung Galaxy Ace 2 (GT-I8160)	"codina"
 
 and likely others as well (untested).
 

+ 148 - 1
board/ste/stemmy/stemmy.c

@@ -3,18 +3,165 @@
  * Copyright (C) 2019 Stephan Gerhold <stephan@gerhold.net>
  */
 #include <common.h>
+#include <env.h>
 #include <init.h>
+#include <log.h>
+#include <stdlib.h>
 #include <asm/global_data.h>
+#include <asm/setup.h>
+#include <asm/system.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* Parse atags provided by Samsung bootloader to get available memory */
+static ulong fw_mach __section(".data");
+static ulong fw_atags __section(".data");
+
+static const struct tag *fw_atags_copy;
+static uint fw_atags_size;
+
+void save_boot_params(ulong r0, ulong r1, ulong r2, ulong r3)
+{
+	fw_mach = r1;
+	fw_atags = r2;
+	save_boot_params_ret();
+}
+
+static const struct tag *fw_atags_get(void)
+{
+	const struct tag *tags = (const struct tag *)fw_atags;
+
+	if (tags->hdr.tag != ATAG_CORE) {
+		log_err("Invalid atags: tag 0x%x at %p\n", tags->hdr.tag, tags);
+		return NULL;
+	}
+
+	return tags;
+}
+
 int dram_init(void)
 {
-	gd->ram_size = get_ram_size(CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_SIZE);
+	const struct tag *t, *tags = fw_atags_get();
+
+	if (!tags)
+		return -EINVAL;
+
+	for_each_tag(t, tags) {
+		if (t->hdr.tag != ATAG_MEM)
+			continue;
+
+		debug("Memory: %#x-%#x (size %#x)\n", t->u.mem.start,
+		      t->u.mem.start + t->u.mem.size, t->u.mem.size);
+		gd->ram_size += t->u.mem.size;
+	}
+	return 0;
+}
+
+int dram_init_banksize(void)
+{
+	const struct tag *t, *tags = fw_atags_get();
+	unsigned int bank = 0;
+
+	if (!tags)
+		return -EINVAL;
+
+	for_each_tag(t, tags) {
+		if (t->hdr.tag != ATAG_MEM)
+			continue;
+
+		gd->bd->bi_dram[bank].start = t->u.mem.start;
+		gd->bd->bi_dram[bank].size = t->u.mem.size;
+		if (++bank == CONFIG_NR_DRAM_BANKS)
+			break;
+	}
 	return 0;
 }
 
 int board_init(void)
 {
+	gd->bd->bi_arch_number = fw_mach;
+	gd->bd->bi_boot_params = fw_atags;
 	return 0;
 }
+
+static void parse_serial(const struct tag_serialnr *serialnr)
+{
+	char serial[17];
+
+	if (env_get("serial#"))
+		return;
+
+	sprintf(serial, "%08x%08x", serialnr->high, serialnr->low);
+	env_set("serial#", serial);
+}
+
+/*
+ * The downstream/vendor kernel (provided by Samsung) uses ATAGS for booting.
+ * It also requires an extremely long cmdline provided by the primary bootloader
+ * that is not suitable for booting mainline.
+ *
+ * Since downstream is the only user of ATAGS, we emulate the behavior of the
+ * Samsung bootloader by generating only the initrd atag in U-Boot, and copying
+ * all other ATAGS as-is from the primary bootloader.
+ */
+static inline bool skip_atag(u32 tag)
+{
+	return (tag == ATAG_NONE || tag == ATAG_CORE ||
+		tag == ATAG_INITRD || tag == ATAG_INITRD2);
+}
+
+static void copy_atags(const struct tag *tags)
+{
+	const struct tag *t;
+	struct tag *copy;
+
+	if (!tags)
+		return;
+
+	/* Calculate necessary size for tags we want to copy */
+	for_each_tag(t, tags) {
+		if (skip_atag(t->hdr.tag))
+			continue;
+
+		if (t->hdr.tag == ATAG_SERIAL)
+			parse_serial(&t->u.serialnr);
+
+		fw_atags_size += t->hdr.size * sizeof(u32);
+	}
+
+	if (!fw_atags_size)
+		return;  /* No tags to copy */
+
+	copy = malloc(fw_atags_size);
+	if (!copy)
+		return;
+	fw_atags_copy = copy;
+
+	/* Copy tags */
+	for_each_tag(t, tags) {
+		if (skip_atag(t->hdr.tag))
+			continue;
+
+		memcpy(copy, t, t->hdr.size * sizeof(u32));
+		copy = tag_next(copy);
+	}
+}
+
+int misc_init_r(void)
+{
+	copy_atags(fw_atags_get());
+	return 0;
+}
+
+void setup_board_tags(struct tag **in_params)
+{
+	if (!fw_atags_copy)
+		return;
+
+	/*
+	 * fw_atags_copy contains only full "struct tag" (plus data)
+	 * so copying it bytewise here should be fine.
+	 */
+	memcpy(*in_params, fw_atags_copy, fw_atags_size);
+	*(u8 **)in_params += fw_atags_size;
+}

+ 7 - 3
common/board_info.c

@@ -31,11 +31,15 @@ int __weak show_board_info(void)
 
 		if (IS_ENABLED(CONFIG_SYSINFO)) {
 			/* This might provide more detail */
-			ret = uclass_first_device_err(UCLASS_SYSINFO, &dev);
-			if (!ret)
-				ret = sysinfo_get_str(dev,
+			ret = sysinfo_get(&dev);
+			if (!ret) {
+				ret = sysinfo_detect(dev);
+				if (!ret) {
+					ret = sysinfo_get_str(dev,
 						      SYSINFO_ID_BOARD_MODEL,
 						      sizeof(str), str);
+				}
+			}
 		}
 
 		/* Fail back to the main 'model' if available */

+ 1 - 1
configs/stemmy_defconfig

@@ -1,7 +1,7 @@
 CONFIG_ARM=y
 CONFIG_ARCH_U8500=y
 CONFIG_SYS_TEXT_BASE=0x100000
-CONFIG_NR_DRAM_BANKS=1
+CONFIG_NR_DRAM_BANKS=2
 CONFIG_DEFAULT_DEVICE_TREE="ste-ux500-samsung-stemmy"
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
 CONFIG_HUSH_PARSER=y

+ 1 - 2
configs/total_compute_defconfig

@@ -13,8 +13,7 @@ CONFIG_FIT=y
 CONFIG_FIT_SIGNATURE=y
 CONFIG_LEGACY_IMAGE_FORMAT=y
 CONFIG_BOOTDELAY=5
-CONFIG_USE_BOOTARGS=y
-CONFIG_BOOTARGS="console=ttyAMA0 debug user_debug=31 earlycon=pl011,0x7ff80000 loglevel=9 androidboot.hardware=total_compute video=640x480-32@60 androidboot.boot_devices=1c050000.mmci ip=dhcp androidboot.selinux=permissive"
+# CONFIG_USE_BOOTARGS is not set
 # CONFIG_USE_BOOTCOMMAND is not set
 # CONFIG_DISPLAY_CPUINFO is not set
 # CONFIG_DISPLAY_BOARDINFO is not set

+ 2 - 1
doc/git-mailrc

@@ -46,6 +46,7 @@ alias simongoldschmidt Simon Goldschmidt <simon.k.r.goldschmidt@gmail.com>
 alias sjg            Simon Glass <sjg@chromium.org>
 alias smcnutt        Scott McNutt <smcnutt@psyent.com>
 alias stroese        Stefan Roese <sr@denx.de>
+alias tienfong       Tien Fong Chee <tien.fong.chee@intel.com>
 alias trini          Tom Rini <trini@konsulko.com>
 alias wd             Wolfgang Denk <wd@denx.de>
 alias priyankajain   Priyanka Jain <priyanka.jain@nxp.com>
@@ -69,7 +70,7 @@ alias s3c            samsung
 alias s5pc           samsung
 alias samsung        uboot, prom
 alias snapdragon     uboot, mateusz
-alias socfpga        uboot, marex, dinh, simongoldschmidt, leyfoon
+alias socfpga        uboot, marex, dinh, simongoldschmidt, tienfong
 alias sunxi          uboot, jagan, apritzel
 alias tegra          uboot, sjg, Tom Warren <twarren@nvidia.com>, Stephen Warren <swarren@nvidia.com>
 alias tegra2         tegra

+ 2 - 0
drivers/Kconfig

@@ -80,6 +80,8 @@ source "drivers/phy/allwinner/Kconfig"
 
 source "drivers/phy/marvell/Kconfig"
 
+source "drivers/phy/socionext/Kconfig"
+
 source "drivers/pinctrl/Kconfig"
 
 source "drivers/power/Kconfig"

+ 1 - 0
drivers/Makefile

@@ -96,6 +96,7 @@ obj-$(CONFIG_PCH) += pch/
 obj-y += phy/allwinner/
 obj-y += phy/marvell/
 obj-y += phy/rockchip/
+obj-y += phy/socionext/
 obj-y += rtc/
 obj-y += scsi/
 obj-y += sound/

+ 3 - 0
drivers/clk/uniphier/clk-uniphier-sys.c

@@ -29,6 +29,7 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
 	UNIPHIER_CLK_GATE_SIMPLE(15, 0x2104, 17),	/* usb31 (Pro4, Pro5, PXs2) */
 	UNIPHIER_CLK_GATE_SIMPLE(16, 0x2104, 19),	/* usb30-phy (PXs2) */
 	UNIPHIER_CLK_GATE_SIMPLE(20, 0x2104, 20),	/* usb31-phy (PXs2) */
+	UNIPHIER_CLK_GATE_SIMPLE(24, 0x2108, 2),	/* pcie (Pro5) */
 	{ /* sentinel */ }
 #endif
 };
@@ -43,6 +44,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
 	UNIPHIER_CLK_GATE_SIMPLE(14, 0x210c, 14),	/* usb30 (LD20) */
 	UNIPHIER_CLK_GATE_SIMPLE(16, 0x210c, 12),	/* usb30-phy0 (LD20) */
 	UNIPHIER_CLK_GATE_SIMPLE(17, 0x210c, 13),	/* usb30-phy1 (LD20) */
+	UNIPHIER_CLK_GATE_SIMPLE(24, 0x210c, 4),	/* pcie */
 	{ /* sentinel */ }
 #endif
 };
@@ -62,6 +64,7 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = {
 	UNIPHIER_CLK_GATE_SIMPLE(18, 0x210c, 20),	/* usb30-phy2 */
 	UNIPHIER_CLK_GATE_SIMPLE(20, 0x210c, 17),	/* usb31-phy0 */
 	UNIPHIER_CLK_GATE_SIMPLE(21, 0x210c, 19),	/* usb31-phy1 */
+	UNIPHIER_CLK_GATE_SIMPLE(24, 0x210c, 3),	/* pcie */
 	{ /* sentinel */ }
 #endif
 };

+ 8 - 0
drivers/gpio/Kconfig

@@ -495,4 +495,12 @@ config NX_GPIO
 	  The GPIOs for a device are defined in the device tree with one node
 	  for each bank.
 
+config NOMADIK_GPIO
+	bool "Nomadik GPIO driver"
+	depends on DM_GPIO
+	help
+	  Support GPIO access on ST-Ericsson Ux500 SoCs. The GPIOs are arranged
+	  into a number of banks each with 32 GPIOs. The GPIOs for a device are
+	  defined in the device tree with one node for each bank.
+
 endmenu

+ 1 - 1
drivers/gpio/Makefile

@@ -43,7 +43,6 @@ obj-$(CONFIG_MPC8XXX_GPIO)	+= mpc8xxx_gpio.o
 obj-$(CONFIG_MPC83XX_SPISEL_BOOT)	+= mpc83xx_spisel_boot.o
 obj-$(CONFIG_SH_GPIO_PFC)	+= sh_pfc.o
 obj-$(CONFIG_OMAP_GPIO)	+= omap_gpio.o
-obj-$(CONFIG_DB8500_GPIO)	+= db8500_gpio.o
 obj-$(CONFIG_BCM2835_GPIO)	+= bcm2835_gpio.o
 obj-$(CONFIG_XILINX_GPIO)	+= xilinx_gpio.o
 obj-$(CONFIG_ADI_GPIO2)	+= adi_gpio2.o
@@ -68,3 +67,4 @@ obj-$(CONFIG_MT7621_GPIO)	+= mt7621_gpio.o
 obj-$(CONFIG_MSCC_SGPIO)	+= mscc_sgpio.o
 obj-$(CONFIG_NX_GPIO)		+= nx_gpio.o
 obj-$(CONFIG_SIFIVE_GPIO)	+= sifive-gpio.o
+obj-$(CONFIG_NOMADIK_GPIO)	+= nmk_gpio.o

+ 0 - 221
drivers/gpio/db8500_gpio.c

@@ -1,221 +0,0 @@
-/*
- * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code.
- * The purpose is that GPIO config found in kernel should work by simply
- * copy-paste it to U-Boot.
- *
- * Original Linux authors:
- * Copyright (C) 2008,2009 STMicroelectronics
- * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
- *   Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com>
- *
- * Ported to U-Boot by:
- * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com>
- *
- * 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 <asm/arch/db8500_gpio.h>
-#include <asm/arch/db8500_pincfg.h>
-#include <linux/compiler.h>
-
-#define IO_ADDR(x) (void *) (x)
-
-/*
- * The GPIO module in the db8500 Systems-on-Chip is an
- * AMBA device, managing 32 pins and alternate functions. The logic block
- * is currently only used in the db8500.
- */
-
-#define GPIO_TOTAL_PINS		268
-#define GPIO_PINS_PER_BLOCK	32
-#define GPIO_BLOCKS_COUNT	(GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1)
-#define GPIO_BLOCK(pin)		(((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1)
-#define GPIO_PIN_WITHIN_BLOCK(pin)	((pin)%(GPIO_PINS_PER_BLOCK))
-
-/* Register in the logic block */
-#define DB8500_GPIO_DAT		0x00
-#define DB8500_GPIO_DATS	0x04
-#define DB8500_GPIO_DATC	0x08
-#define DB8500_GPIO_PDIS	0x0c
-#define DB8500_GPIO_DIR		0x10
-#define DB8500_GPIO_DIRS	0x14
-#define DB8500_GPIO_DIRC	0x18
-#define DB8500_GPIO_SLPC	0x1c
-#define DB8500_GPIO_AFSLA	0x20
-#define DB8500_GPIO_AFSLB	0x24
-
-#define DB8500_GPIO_RIMSC	0x40
-#define DB8500_GPIO_FIMSC	0x44
-#define DB8500_GPIO_IS		0x48
-#define DB8500_GPIO_IC		0x4c
-#define DB8500_GPIO_RWIMSC	0x50
-#define DB8500_GPIO_FWIMSC	0x54
-#define DB8500_GPIO_WKS		0x58
-
-static void __iomem *get_gpio_addr(unsigned gpio)
-{
-	/* Our list of GPIO chips */
-	static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = {
-		IO_ADDR(CFG_GPIO_0_BASE),
-		IO_ADDR(CFG_GPIO_1_BASE),
-		IO_ADDR(CFG_GPIO_2_BASE),
-		IO_ADDR(CFG_GPIO_3_BASE),
-		IO_ADDR(CFG_GPIO_4_BASE),
-		IO_ADDR(CFG_GPIO_5_BASE),
-		IO_ADDR(CFG_GPIO_6_BASE),
-		IO_ADDR(CFG_GPIO_7_BASE),
-		IO_ADDR(CFG_GPIO_8_BASE)
-	};
-
-	return gpio_addrs[GPIO_BLOCK(gpio)];
-}
-
-static unsigned get_gpio_offset(unsigned gpio)
-{
-	return GPIO_PIN_WITHIN_BLOCK(gpio);
-}
-
-/* Can only be called from config_pin. Don't configure alt-mode directly */
-static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-	u32 bit = 1 << offset;
-	u32 afunc, bfunc;
-
-	afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit;
-	bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit;
-	if (mode & DB8500_GPIO_ALT_A)
-		afunc |= bit;
-	if (mode & DB8500_GPIO_ALT_B)
-		bfunc |= bit;
-	writel(afunc, addr + DB8500_GPIO_AFSLA);
-	writel(bfunc, addr + DB8500_GPIO_AFSLB);
-}
-
-/**
- * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio
- * @gpio: pin number
- * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP,
- *  and DB8500_GPIO_PULL_NONE
- *
- * Enables/disables pull up/down on a specified pin.  This only takes effect if
- * the pin is configured as an input (either explicitly or by the alternate
- * function).
- *
- * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is
- * configured as an input.  Otherwise, due to the way the controller registers
- * work, this function will change the value output on the pin.
- */
-void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-	u32 bit = 1 << offset;
-	u32 pdis;
-
-	pdis = readl(addr + DB8500_GPIO_PDIS);
-	if (pull == DB8500_GPIO_PULL_NONE)
-		pdis |= bit;
-	else
-		pdis &= ~bit;
-	writel(pdis, addr + DB8500_GPIO_PDIS);
-
-	if (pull == DB8500_GPIO_PULL_UP)
-		writel(bit, addr + DB8500_GPIO_DATS);
-	else if (pull == DB8500_GPIO_PULL_DOWN)
-		writel(bit, addr + DB8500_GPIO_DATC);
-}
-
-void db8500_gpio_make_input(unsigned gpio)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-
-	writel(1 << offset, addr + DB8500_GPIO_DIRC);
-}
-
-int db8500_gpio_get_input(unsigned gpio)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-	u32 bit = 1 << offset;
-
-	printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n",
-		gpio, addr, offset, bit);
-
-	return (readl(addr + DB8500_GPIO_DAT) & bit) != 0;
-}
-
-void db8500_gpio_make_output(unsigned gpio, int val)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-
-	writel(1 << offset, addr + DB8500_GPIO_DIRS);
-	db8500_gpio_set_output(gpio, val);
-}
-
-void db8500_gpio_set_output(unsigned gpio, int val)
-{
-	void __iomem *addr = get_gpio_addr(gpio);
-	unsigned offset = get_gpio_offset(gpio);
-
-	if (val)
-		writel(1 << offset, addr + DB8500_GPIO_DATS);
-	else
-		writel(1 << offset, addr + DB8500_GPIO_DATC);
-}
-
-/**
- * config_pin - configure a pin's mux attributes
- * @cfg: pin configuration
- *
- * Configures a pin's mode (alternate function or GPIO), its pull up status,
- * and its sleep mode based on the specified configuration.  The @cfg is
- * usually one of the SoC specific macros defined in mach/<soc>-pins.h.  These
- * are constructed using, and can be further enhanced with, the macros in
- * plat/pincfg.h.
- *
- * If a pin's mode is set to GPIO, it is configured as an input to avoid
- * side-effects.  The gpio can be manipulated later using standard GPIO API
- * calls.
- */
-static void config_pin(unsigned long cfg)
-{
-	int pin = PIN_NUM(cfg);
-	int pull = PIN_PULL(cfg);
-	int af = PIN_ALT(cfg);
-	int output = PIN_DIR(cfg);
-	int val = PIN_VAL(cfg);
-
-	if (output)
-		db8500_gpio_make_output(pin, val);
-	else {
-		db8500_gpio_make_input(pin);
-		db8500_gpio_set_pull(pin, pull);
-	}
-
-	gpio_set_mode(pin, af);
-}
-
-/**
- * db8500_config_pins - configure several pins at once
- * @cfgs: array of pin configurations
- * @num: number of elments in the array
- *
- * Configures several pins using config_pin(). Refer to that function for
- * further information.
- */
-void db8500_gpio_config_pins(unsigned long *cfgs, size_t num)
-{
-	size_t i;
-
-	for (i = 0; i < num; i++)
-		config_pin(cfgs[i]);
-}

+ 125 - 0
drivers/gpio/nmk_gpio.c

@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2019 Stephan Gerhold */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+struct nmk_gpio_regs {
+	u32 dat;	/* data */
+	u32 dats;	/* data set */
+	u32 datc;	/* data clear */
+	u32 pdis;	/* pull disable */
+	u32 dir;	/* direction */
+	u32 dirs;	/* direction set */
+	u32 dirc;	/* direction clear */
+	u32 slpm;	/* sleep mode */
+	u32 afsla;	/* alternate function select A */
+	u32 afslb;	/* alternate function select B */
+	u32 lowemi;	/* low EMI mode */
+};
+
+struct nmk_gpio {
+	struct nmk_gpio_regs *regs;
+};
+
+static int nmk_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+
+	return !!(readl(&priv->regs->dat) & BIT(offset));
+}
+
+static int nmk_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+
+	if (value)
+		writel(BIT(offset), &priv->regs->dats);
+	else
+		writel(BIT(offset), &priv->regs->datc);
+
+	return 0;
+}
+
+static int nmk_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+
+	if (readl(&priv->regs->afsla) & BIT(offset) ||
+	    readl(&priv->regs->afslb) & BIT(offset))
+		return GPIOF_FUNC;
+
+	if (readl(&priv->regs->dir) & BIT(offset))
+		return GPIOF_OUTPUT;
+	else
+		return GPIOF_INPUT;
+}
+
+static int nmk_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+
+	writel(BIT(offset), &priv->regs->dirc);
+	return 0;
+}
+
+static int nmk_gpio_direction_output(struct udevice *dev, unsigned offset,
+				     int value)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+
+	writel(BIT(offset), &priv->regs->dirs);
+	return nmk_gpio_set_value(dev, offset, value);
+}
+
+static const struct dm_gpio_ops nmk_gpio_ops = {
+	.get_value		= nmk_gpio_get_value,
+	.set_value		= nmk_gpio_set_value,
+	.get_function		= nmk_gpio_get_function,
+	.direction_input	= nmk_gpio_direction_input,
+	.direction_output	= nmk_gpio_direction_output,
+};
+
+static int nmk_gpio_probe(struct udevice *dev)
+{
+	struct nmk_gpio *priv = dev_get_priv(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	char buf[20];
+	u32 bank;
+	int ret;
+
+	priv->regs = dev_read_addr_ptr(dev);
+	if (!priv->regs)
+		return -EINVAL;
+
+	ret = dev_read_u32(dev, "gpio-bank", &bank);
+	if (ret < 0) {
+		printf("nmk_gpio(%s): Failed to read gpio-bank\n", dev->name);
+		return ret;
+	}
+
+	sprintf(buf, "nmk%u-gpio", bank);
+	uc_priv->bank_name = strdup(buf);
+	if (!uc_priv->bank_name)
+		return -ENOMEM;
+
+	uc_priv->gpio_count = sizeof(priv->regs->dat) * BITS_PER_BYTE;
+
+	return 0;
+}
+
+static const struct udevice_id nmk_gpio_ids[] = {
+	{ .compatible = "st,nomadik-gpio" },
+	{ }
+};
+
+U_BOOT_DRIVER(gpio_nmk) = {
+	.name		= "nmk_gpio",
+	.id		= UCLASS_GPIO,
+	.of_match	= nmk_gpio_ids,
+	.probe		= nmk_gpio_probe,
+	.ops		= &nmk_gpio_ops,
+	.priv_auto	= sizeof(struct nmk_gpio),
+};

+ 1 - 0
drivers/misc/i2c_eeprom.c

@@ -264,6 +264,7 @@ static const struct i2c_eeprom_drv_data atmel24c512_data = {
 static const struct udevice_id i2c_eeprom_std_ids[] = {
 	{ .compatible = "i2c-eeprom", (ulong)&eeprom_data },
 	{ .compatible = "microchip,24aa02e48", (ulong)&mc24aa02e48_data },
+	{ .compatible = "atmel,24c01", (ulong)&atmel24c01a_data },
 	{ .compatible = "atmel,24c01a", (ulong)&atmel24c01a_data },
 	{ .compatible = "atmel,24c02", (ulong)&atmel24c02_data },
 	{ .compatible = "atmel,24c04", (ulong)&atmel24c04_data },

+ 10 - 0
drivers/pci/Kconfig

@@ -340,4 +340,14 @@ config PCI_BRCMSTB
 	  on Broadcom set-top-box (STB) SoCs.
 	  This driver currently supports only BCM2711 SoC and RC mode
 	  of the controller.
+
+config PCIE_UNIPHIER
+	bool "Socionext UniPhier PCIe driver"
+	depends on DM_PCI
+	depends on ARCH_UNIPHIER
+	select PHY_UNIPHIER_PCIE
+	help
+	  Say Y here if you want to enable PCIe controller support on
+	  UniPhier SoCs.
+
 endif

+ 1 - 0
drivers/pci/Makefile

@@ -56,3 +56,4 @@ obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
 obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o
 obj-$(CONFIG_PCIE_OCTEON) += pcie_octeon.o
 obj-$(CONFIG_PCIE_DW_SIFIVE) += pcie_dw_sifive.o
+obj-$(CONFIG_PCIE_UNIPHIER) += pcie_uniphier.o

+ 424 - 0
drivers/pci/pcie_uniphier.c

@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pcie_uniphier.c - Socionext UniPhier PCIe driver
+ * Copyright 2019-2021 Socionext, Inc.
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <generic-phy.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <pci.h>
+#include <reset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* DBI registers */
+#define PCIE_LINK_STATUS_REG		0x0080
+#define PCIE_LINK_STATUS_WIDTH_MASK	GENMASK(25, 20)
+#define PCIE_LINK_STATUS_SPEED_MASK	GENMASK(19, 16)
+
+#define PCIE_MISC_CONTROL_1_OFF		0x08BC
+#define PCIE_DBI_RO_WR_EN		BIT(0)
+
+/* DBI iATU registers */
+#define PCIE_ATU_VIEWPORT		0x0900
+#define PCIE_ATU_REGION_INBOUND		BIT(31)
+#define PCIE_ATU_REGION_OUTBOUND	0
+#define PCIE_ATU_REGION_INDEX_MASK	GENMASK(3, 0)
+
+#define PCIE_ATU_CR1			0x0904
+#define PCIE_ATU_TYPE_MEM		0
+#define PCIE_ATU_TYPE_IO		2
+#define PCIE_ATU_TYPE_CFG0		4
+#define PCIE_ATU_TYPE_CFG1		5
+
+#define PCIE_ATU_CR2			0x0908
+#define PCIE_ATU_ENABLE			BIT(31)
+#define PCIE_ATU_MATCH_MODE		BIT(30)
+#define PCIE_ATU_BAR_NUM_MASK		GENMASK(10, 8)
+
+#define PCIE_ATU_LOWER_BASE		0x090C
+#define PCIE_ATU_UPPER_BASE		0x0910
+#define PCIE_ATU_LIMIT			0x0914
+#define PCIE_ATU_LOWER_TARGET		0x0918
+#define PCIE_ATU_BUS(x)			FIELD_PREP(GENMASK(31, 24), x)
+#define PCIE_ATU_DEV(x)			FIELD_PREP(GENMASK(23, 19), x)
+#define PCIE_ATU_FUNC(x)		FIELD_PREP(GENMASK(18, 16), x)
+#define PCIE_ATU_UPPER_TARGET		0x091C
+
+/* Link Glue registers */
+#define PCL_PINCTRL0			0x002c
+#define PCL_PERST_PLDN_REGEN		BIT(12)
+#define PCL_PERST_NOE_REGEN		BIT(11)
+#define PCL_PERST_OUT_REGEN		BIT(8)
+#define PCL_PERST_PLDN_REGVAL		BIT(4)
+#define PCL_PERST_NOE_REGVAL		BIT(3)
+#define PCL_PERST_OUT_REGVAL		BIT(0)
+
+#define PCL_MODE			0x8000
+#define PCL_MODE_REGEN			BIT(8)
+#define PCL_MODE_REGVAL			BIT(0)
+
+#define PCL_APP_READY_CTRL		0x8008
+#define PCL_APP_LTSSM_ENABLE		BIT(0)
+
+#define PCL_APP_PM0			0x8078
+#define PCL_SYS_AUX_PWR_DET		BIT(8)
+
+#define PCL_STATUS_LINK			0x8140
+#define PCL_RDLH_LINK_UP		BIT(1)
+#define PCL_XMLH_LINK_UP		BIT(0)
+
+#define LINK_UP_TIMEOUT_MS		100
+
+struct uniphier_pcie_priv {
+	void *base;
+	void *dbi_base;
+	void *cfg_base;
+	fdt_size_t cfg_size;
+	struct fdt_resource link_res;
+	struct fdt_resource dbi_res;
+	struct fdt_resource cfg_res;
+
+	struct clk clk;
+	struct reset_ctl rst;
+	struct phy phy;
+
+	struct pci_region io;
+	struct pci_region mem;
+};
+
+static int pcie_dw_get_link_speed(struct uniphier_pcie_priv *priv)
+{
+	u32 val = readl(priv->dbi_base + PCIE_LINK_STATUS_REG);
+
+	return FIELD_GET(PCIE_LINK_STATUS_SPEED_MASK, val);
+}
+
+static int pcie_dw_get_link_width(struct uniphier_pcie_priv *priv)
+{
+	u32 val = readl(priv->dbi_base + PCIE_LINK_STATUS_REG);
+
+	return FIELD_GET(PCIE_LINK_STATUS_WIDTH_MASK, val);
+}
+
+static void pcie_dw_prog_outbound_atu(struct uniphier_pcie_priv *priv,
+				      int index, int type, u64 cpu_addr,
+				      u64 pci_addr, u32 size)
+{
+	writel(PCIE_ATU_REGION_OUTBOUND
+	       | FIELD_PREP(PCIE_ATU_REGION_INDEX_MASK, index),
+	       priv->dbi_base + PCIE_ATU_VIEWPORT);
+	writel(lower_32_bits(cpu_addr),
+	       priv->dbi_base + PCIE_ATU_LOWER_BASE);
+	writel(upper_32_bits(cpu_addr),
+	       priv->dbi_base + PCIE_ATU_UPPER_BASE);
+	writel(lower_32_bits(cpu_addr + size - 1),
+	       priv->dbi_base + PCIE_ATU_LIMIT);
+	writel(lower_32_bits(pci_addr),
+	       priv->dbi_base + PCIE_ATU_LOWER_TARGET);
+	writel(upper_32_bits(pci_addr),
+	       priv->dbi_base + PCIE_ATU_UPPER_TARGET);
+
+	writel(type, priv->dbi_base + PCIE_ATU_CR1);
+	writel(PCIE_ATU_ENABLE, priv->dbi_base + PCIE_ATU_CR2);
+}
+
+static int uniphier_pcie_addr_valid(pci_dev_t bdf, int first_busno)
+{
+	/* accept only device {0,1} on first bus */
+	if ((PCI_BUS(bdf) != first_busno) || (PCI_DEV(bdf) > 1))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int uniphier_pcie_conf_address(const struct udevice *dev, pci_dev_t bdf,
+				      uint offset, void **paddr)
+{
+	struct uniphier_pcie_priv *priv = dev_get_priv(dev);
+	u32 busdev;
+	int seq = dev_seq(dev);
+	int ret;
+
+	ret = uniphier_pcie_addr_valid(bdf, seq);
+	if (ret)
+		return ret;
+
+	if ((PCI_BUS(bdf) == seq) && !PCI_DEV(bdf)) {
+		*paddr = (void *)(priv->dbi_base + offset);
+		return 0;
+	}
+
+	busdev = PCIE_ATU_BUS(PCI_BUS(bdf) - seq)
+		| PCIE_ATU_DEV(PCI_DEV(bdf))
+		| PCIE_ATU_FUNC(PCI_FUNC(bdf));
+
+	pcie_dw_prog_outbound_atu(priv, 0,
+				  PCIE_ATU_TYPE_CFG0, (u64)priv->cfg_base,
+				  busdev, priv->cfg_size);
+	*paddr = (void *)(priv->cfg_base + offset);
+
+	return 0;
+}
+
+static int uniphier_pcie_read_config(const struct udevice *dev, pci_dev_t bdf,
+				     uint offset, ulong *valp,
+				     enum pci_size_t size)
+{
+	return pci_generic_mmap_read_config(dev, uniphier_pcie_conf_address,
+					    bdf, offset, valp, size);
+}
+
+static int uniphier_pcie_write_config(struct udevice *dev, pci_dev_t bdf,
+				      uint offset, ulong val,
+				      enum pci_size_t size)
+{
+	return pci_generic_mmap_write_config(dev, uniphier_pcie_conf_address,
+					     bdf, offset, val, size);
+}
+
+static void uniphier_pcie_ltssm_enable(struct uniphier_pcie_priv *priv,
+				       bool enable)
+{
+	u32 val;
+
+	val = readl(priv->base + PCL_APP_READY_CTRL);
+	if (enable)
+		val |= PCL_APP_LTSSM_ENABLE;
+	else
+		val &= ~PCL_APP_LTSSM_ENABLE;
+	writel(val, priv->base + PCL_APP_READY_CTRL);
+}
+
+static int uniphier_pcie_link_up(struct uniphier_pcie_priv *priv)
+{
+	u32 val, mask;
+
+	val = readl(priv->base + PCL_STATUS_LINK);
+	mask = PCL_RDLH_LINK_UP | PCL_XMLH_LINK_UP;
+
+	return (val & mask) == mask;
+}
+
+static int uniphier_pcie_wait_link(struct uniphier_pcie_priv *priv)
+{
+	unsigned long timeout;
+
+	timeout = get_timer(0) + LINK_UP_TIMEOUT_MS;
+
+	while (get_timer(0) < timeout) {
+		if (uniphier_pcie_link_up(priv))
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int uniphier_pcie_establish_link(struct uniphier_pcie_priv *priv)
+{
+	if (uniphier_pcie_link_up(priv))
+		return 0;
+
+	uniphier_pcie_ltssm_enable(priv, true);
+
+	return uniphier_pcie_wait_link(priv);
+}
+
+static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv)
+{
+	u32 val;
+
+	/* set RC mode */
+	val = readl(priv->base + PCL_MODE);
+	val |= PCL_MODE_REGEN;
+	val &= ~PCL_MODE_REGVAL;
+	writel(val, priv->base + PCL_MODE);
+
+	/* use auxiliary power detection */
+	val = readl(priv->base + PCL_APP_PM0);
+	val |= PCL_SYS_AUX_PWR_DET;
+	writel(val, priv->base + PCL_APP_PM0);
+
+	/* assert PERST# */
+	val = readl(priv->base + PCL_PINCTRL0);
+	val &= ~(PCL_PERST_NOE_REGVAL | PCL_PERST_OUT_REGVAL
+		 | PCL_PERST_PLDN_REGVAL);
+	val |= PCL_PERST_NOE_REGEN | PCL_PERST_OUT_REGEN
+		| PCL_PERST_PLDN_REGEN;
+	writel(val, priv->base + PCL_PINCTRL0);
+
+	uniphier_pcie_ltssm_enable(priv, false);
+
+	mdelay(100);
+
+	/* deassert PERST# */
+	val = readl(priv->base + PCL_PINCTRL0);
+	val |= PCL_PERST_OUT_REGVAL | PCL_PERST_OUT_REGEN;
+	writel(val, priv->base + PCL_PINCTRL0);
+}
+
+static void uniphier_pcie_setup_rc(struct uniphier_pcie_priv *priv,
+				   struct pci_controller *hose)
+{
+	/* Store the IO and MEM windows settings for future use by the ATU */
+	priv->io.phys_start  = hose->regions[0].phys_start; /* IO base */
+	priv->io.bus_start   = hose->regions[0].bus_start;  /* IO_bus_addr */
+	priv->io.size	     = hose->regions[0].size;	    /* IO size */
+	priv->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
+	priv->mem.bus_start  = hose->regions[1].bus_start;  /* MEM_bus_addr */
+	priv->mem.size	     = hose->regions[1].size;	    /* MEM size */
+
+	/* outbound: IO */
+	pcie_dw_prog_outbound_atu(priv, 0,
+				  PCIE_ATU_TYPE_IO, priv->io.phys_start,
+				  priv->io.bus_start, priv->io.size);
+
+	/* outbound: MEM */
+	pcie_dw_prog_outbound_atu(priv, 1,
+				  PCIE_ATU_TYPE_MEM, priv->mem.phys_start,
+				  priv->mem.bus_start, priv->mem.size);
+}
+
+static int uniphier_pcie_probe(struct udevice *dev)
+{
+	struct uniphier_pcie_priv *priv = dev_get_priv(dev);
+	struct udevice *ctlr = pci_get_controller(dev);
+	struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+	int ret;
+
+	priv->base = map_physmem(priv->link_res.start,
+				 fdt_resource_size(&priv->link_res),
+				 MAP_NOCACHE);
+	priv->dbi_base = map_physmem(priv->dbi_res.start,
+				     fdt_resource_size(&priv->dbi_res),
+				     MAP_NOCACHE);
+	priv->cfg_size = fdt_resource_size(&priv->cfg_res);
+	priv->cfg_base = map_physmem(priv->cfg_res.start,
+				     priv->cfg_size, MAP_NOCACHE);
+
+	ret = clk_enable(&priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clk: %d\n", ret);
+		return ret;
+	}
+	ret = reset_deassert(&priv->rst);
+	if (ret) {
+		dev_err(dev, "Failed to deassert reset: %d\n", ret);
+		goto out_clk_release;
+	}
+
+	ret = generic_phy_init(&priv->phy);
+	if (ret) {
+		dev_err(dev, "Failed to initialize phy: %d\n", ret);
+		goto out_reset_release;
+	}
+
+	ret = generic_phy_power_on(&priv->phy);
+	if (ret) {
+		dev_err(dev, "Failed to power on phy: %d\n", ret);
+		goto out_phy_exit;
+	}
+
+	uniphier_pcie_init_rc(priv);
+
+	/* set DBI to read only */
+	writel(0, priv->dbi_base + PCIE_MISC_CONTROL_1_OFF);
+
+	uniphier_pcie_setup_rc(priv, hose);
+
+	if (uniphier_pcie_establish_link(priv)) {
+		printf("PCIE-%d: Link down\n", dev_seq(dev));
+	} else {
+		printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n",
+		       dev_seq(dev), pcie_dw_get_link_speed(priv),
+		       pcie_dw_get_link_width(priv), hose->first_busno);
+	}
+
+	return 0;
+
+out_phy_exit:
+	generic_phy_exit(&priv->phy);
+out_reset_release:
+	reset_release_all(&priv->rst, 1);
+out_clk_release:
+	clk_release_all(&priv->clk, 1);
+
+	return ret;
+}
+
+static int uniphier_pcie_of_to_plat(struct udevice *dev)
+{
+	struct uniphier_pcie_priv *priv = dev_get_priv(dev);
+	const void *fdt = gd->fdt_blob;
+	int node = dev_of_offset(dev);
+	int ret;
+
+	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+				     "link", &priv->link_res);
+	if (ret) {
+		dev_err(dev, "Failed to get link regs: %d\n", ret);
+		return ret;
+	}
+
+	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+				     "dbi", &priv->dbi_res);
+	if (ret) {
+		dev_err(dev, "Failed to get dbi regs: %d\n", ret);
+		return ret;
+	}
+
+	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
+				     "config", &priv->cfg_res);
+	if (ret) {
+		dev_err(dev, "Failed to get config regs: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret) {
+		dev_err(dev, "Failed to get clocks property: %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_get_by_index(dev, 0, &priv->rst);
+	if (ret) {
+		dev_err(dev, "Failed to get resets property: %d\n", ret);
+		return ret;
+	}
+
+	ret = generic_phy_get_by_index(dev, 0, &priv->phy);
+	if (ret) {
+		dev_err(dev, "Failed to get phy property: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct dm_pci_ops uniphier_pcie_ops = {
+	.read_config	= uniphier_pcie_read_config,
+	.write_config	= uniphier_pcie_write_config,
+};
+
+static const struct udevice_id uniphier_pcie_ids[] = {
+	{ .compatible = "socionext,uniphier-pcie", },
+	{ /* Sentinel */ }
+};
+
+U_BOOT_DRIVER(pcie_uniphier) = {
+	.name     = "uniphier-pcie",
+	.id       = UCLASS_PCI,
+	.of_match = uniphier_pcie_ids,
+	.probe    = uniphier_pcie_probe,
+	.ops      = &uniphier_pcie_ops,
+	.of_to_plat = uniphier_pcie_of_to_plat,
+	.priv_auto = sizeof(struct uniphier_pcie_priv),
+};

+ 6 - 0
drivers/phy/Kconfig

@@ -64,6 +64,12 @@ config MIPI_DPHY_HELPERS
 	help
 	  Provides a number of helpers a core functions for MIPI D-PHY drivers.
 
+config AB8500_USB_PHY
+	bool "AB8500 USB PHY Driver"
+	depends on PHY && PMIC_AB8500
+	help
+	  Support for the USB OTG PHY in ST-Ericsson AB8500.
+
 config BCM6318_USBH_PHY
 	bool "BCM6318 USBH PHY support"
 	depends on PHY && ARCH_BMIPS

+ 1 - 0
drivers/phy/Makefile

@@ -6,6 +6,7 @@
 obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o
 obj-$(CONFIG_$(SPL_)NOP_PHY) += nop-phy.o
 obj-$(CONFIG_MIPI_DPHY_HELPERS) += phy-core-mipi-dphy.o
+obj-$(CONFIG_AB8500_USB_PHY) += phy-ab8500-usb.o
 obj-$(CONFIG_BCM6318_USBH_PHY) += bcm6318-usbh-phy.o
 obj-$(CONFIG_BCM6348_USBH_PHY) += bcm6348-usbh-phy.o
 obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o

+ 52 - 0
drivers/phy/phy-ab8500-usb.c

@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2019 Stephan Gerhold */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <linux/bitops.h>
+#include <power/pmic.h>
+#include <power/ab8500.h>
+
+#define AB8500_USB_PHY_CTRL_REG			AB8500_USB(0x8A)
+#define AB8500_BIT_PHY_CTRL_HOST_EN		BIT(0)
+#define AB8500_BIT_PHY_CTRL_DEVICE_EN		BIT(1)
+#define AB8500_USB_PHY_CTRL_MASK		(AB8500_BIT_PHY_CTRL_HOST_EN |\
+						 AB8500_BIT_PHY_CTRL_DEVICE_EN)
+
+static int ab8500_usb_phy_power_on(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	uint set = AB8500_BIT_PHY_CTRL_DEVICE_EN;
+
+	if (CONFIG_IS_ENABLED(USB_MUSB_HOST))
+		set = AB8500_BIT_PHY_CTRL_HOST_EN;
+
+	return pmic_clrsetbits(dev->parent, AB8500_USB_PHY_CTRL_REG,
+			       AB8500_USB_PHY_CTRL_MASK, set);
+}
+
+static int ab8500_usb_phy_power_off(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+
+	return pmic_clrsetbits(dev->parent, AB8500_USB_PHY_CTRL_REG,
+			       AB8500_USB_PHY_CTRL_MASK, 0);
+}
+
+struct phy_ops ab8500_usb_phy_ops = {
+	.power_on = ab8500_usb_phy_power_on,
+	.power_off = ab8500_usb_phy_power_off,
+};
+
+static const struct udevice_id ab8500_usb_phy_ids[] = {
+	{ .compatible = "stericsson,ab8500-usb" },
+	{ }
+};
+
+U_BOOT_DRIVER(ab8500_usb_phy) = {
+	.name = "ab8500_usb_phy",
+	.id = UCLASS_PHY,
+	.of_match = ab8500_usb_phy_ids,
+	.ops = &ab8500_usb_phy_ops,
+};

+ 12 - 0
drivers/phy/socionext/Kconfig

@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# PHY drivers for Socionext platforms.
+#
+
+config PHY_UNIPHIER_PCIE
+	bool "UniPhier PCIe PHY driver"
+	depends on PHY && ARCH_UNIPHIER
+	imply REGMAP
+	help
+	  Enable this to support PHY implemented in PCIe controller
+	  on UniPhier SoCs.

+ 6 - 0
drivers/phy/socionext/Makefile

@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_PHY_UNIPHIER_PCIE)	+= phy-uniphier-pcie.o

+ 59 - 0
drivers/phy/socionext/phy-uniphier-pcie.c

@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * phy_uniphier_pcie.c - Socionext UniPhier PCIe PHY driver
+ * Copyright 2019-2021 Socionext, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <regmap.h>
+#include <syscon.h>
+
+/* SG */
+#define SG_USBPCIESEL		0x590
+#define SG_USBPCIESEL_PCIE	BIT(0)
+
+struct uniphier_pciephy_priv {
+	int dummy;
+};
+
+static int uniphier_pciephy_init(struct phy *phy)
+{
+	return 0;
+}
+
+static int uniphier_pciephy_probe(struct udevice *dev)
+{
+	struct regmap *regmap;
+
+	regmap = syscon_regmap_lookup_by_phandle(dev,
+						 "socionext,syscon");
+	if (!IS_ERR(regmap))
+		regmap_update_bits(regmap, SG_USBPCIESEL,
+				   SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
+
+	return 0;
+}
+
+static struct phy_ops uniphier_pciephy_ops = {
+	.init = uniphier_pciephy_init,
+};
+
+static const struct udevice_id uniphier_pciephy_ids[] = {
+	{ .compatible = "socionext,uniphier-pro5-pcie-phy" },
+	{ .compatible = "socionext,uniphier-ld20-pcie-phy" },
+	{ .compatible = "socionext,uniphier-pxs3-pcie-phy" },
+	{ }
+};
+
+U_BOOT_DRIVER(uniphier_pcie_phy) = {
+	.name		= "uniphier-pcie-phy",
+	.id		= UCLASS_PHY,
+	.of_match	= uniphier_pciephy_ids,
+	.ops		= &uniphier_pciephy_ops,
+	.probe		= uniphier_pciephy_probe,
+	.priv_auto      = sizeof(struct uniphier_pciephy_priv),
+};

+ 10 - 0
drivers/power/pmic/Kconfig

@@ -31,6 +31,16 @@ config SPL_PMIC_CHILDREN
 	to call your regulator code (e.g. see rk8xx.c for direct functions
 	for use in SPL).
 
+config PMIC_AB8500
+	bool "Enable driver for ST-Ericsson AB8500 PMIC via PRCMU"
+	depends on DM_PMIC
+	select REGMAP
+	select SYSCON
+	help
+	  Enable support for the ST-Ericsson AB8500 (Analog Baseband) PMIC.
+	  It connects with the ST-Ericsson DB8500 SoC via an I2C bus managed by
+	  the power/reset/clock management unit (PRCMU) firmware.
+
 config PMIC_ACT8846
 	bool "Enable support for the active-semi 8846 PMIC"
 	depends on DM_PMIC && DM_I2C

+ 1 - 0
drivers/power/pmic/Makefile

@@ -15,6 +15,7 @@ obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
 obj-$(CONFIG_$(SPL_)DM_PMIC_PCA9450) += pca9450.o
 obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
 obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
+obj-$(CONFIG_PMIC_AB8500) += ab8500.o
 obj-$(CONFIG_PMIC_ACT8846) += act8846.o
 obj-$(CONFIG_PMIC_AS3722) += as3722.o as3722_gpio.o
 obj-$(CONFIG_PMIC_MAX8997) += max8997.o

+ 268 - 0
drivers/power/pmic/ab8500.c

@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Stephan Gerhold
+ *
+ * Adapted from old U-Boot and Linux kernel implementation:
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <power/ab8500.h>
+#include <power/pmic.h>
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL		0x0fc
+#define PRCM_MBOX_CPU_SET		0x100
+#define PRCM_MBOX_CPU_CLR		0x104
+
+#define PRCM_ARM_IT1_CLR		0x48C
+#define PRCM_ARM_IT1_VAL		0x494
+
+#define PRCM_TCDM_RANGE			2
+#define PRCM_REQ_MB5			0xE44
+#define PRCM_ACK_MB5			0xDF4
+#define _PRCM_MBOX_HEADER		0xFE8
+#define PRCM_MBOX_HEADER_REQ_MB5	(_PRCM_MBOX_HEADER + 0x5)
+#define PRCMU_I2C_MBOX_BIT		BIT(5)
+
+/* Mailbox 5 Requests */
+#define PRCM_REQ_MB5_I2C_SLAVE_OP	(PRCM_REQ_MB5 + 0x0)
+#define PRCM_REQ_MB5_I2C_HW_BITS	(PRCM_REQ_MB5 + 0x1)
+#define PRCM_REQ_MB5_I2C_REG		(PRCM_REQ_MB5 + 0x2)
+#define PRCM_REQ_MB5_I2C_VAL		(PRCM_REQ_MB5 + 0x3)
+#define PRCMU_I2C(bank)			(((bank) << 1) | BIT(6))
+#define PRCMU_I2C_WRITE			0
+#define PRCMU_I2C_READ			1
+#define PRCMU_I2C_STOP_EN		BIT(3)
+
+/* Mailbox 5 ACKs */
+#define PRCM_ACK_MB5_I2C_STATUS		(PRCM_ACK_MB5 + 0x1)
+#define PRCM_ACK_MB5_I2C_VAL		(PRCM_ACK_MB5 + 0x3)
+#define PRCMU_I2C_WR_OK			0x1
+#define PRCMU_I2C_RD_OK			0x2
+
+/* AB8500 version registers */
+#define AB8500_MISC_REV_REG		AB8500_MISC(0x80)
+#define AB8500_MISC_IC_NAME_REG		AB8500_MISC(0x82)
+
+struct ab8500_priv {
+	struct ab8500 ab8500;
+	struct regmap *regmap;
+};
+
+static inline int prcmu_tcdm_readb(struct regmap *map, uint offset, u8 *valp)
+{
+	return regmap_raw_read_range(map, PRCM_TCDM_RANGE, offset,
+				     valp, sizeof(*valp));
+}
+
+static inline int prcmu_tcdm_writeb(struct regmap *map, uint offset, u8 val)
+{
+	return regmap_raw_write_range(map, PRCM_TCDM_RANGE, offset,
+				      &val, sizeof(val));
+}
+
+static int prcmu_wait_i2c_mbx_ready(struct ab8500_priv *priv)
+{
+	uint val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, PRCM_ARM_IT1_VAL, &val);
+	if (ret)
+		return ret;
+
+	if (val & PRCMU_I2C_MBOX_BIT) {
+		printf("ab8500: warning: PRCMU i2c mailbox was not acked\n");
+		/* clear mailbox 5 ack irq */
+		ret = regmap_write(priv->regmap, PRCM_ARM_IT1_CLR,
+				   PRCMU_I2C_MBOX_BIT);
+		if (ret)
+			return ret;
+	}
+
+	/* wait for on-going transaction, use 1s timeout */
+	return regmap_read_poll_timeout(priv->regmap, PRCM_MBOX_CPU_VAL, val,
+					!(val & PRCMU_I2C_MBOX_BIT), 0, 1000);
+}
+
+static int prcmu_wait_i2c_mbx_done(struct ab8500_priv *priv)
+{
+	uint val;
+	int ret;
+
+	/* set interrupt to XP70 */
+	ret = regmap_write(priv->regmap, PRCM_MBOX_CPU_SET, PRCMU_I2C_MBOX_BIT);
+	if (ret)
+		return ret;
+
+	/* wait for mailbox 5 (i2c) ack, use 1s timeout */
+	return regmap_read_poll_timeout(priv->regmap, PRCM_ARM_IT1_VAL, val,
+					(val & PRCMU_I2C_MBOX_BIT), 0, 1000);
+}
+
+static int ab8500_transfer(struct udevice *dev, uint bank_reg, u8 *val,
+			   u8 op, u8 expected_status)
+{
+	struct ab8500_priv *priv = dev_get_priv(dev);
+	u8 reg = bank_reg & 0xff;
+	u8 bank = bank_reg >> 8;
+	u8 status;
+	int ret;
+
+	ret = prcmu_wait_i2c_mbx_ready(priv);
+	if (ret)
+		return ret;
+
+	ret = prcmu_tcdm_writeb(priv->regmap, PRCM_MBOX_HEADER_REQ_MB5, 0);
+	if (ret)
+		return ret;
+	ret = prcmu_tcdm_writeb(priv->regmap, PRCM_REQ_MB5_I2C_SLAVE_OP,
+				PRCMU_I2C(bank) | op);
+	if (ret)
+		return ret;
+	ret = prcmu_tcdm_writeb(priv->regmap, PRCM_REQ_MB5_I2C_HW_BITS,
+				PRCMU_I2C_STOP_EN);
+	if (ret)
+		return ret;
+	ret = prcmu_tcdm_writeb(priv->regmap, PRCM_REQ_MB5_I2C_REG, reg);
+	if (ret)
+		return ret;
+	ret = prcmu_tcdm_writeb(priv->regmap, PRCM_REQ_MB5_I2C_VAL, *val);
+	if (ret)
+		return ret;
+
+	ret = prcmu_wait_i2c_mbx_done(priv);
+	if (ret) {
+		printf("%s: mailbox request timed out\n", __func__);
+		return ret;
+	}
+
+	/* read transfer result */
+	ret = prcmu_tcdm_readb(priv->regmap, PRCM_ACK_MB5_I2C_STATUS, &status);
+	if (ret)
+		return ret;
+	ret = prcmu_tcdm_readb(priv->regmap, PRCM_ACK_MB5_I2C_VAL, val);
+	if (ret)
+		return ret;
+
+	/*
+	 * Clear mailbox 5 ack irq. Note that the transfer is already complete
+	 * here so checking for errors does not make sense. Clearing the irq
+	 * will be retried in prcmu_wait_i2c_mbx_ready() on the next transfer.
+	 */
+	regmap_write(priv->regmap, PRCM_ARM_IT1_CLR, PRCMU_I2C_MBOX_BIT);
+
+	if (status != expected_status) {
+		/*
+		 * AB8500 does not have the AB8500_MISC_IC_NAME_REG register,
+		 * but we need to try reading it to detect AB8505.
+		 * In case of an error, assume that we have AB8500.
+		 */
+		if (op == PRCMU_I2C_READ && bank_reg == AB8500_MISC_IC_NAME_REG) {
+			*val = AB8500_VERSION_AB8500;
+			return 0;
+		}
+
+		printf("%s: return status %d\n", __func__, status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ab8500_reg_count(struct udevice *dev)
+{
+	return AB8500_NUM_REGISTERS;
+}
+
+static int ab8500_read(struct udevice *dev, uint reg, uint8_t *buf, int len)
+{
+	int ret;
+
+	if (len != 1)
+		return -EINVAL;
+
+	*buf = 0;
+	ret = ab8500_transfer(dev, reg, buf, PRCMU_I2C_READ, PRCMU_I2C_RD_OK);
+	if (ret) {
+		printf("%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ab8500_write(struct udevice *dev, uint reg, const uint8_t *buf, int len)
+{
+	int ret;
+	u8 val;
+
+	if (len != 1)
+		return -EINVAL;
+
+	val = *buf;
+	ret = ab8500_transfer(dev, reg, &val, PRCMU_I2C_WRITE, PRCMU_I2C_WR_OK);
+	if (ret) {
+		printf("%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct dm_pmic_ops ab8500_ops = {
+	.reg_count = ab8500_reg_count,
+	.read = ab8500_read,
+	.write = ab8500_write,
+};
+
+static int ab8500_probe(struct udevice *dev)
+{
+	struct ab8500_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	/* get regmap from the PRCMU parent device (syscon in U-Boot) */
+	priv->regmap = syscon_get_regmap(dev->parent);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	ret = pmic_reg_read(dev, AB8500_MISC_IC_NAME_REG);
+	if (ret < 0) {
+		printf("ab8500: failed to read chip version: %d\n", ret);
+		return ret;
+	}
+	priv->ab8500.version = ret;
+
+	ret = pmic_reg_read(dev, AB8500_MISC_REV_REG);
+	if (ret < 0) {
+		printf("ab8500: failed to read chip id: %d\n", ret);
+		return ret;
+	}
+	priv->ab8500.chip_id = ret;
+
+	debug("ab8500: version: %#x, chip id: %#x\n",
+	      priv->ab8500.version, priv->ab8500.chip_id);
+
+	return 0;
+}
+
+static const struct udevice_id ab8500_ids[] = {
+	{ .compatible = "stericsson,ab8500" },
+	{ }
+};
+
+U_BOOT_DRIVER(pmic_ab8500) = {
+	.name		= "pmic_ab8500",
+	.id		= UCLASS_PMIC,
+	.of_match	= ab8500_ids,
+	.bind		= dm_scan_fdt_dev,
+	.probe		= ab8500_probe,
+	.ops		= &ab8500_ops,
+	.priv_auto	= sizeof(struct ab8500_priv),
+};

+ 3 - 0
drivers/reset/reset-uniphier.c

@@ -50,6 +50,7 @@ static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
 	UNIPHIER_RESETX(12, 0x2000, 6),		/* GIO */
 	UNIPHIER_RESETX(14, 0x2000, 17),	/* USB30 */
 	UNIPHIER_RESETX(15, 0x2004, 17),	/* USB31 */
+	UNIPHIER_RESETX(24, 0x2008, 2),		/* PCIE */
 	UNIPHIER_RESET_END,
 };
 
@@ -79,6 +80,7 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
 	UNIPHIER_RESETX(17, 0x200c, 13),	/* USB30-PHY1 */
 	UNIPHIER_RESETX(18, 0x200c, 14),	/* USB30-PHY2 */
 	UNIPHIER_RESETX(19, 0x200c, 15),	/* USB30-PHY3 */
+	UNIPHIER_RESETX(24, 0x200c, 4),		/* PCIE */
 	UNIPHIER_RESET_END,
 };
 
@@ -95,6 +97,7 @@ static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
 	UNIPHIER_RESETX(18, 0x200c, 20),	/* USB30-PHY2 */
 	UNIPHIER_RESETX(20, 0x200c, 17),	/* USB31-PHY0 */
 	UNIPHIER_RESETX(21, 0x200c, 19),	/* USB31-PHY1 */
+	UNIPHIER_RESETX(24, 0x200c, 3),		/* PCIE */
 	UNIPHIER_RESET_END,
 };
 

+ 2 - 5
drivers/timer/nomadik-mtu-timer.c

@@ -67,14 +67,11 @@ static int nomadik_mtu_probe(struct udevice *dev)
 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 	struct nomadik_mtu_priv *priv = dev_get_priv(dev);
 	struct nomadik_mtu_regs *mtu;
-	fdt_addr_t addr;
 	u32 prescale;
 
-	addr = dev_read_addr(dev);
-	if (addr == FDT_ADDR_T_NONE)
+	mtu = dev_read_addr_ptr(dev);
+	if (!mtu)
 		return -EINVAL;
-
-	mtu = (struct nomadik_mtu_regs *)addr;
 	priv->timer = mtu->timers; /* Use first timer */
 
 	if (!uc_priv->clock_rate)

+ 10 - 1
drivers/usb/musb-new/Kconfig

@@ -72,6 +72,15 @@ config USB_MUSB_SUNXI
 	Say y here to enable support for the sunxi OTG / DRC USB controller
 	used on almost all sunxi boards.
 
+config USB_MUSB_UX500
+	bool "Enable ST-Ericsson Ux500 USB controller"
+	depends on DM_USB && DM_USB_GADGET && ARCH_U8500
+	default y
+	help
+	  Say y to enable support for the MUSB OTG USB controller used in
+	  ST-Ericsson Ux500. The driver supports either gadget or host mode
+	  based on the selection of CONFIG_USB_MUSB_HOST.
+
 config USB_MUSB_DISABLE_BULK_COMBINE_SPLIT
 	bool "Disable MUSB bulk split/combine"
 	default y
@@ -85,7 +94,7 @@ endif
 
 config USB_MUSB_PIO_ONLY
 	bool "Disable DMA (always use PIO)"
-	default y if USB_MUSB_AM35X || USB_MUSB_PIC32 || USB_MUSB_OMAP2PLUS || USB_MUSB_DSPS || USB_MUSB_SUNXI || USB_MUSB_MT85XX
+	default y if USB_MUSB_AM35X || USB_MUSB_PIC32 || USB_MUSB_OMAP2PLUS || USB_MUSB_DSPS || USB_MUSB_SUNXI || USB_MUSB_MT85XX || USB_MUSB_UX500
 	help
 	  All data is copied between memory and FIFO by the CPU.
 	  DMA controllers are ignored.

+ 1 - 0
drivers/usb/musb-new/Makefile

@@ -13,6 +13,7 @@ obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
 obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
 obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
 obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o
+obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
 
 ccflags-y := $(call cc-option,-Wno-unused-variable) \
 		$(call cc-option,-Wno-unused-but-set-variable) \

+ 1 - 1
drivers/usb/musb-new/musb_core.c

@@ -1526,7 +1526,7 @@ static int __devinit musb_core_init(u16 musb_type, struct musb *musb)
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \
-	defined(CONFIG_ARCH_OMAP4)
+	defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
 
 static irqreturn_t generic_interrupt(int irq, void *__hci)
 {

+ 179 - 0
drivers/usb/musb-new/ux500.c

@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2019 Stephan Gerhold */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <dm/device_compat.h>
+#include "musb_uboot.h"
+
+static struct musb_hdrc_config ux500_musb_hdrc_config = {
+	.multipoint	= true,
+	.dyn_fifo	= true,
+	.num_eps	= 16,
+	.ram_bits	= 16,
+};
+
+struct ux500_glue {
+	struct musb_host_data mdata;
+	struct device dev;
+	struct phy phy;
+	bool enabled;
+};
+#define to_ux500_glue(d)	container_of(d, struct ux500_glue, dev)
+
+static int ux500_musb_enable(struct musb *musb)
+{
+	struct ux500_glue *glue = to_ux500_glue(musb->controller);
+	int ret;
+
+	if (glue->enabled)
+		return 0;
+
+	ret = generic_phy_power_on(&glue->phy);
+	if (ret) {
+		printf("%s: failed to power on USB PHY\n", __func__);
+		return ret;
+	}
+
+	glue->enabled = true;
+	return 0;
+}
+
+static void ux500_musb_disable(struct musb *musb)
+{
+	struct ux500_glue *glue = to_ux500_glue(musb->controller);
+	int ret;
+
+	if (!glue->enabled)
+		return;
+
+	ret = generic_phy_power_off(&glue->phy);
+	if (ret) {
+		printf("%s: failed to power off USB PHY\n", __func__);
+		return;
+	}
+
+	glue->enabled = false;
+}
+
+static int ux500_musb_init(struct musb *musb)
+{
+	struct ux500_glue *glue = to_ux500_glue(musb->controller);
+	int ret;
+
+	ret = generic_phy_init(&glue->phy);
+	if (ret) {
+		printf("%s: failed to init USB PHY\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ux500_musb_exit(struct musb *musb)
+{
+	struct ux500_glue *glue = to_ux500_glue(musb->controller);
+	int ret;
+
+	ret = generic_phy_exit(&glue->phy);
+	if (ret) {
+		printf("%s: failed to exit USB PHY\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct musb_platform_ops ux500_musb_ops = {
+	.init		= ux500_musb_init,
+	.exit		= ux500_musb_exit,
+	.enable		= ux500_musb_enable,
+	.disable	= ux500_musb_disable,
+};
+
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+	struct ux500_glue *glue = dev_get_priv(dev);
+
+	glue->mdata.host->isr(0, glue->mdata.host);
+	return 0;
+}
+
+static int ux500_musb_probe(struct udevice *dev)
+{
+#ifdef CONFIG_USB_MUSB_HOST
+	struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+#endif
+	struct ux500_glue *glue = dev_get_priv(dev);
+	struct musb_host_data *host = &glue->mdata;
+	struct musb_hdrc_platform_data pdata;
+	void *base = dev_read_addr_ptr(dev);
+	int ret;
+
+	if (!base)
+		return -EINVAL;
+
+	ret = generic_phy_get_by_name(dev, "usb", &glue->phy);
+	if (ret) {
+		dev_err(dev, "failed to get USB PHY: %d\n", ret);
+		return ret;
+	}
+
+	memset(&pdata, 0, sizeof(pdata));
+	pdata.platform_ops = &ux500_musb_ops;
+	pdata.config = &ux500_musb_hdrc_config;
+
+#ifdef CONFIG_USB_MUSB_HOST
+	priv->desc_before_addr = true;
+	pdata.mode = MUSB_HOST;
+
+	host->host = musb_init_controller(&pdata, &glue->dev, base);
+	if (!host->host)
+		return -EIO;
+
+	return musb_lowlevel_init(host);
+#else
+	pdata.mode = MUSB_PERIPHERAL;
+	host->host = musb_init_controller(&pdata, &glue->dev, base);
+	if (!host->host)
+		return -EIO;
+
+	return usb_add_gadget_udc(&glue->dev, &host->host->g);
+#endif
+}
+
+static int ux500_musb_remove(struct udevice *dev)
+{
+	struct ux500_glue *glue = dev_get_priv(dev);
+	struct musb_host_data *host = &glue->mdata;
+
+	usb_del_gadget_udc(&host->host->g);
+	musb_stop(host->host);
+	free(host->host);
+	host->host = NULL;
+
+	return 0;
+}
+
+static const struct udevice_id ux500_musb_ids[] = {
+	{ .compatible = "stericsson,db8500-musb" },
+	{ }
+};
+
+U_BOOT_DRIVER(ux500_musb) = {
+	.name		= "ux500-musb",
+#ifdef CONFIG_USB_MUSB_HOST
+	.id		= UCLASS_USB,
+#else
+	.id		= UCLASS_USB_GADGET_GENERIC,
+#endif
+	.of_match	= ux500_musb_ids,
+	.probe		= ux500_musb_probe,
+	.remove		= ux500_musb_remove,
+#ifdef CONFIG_USB_MUSB_HOST
+	.ops		= &musb_usb_ops,
+#endif
+	.plat_auto	= sizeof(struct usb_plat),
+	.priv_auto	= sizeof(struct ux500_glue),
+};

+ 12 - 12
include/configs/stemmy.h

@@ -7,23 +7,23 @@
 
 #include <linux/sizes.h>
 
-#define CONFIG_SKIP_LOWLEVEL_INIT	/* Loaded by another bootloader */
-#define CONFIG_SYS_MALLOC_LEN		SZ_2M
+/*
+ * The "stemmy" U-Boot port is designed to be chainloaded by the Samsung
+ * bootloader on devices based on ST-Ericsson Ux500. Therefore, we skip most
+ * low-level initialization and rely on configuration provided by the Samsung
+ * bootloader. New images are loaded at the same address for compatibility.
+ */
+#define CONFIG_SKIP_LOWLEVEL_INIT
+#define CONFIG_SYS_INIT_SP_ADDR		CONFIG_SYS_TEXT_BASE
+#define CONFIG_SYS_LOAD_ADDR		CONFIG_SYS_TEXT_BASE
 
-/* Physical Memory Map */
-#define PHYS_SDRAM_1			0x00000000	/* DDR-SDRAM Bank #1 */
-#define CONFIG_SYS_SDRAM_BASE		PHYS_SDRAM_1
-#define CONFIG_SYS_SDRAM_SIZE		SZ_1G
-#define CONFIG_SYS_INIT_RAM_SIZE	0x00100000
-#define CONFIG_SYS_GBL_DATA_OFFSET	(CONFIG_SYS_SDRAM_BASE + \
-					 CONFIG_SYS_INIT_RAM_SIZE - \
-					 GENERATED_GBL_DATA_SIZE)
-#define CONFIG_SYS_INIT_SP_ADDR		CONFIG_SYS_GBL_DATA_OFFSET
+#define CONFIG_SYS_MALLOC_LEN		SZ_2M
 
 /* FIXME: This should be loaded from device tree... */
 #define CONFIG_SYS_L2_PL310
 #define CONFIG_SYS_PL310_BASE		0xa0412000
 
-#define CONFIG_SYS_LOAD_ADDR		0x00100000
+/* Generate initrd atag for downstream kernel (others are copied in stemmy.c) */
+#define CONFIG_INITRD_TAG
 
 #endif

+ 2 - 0
include/configs/uniphier.h

@@ -210,4 +210,6 @@
 
 #define CONFIG_SPL_PAD_TO			0x20000
 
+#define CONFIG_SYS_PCI_64BIT
+
 #endif /* __CONFIG_UNIPHIER_H__ */

+ 125 - 0
include/power/ab8500.h

@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Based on include/linux/mfd/abx500/ab8500.h from Linux
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
+ */
+
+#ifndef _PMIC_AB8500_H_
+#define _PMIC_AB8500_H_
+
+/*
+ * AB IC versions
+ *
+ * AB8500_VERSION_AB8500 should be 0xFF but will never be read as need a
+ * non-supported multi-byte I2C access via PRCMU. Set to 0x00 to ease the
+ * print of version string.
+ */
+enum ab8500_version {
+	AB8500_VERSION_AB8500 = 0x0,
+	AB8500_VERSION_AB8505 = 0x1,
+	AB8500_VERSION_AB9540 = 0x2,
+	AB8500_VERSION_AB8540 = 0x4,
+	AB8500_VERSION_UNDEFINED,
+};
+
+/* AB8500 CIDs*/
+#define AB8500_CUTEARLY	0x00
+#define AB8500_CUT1P0	0x10
+#define AB8500_CUT1P1	0x11
+#define AB8500_CUT1P2	0x12 /* Only valid for AB8540 */
+#define AB8500_CUT2P0	0x20
+#define AB8500_CUT3P0	0x30
+#define AB8500_CUT3P3	0x33
+
+/*
+ * AB8500 bank addresses
+ */
+#define AB8500_BANK(bank, reg)		(((bank) << 8) | (reg))
+#define AB8500_M_FSM_RANK(reg)		AB8500_BANK(0x0, reg)
+#define AB8500_SYS_CTRL1_BLOCK(reg)	AB8500_BANK(0x1, reg)
+#define AB8500_SYS_CTRL2_BLOCK(reg)	AB8500_BANK(0x2, reg)
+#define AB8500_REGU_CTRL1(reg)		AB8500_BANK(0x3, reg)
+#define AB8500_REGU_CTRL2(reg)		AB8500_BANK(0x4, reg)
+#define AB8500_USB(reg)			AB8500_BANK(0x5, reg)
+#define AB8500_TVOUT(reg)		AB8500_BANK(0x6, reg)
+#define AB8500_DBI(reg)			AB8500_BANK(0x7, reg)
+#define AB8500_ECI_AV_ACC(reg)		AB8500_BANK(0x8, reg)
+#define AB8500_RESERVED(reg)		AB8500_BANK(0x9, reg)
+#define AB8500_GPADC(reg)		AB8500_BANK(0xA, reg)
+#define AB8500_CHARGER(reg)		AB8500_BANK(0xB, reg)
+#define AB8500_GAS_GAUGE(reg)		AB8500_BANK(0xC, reg)
+#define AB8500_AUDIO(reg)		AB8500_BANK(0xD, reg)
+#define AB8500_INTERRUPT(reg)		AB8500_BANK(0xE, reg)
+#define AB8500_RTC(reg)			AB8500_BANK(0xF, reg)
+#define AB8500_GPIO(reg)		AB8500_BANK(0x10, reg)
+#define AB8500_MISC(reg)		AB8500_BANK(0x10, reg)
+#define AB8500_DEVELOPMENT(reg)		AB8500_BANK(0x11, reg)
+#define AB8500_DEBUG(reg)		AB8500_BANK(0x12, reg)
+#define AB8500_PROD_TEST(reg)		AB8500_BANK(0x13, reg)
+#define AB8500_STE_TEST(reg)		AB8500_BANK(0x14, reg)
+#define AB8500_OTP_EMUL(reg)		AB8500_BANK(0x15, reg)
+
+#define AB8500_NUM_BANKS		0x16
+#define AB8500_NUM_REGISTERS		AB8500_BANK(AB8500_NUM_BANKS, 0)
+
+struct ab8500 {
+	enum ab8500_version version;
+	u8 chip_id;
+};
+
+static inline int is_ab8500(struct ab8500 *ab)
+{
+	return ab->version == AB8500_VERSION_AB8500;
+}
+
+static inline int is_ab8505(struct ab8500 *ab)
+{
+	return ab->version == AB8500_VERSION_AB8505;
+}
+
+/* exclude also ab8505, ab9540... */
+static inline int is_ab8500_1p0_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+/* exclude also ab8505, ab9540... */
+static inline int is_ab8500_1p1_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT1P1));
+}
+
+/* exclude also ab8505, ab9540... */
+static inline int is_ab8500_2p0_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT2P0));
+}
+
+static inline int is_ab8500_3p3_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT3P3));
+}
+
+/* exclude also ab8505, ab9540... */
+static inline int is_ab8500_2p0(struct ab8500 *ab)
+{
+	return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab8505_1p0_or_earlier(struct ab8500 *ab)
+{
+	return (is_ab8505(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab8505_2p0(struct ab8500 *ab)
+{
+	return (is_ab8505(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab8505_2p0_earlier(struct ab8500 *ab)
+{
+	return (is_ab8505(ab) && (ab->chip_id < AB8500_CUT2P0));
+}
+
+#endif