diff options
41 files changed, 1682 insertions, 254 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 79d356dd72..34ed880387 100644 --- a/MAINTAINERS +++ b/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* diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 7ce3ae6caa..708ce2d094 100644 --- a/arch/arm/dts/Makefile +++ b/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) += \ diff --git a/arch/arm/dts/uniphier-ld20-akebi96.dts b/arch/arm/dts/uniphier-ld20-akebi96.dts new file mode 100644 index 0000000000..aa159a1129 --- /dev/null +++ b/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 = ð + }; + + 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"; +}; + +ð { + status = "okay"; + phy-handle = <ðphy>; +}; + +&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"; +}; diff --git a/arch/arm/mach-snapdragon/pinctrl-apq8016.c b/arch/arm/mach-snapdragon/pinctrl-apq8016.c index 1042b564c3..70c0be0bca 100644 --- a/arch/arm/mach-snapdragon/pinctrl-apq8016.c +++ b/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, }; - diff --git a/arch/arm/mach-snapdragon/pinctrl-apq8096.c b/arch/arm/mach-snapdragon/pinctrl-apq8096.c index 20a71c319b..45462f01c2 100644 --- a/arch/arm/mach-snapdragon/pinctrl-apq8096.c +++ b/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", diff --git a/arch/arm/mach-u8500/Kconfig b/arch/arm/mach-u8500/Kconfig index 7478deb25f..db7a29a54c 100644 --- a/arch/arm/mach-u8500/Kconfig +++ b/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). diff --git a/board/ste/stemmy/README b/board/ste/stemmy/README index 81f72426f2..1b83b833c0 100644 --- a/board/ste/stemmy/README +++ b/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). diff --git a/board/ste/stemmy/stemmy.c b/board/ste/stemmy/stemmy.c index b9b2a6fddc..5f1150c0c7 100644 --- a/board/ste/stemmy/stemmy.c +++ b/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; +} diff --git a/common/board_info.c b/common/board_info.c index 1cfe34f706..e0f2d93922 100644 --- a/common/board_info.c +++ b/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 */ diff --git a/configs/stemmy_defconfig b/configs/stemmy_defconfig index 79c05acc6a..f31960b814 100644 --- a/configs/stemmy_defconfig +++ b/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 diff --git a/configs/total_compute_defconfig b/configs/total_compute_defconfig index 418f94b4bc..63f2e9c375 100644 --- a/configs/total_compute_defconfig +++ b/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 diff --git a/doc/git-mailrc b/doc/git-mailrc index 3ed38026af..001681c899 100644 --- a/doc/git-mailrc +++ b/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 diff --git a/drivers/Kconfig b/drivers/Kconfig index b1ada1cb7f..c9c812b752 100644 --- a/drivers/Kconfig +++ b/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" diff --git a/drivers/Makefile b/drivers/Makefile index 3510daba29..4081289104 100644 --- a/drivers/Makefile +++ b/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/ diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index c627a4bf85..ff5d364f59 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/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 }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index de4dc51d4b..0817b12c5f 100644 --- a/drivers/gpio/Kconfig +++ b/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 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 06dfc32fa5..16b09fb1b5 100644 --- a/drivers/gpio/Makefile +++ b/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 diff --git a/drivers/gpio/db8500_gpio.c b/drivers/gpio/db8500_gpio.c deleted file mode 100644 index eefb56d83f..0000000000 --- a/drivers/gpio/db8500_gpio.c +++ /dev/null @@ -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]); -} diff --git a/drivers/gpio/nmk_gpio.c b/drivers/gpio/nmk_gpio.c new file mode 100644 index 0000000000..e1bb41b196 --- /dev/null +++ b/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), +}; diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c index 3b249842f8..89a450d0f8 100644 --- a/drivers/misc/i2c_eeprom.c +++ b/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 }, diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 782179eb0f..517cf956ea 100644 --- a/drivers/pci/Kconfig +++ b/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 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6568dc9a08..ec8ee9dda7 100644 --- a/drivers/pci/Makefile +++ b/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 diff --git a/drivers/pci/pcie_uniphier.c b/drivers/pci/pcie_uniphier.c new file mode 100644 index 0000000000..f2edea9899 --- /dev/null +++ b/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), +}; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 008186a10d..92c74b9d0b 100644 --- a/drivers/phy/Kconfig +++ b/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 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 3c4a673a83..bf03d05d9b 100644 --- a/drivers/phy/Makefile +++ b/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 diff --git a/drivers/phy/phy-ab8500-usb.c b/drivers/phy/phy-ab8500-usb.c new file mode 100644 index 0000000000..0e04595717 --- /dev/null +++ b/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, +}; diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig new file mode 100644 index 0000000000..bcd579e98e --- /dev/null +++ b/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. diff --git a/drivers/phy/socionext/Makefile b/drivers/phy/socionext/Makefile new file mode 100644 index 0000000000..5484360b70 --- /dev/null +++ b/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 diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c new file mode 100644 index 0000000000..d352c4ca3a --- /dev/null +++ b/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), +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 583fd3ddcd..fd6648b313 100644 --- a/drivers/power/pmic/Kconfig +++ b/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 diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 89099fde57..5d1a97e5f6 100644 --- a/drivers/power/pmic/Makefile +++ b/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 diff --git a/drivers/power/pmic/ab8500.c b/drivers/power/pmic/ab8500.c new file mode 100644 index 0000000000..1f64f217c3 --- /dev/null +++ b/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), +}; diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c index 2694d130b6..c5af995b4b 100644 --- a/drivers/reset/reset-uniphier.c +++ b/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, }; diff --git a/drivers/timer/nomadik-mtu-timer.c b/drivers/timer/nomadik-mtu-timer.c index 417b419d46..4d24de14ae 100644 --- a/drivers/timer/nomadik-mtu-timer.c +++ b/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) diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index fd6f4109b0..81ceea9740 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/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. diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 6355eb12dd..396ff02654 100644 --- a/drivers/usb/musb-new/Makefile +++ b/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) \ diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index 22811a5efb..18d9bc805f 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/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) { diff --git a/drivers/usb/musb-new/ux500.c b/drivers/usb/musb-new/ux500.c new file mode 100644 index 0000000000..57c7d5630d --- /dev/null +++ b/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), +}; diff --git a/include/configs/stemmy.h b/include/configs/stemmy.h index 922eec43ee..b94ef91c2b 100644 --- a/include/configs/stemmy.h +++ b/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 diff --git a/include/configs/uniphier.h b/include/configs/uniphier.h index bad4e41372..12028e53e9 100644 --- a/include/configs/uniphier.h +++ b/include/configs/uniphier.h @@ -210,4 +210,6 @@ #define CONFIG_SPL_PAD_TO 0x20000 +#define CONFIG_SYS_PCI_64BIT + #endif /* __CONFIG_UNIPHIER_H__ */ diff --git a/include/power/ab8500.h b/include/power/ab8500.h new file mode 100644 index 0000000000..157eb4a5b1 --- /dev/null +++ b/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 |