diff options
Diffstat (limited to 'drivers')
48 files changed, 1388 insertions, 405 deletions
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/clk-uclass.c b/drivers/clk/clk-uclass.c index f049e36380..cea38a4c6e 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -847,13 +847,17 @@ void devm_clk_put(struct udevice *dev, struct clk *clk) int clk_uclass_post_probe(struct udevice *dev) { + int ret; + /* * when a clock provider is probed. Call clk_set_defaults() * also after the device is probed. This takes care of cases * where the DT is used to setup default parents and rates * using assigned-clocks */ - clk_set_defaults(dev, CLK_DEFAULTS_POST); + ret = clk_set_defaults(dev, CLK_DEFAULTS_POST); + if (ret) + return log_ret(ret); return 0; } diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 0c0ef366a1..48c9514ba0 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -540,6 +540,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), STM32MP1_CLK_SET_CLR_F(RCC_MP_APB3ENSETR, 13, VREF, _PCLK3), + STM32MP1_CLK_SET_CLR_F(RCC_MP_APB3ENSETR, 11, SYSCFG, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 0, LTDC_PX, _PLL4_Q), STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 4, DSI_PX, _PLL4_Q), 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/core/dump.c b/drivers/core/dump.c index f8afea30a9..f2f9cacc56 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -130,18 +130,19 @@ void dm_dump_drivers(void) struct driver *entry; struct udevice *udev; struct uclass *uc; + int ret; int i; puts("Driver uid uclass Devices\n"); puts("----------------------------------------------------------\n"); for (entry = d; entry < d + n_ents; entry++) { - uclass_get(entry->id, &uc); + ret = uclass_get(entry->id, &uc); printf("%-25.25s %-3.3d %-20.20s ", entry->name, entry->id, - uc ? uc->uc_drv->name : "<no uclass>"); + !ret ? uc->uc_drv->name : "<no uclass>"); - if (!uc) { + if (ret) { puts("\n"); continue; } diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 3206f3d112..5f98f85cfc 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -293,6 +293,7 @@ struct regmap *devm_regmap_init(struct udevice *dev, int rc; struct regmap **mapp, *map; + /* this looks like a leak, but devres takes care of it */ mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), __GFP_ZERO); if (unlikely(!mapp)) 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/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c index db5e3b0f51..beea47caa3 100644 --- a/drivers/misc/cros_ec_sandbox.c +++ b/drivers/misc/cros_ec_sandbox.c @@ -5,6 +5,8 @@ * Copyright (c) 2013 The Chromium OS Authors. */ +#define LOG_CATEGORY UCLASS_CROS_EC + #include <common.h> #include <cros_ec.h> #include <dm.h> @@ -221,11 +223,12 @@ static int keyscan_read_fdt_matrix(struct ec_state *ec, ofnode node) int len; cell = ofnode_get_property(node, "linux,keymap", &len); + if (!cell) + return log_msg_ret("prop", -EINVAL); ec->matrix_count = len / 4; ec->matrix = calloc(ec->matrix_count, sizeof(*ec->matrix)); if (!ec->matrix) { - debug("%s: Out of memory for key matrix\n", __func__); - return -1; + return log_msg_ret("mem", -ENOMEM); } /* Now read the data */ @@ -243,13 +246,12 @@ static int keyscan_read_fdt_matrix(struct ec_state *ec, ofnode node) matrix->col >= KEYBOARD_COLS) { debug("%s: Matrix pos out of range (%d,%d)\n", __func__, matrix->row, matrix->col); - return -1; + return log_msg_ret("matrix", -ERANGE); } } if (upto != ec->matrix_count) { - debug("%s: Read mismatch from key matrix\n", __func__); - return -1; + return log_msg_ret("matrix", -E2BIG); } return 0; 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/mmc/mmc.c b/drivers/mmc/mmc.c index 1e83007286..8078a89f18 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2766,7 +2766,7 @@ static int mmc_power_cycle(struct mmc *mmc) return mmc_power_on(mmc); } -int mmc_get_op_cond(struct mmc *mmc) +int mmc_get_op_cond(struct mmc *mmc, bool quiet) { bool uhs_en = supports_uhs(mmc->cfg->host_caps); int err; @@ -2842,7 +2842,8 @@ retry: if (err) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - pr_err("Card did not respond to voltage select! : %d\n", err); + if (!quiet) + pr_err("Card did not respond to voltage select! : %d\n", err); #endif return -EOPNOTSUPP; } @@ -2882,7 +2883,7 @@ int mmc_start_init(struct mmc *mmc) return -ENOMEDIUM; } - err = mmc_get_op_cond(mmc); + err = mmc_get_op_cond(mmc, false); if (!err) mmc->init_in_progress = 1; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 3503ccdb2e..178b8cf106 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -23,6 +23,10 @@ #include <asm-generic/gpio.h> #include <linux/delay.h> +#ifndef CCM_MMC_CTRL_MODE_SEL_NEW +#define CCM_MMC_CTRL_MODE_SEL_NEW 0 +#endif + struct sunxi_mmc_plat { struct mmc_config cfg; struct mmc mmc; @@ -33,7 +37,6 @@ struct sunxi_mmc_priv { uint32_t *mclkreg; unsigned fatal_err; struct gpio_desc cd_gpio; /* Change Detect GPIO */ - int cd_inverted; /* Inverted Card Detect */ struct sunxi_mmc *reg; struct mmc_config cfg; }; @@ -99,24 +102,29 @@ static int mmc_resource_init(int sdc_no) } #endif +/* + * All A64 and later MMC controllers feature auto-calibration. This would + * normally be detected via the compatible string, but we need something + * which works in the SPL as well. + */ +static bool sunxi_mmc_can_calibrate(void) +{ + return IS_ENABLED(CONFIG_MACH_SUN50I) || + IS_ENABLED(CONFIG_MACH_SUN50I_H5) || + IS_ENABLED(CONFIG_SUN50I_GEN_H6) || + IS_ENABLED(CONFIG_MACH_SUN8I_R40); +} + static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) { unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly; - bool new_mode = true; - bool calibrate = false; + bool new_mode = IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE); u32 val = 0; - if (!IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE)) - new_mode = false; - /* A83T support new mode only on eMMC */ if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T) && priv->mmc_no != 2) new_mode = false; -#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6) - calibrate = true; -#endif - if (hz <= 24000000) { pll = CCM_MMC_CTRL_OSCM24; pll_hz = 24000000; @@ -124,10 +132,14 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) #ifdef CONFIG_MACH_SUN9I pll = CCM_MMC_CTRL_PLL_PERIPH0; pll_hz = clock_get_pll4_periph0(); -#elif defined(CONFIG_SUN50I_GEN_H6) - pll = CCM_MMC_CTRL_PLL6X2; - pll_hz = clock_get_pll6() * 2; #else + /* + * SoCs since the A64 (H5, H6, H616) actually use the doubled + * rate of PLL6/PERIPH0 as an input clock, but compensate for + * that with a fixed post-divider of 2 in the mod clock. + * This cancels each other out, so for simplicity we just + * pretend it's always PLL6 without a post divider here. + */ pll = CCM_MMC_CTRL_PLL6; pll_hz = clock_get_pll6(); #endif @@ -156,33 +168,27 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) } else if (hz <= 25000000) { oclk_dly = 0; sclk_dly = 5; -#ifdef CONFIG_MACH_SUN9I - } else if (hz <= 52000000) { - oclk_dly = 5; - sclk_dly = 4; } else { - /* hz > 52000000 */ - oclk_dly = 2; - sclk_dly = 4; -#else - } else if (hz <= 52000000) { - oclk_dly = 3; - sclk_dly = 4; - } else { - /* hz > 52000000 */ - oclk_dly = 1; + if (IS_ENABLED(CONFIG_MACH_SUN9I)) { + if (hz <= 52000000) + oclk_dly = 5; + else + oclk_dly = 2; + } else { + if (hz <= 52000000) + oclk_dly = 3; + else + oclk_dly = 1; + } sclk_dly = 4; -#endif } if (new_mode) { -#ifdef CONFIG_MMC_SUNXI_HAS_NEW_MODE -#ifdef CONFIG_MMC_SUNXI_HAS_MODE_SWITCH - val = CCM_MMC_CTRL_MODE_SEL_NEW; -#endif + val |= CCM_MMC_CTRL_MODE_SEL_NEW; setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW); -#endif - } else if (!calibrate) { + } + + if (!sunxi_mmc_can_calibrate()) { /* * Use hardcoded delay values if controller doesn't support * calibration @@ -240,14 +246,15 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc) rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; writel(rval, &priv->reg->clkcr); -#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6) +#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) /* A64 supports calibration of delays on MMC controller and we * have to set delay of zero before starting calibration. * Allwinner BSP driver sets a delay only in the case of * using HS400 which is not supported by mainline U-Boot or * Linux at the moment */ - writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl); + if (sunxi_mmc_can_calibrate()) + writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl); #endif /* Re-enable Clock */ @@ -303,8 +310,9 @@ static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc, SUNXI_MMC_STATUS_FIFO_FULL; unsigned i; unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); - unsigned byte_cnt = data->blocksize * data->blocks; - unsigned timeout_msecs = byte_cnt >> 8; + unsigned word_cnt = (data->blocksize * data->blocks) >> 2; + unsigned timeout_msecs = word_cnt >> 6; + uint32_t status; unsigned long start; if (timeout_msecs < 2000) @@ -315,16 +323,38 @@ static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc, start = get_timer(0); - for (i = 0; i < (byte_cnt >> 2); i++) { - while (readl(&priv->reg->status) & status_bit) { + for (i = 0; i < word_cnt;) { + unsigned int in_fifo; + + while ((status = readl(&priv->reg->status)) & status_bit) { if (get_timer(start) > timeout_msecs) return -1; } - if (reading) - buff[i] = readl(&priv->reg->fifo); - else - writel(buff[i], &priv->reg->fifo); + /* + * For writing we do not easily know the FIFO size, so have + * to check the FIFO status after every word written. + * TODO: For optimisation we could work out a minimum FIFO + * size across all SoCs, and use that together with the current + * fill level to write chunks of words. + */ + if (!reading) { + writel(buff[i++], &priv->reg->fifo); + continue; + } + + /* + * The status register holds the current FIFO level, so we + * can be sure to collect as many words from the FIFO + * register without checking the status register after every + * read. That saves half of the costly MMIO reads, effectively + * doubling the read performance. + */ + for (in_fifo = SUNXI_MMC_STATUS_FIFO_LEVEL(status); + in_fifo > 0; + in_fifo--) + buff[i++] = readl_relaxed(&priv->reg->fifo); + dmb(); } return 0; @@ -521,10 +551,11 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = MMC_MODE_4BIT; -#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_SUN50I_GEN_H6) - if (sdc_no == 2) + + if ((IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_MACH_SUN8I) || + IS_ENABLED(CONFIG_SUN50I_GEN_H6)) && (sdc_no == 2)) cfg->host_caps = MMC_MODE_8BIT; -#endif + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; @@ -580,12 +611,21 @@ static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, static int sunxi_mmc_getcd(struct udevice *dev) { + struct mmc *mmc = mmc_get_mmc_dev(dev); struct sunxi_mmc_priv *priv = dev_get_priv(dev); + /* If polling, assume that the card is always present. */ + if ((mmc->cfg->host_caps & MMC_CAP_NONREMOVABLE) || + (mmc->cfg->host_caps & MMC_CAP_NEEDS_POLL)) + return 1; + if (dm_gpio_is_valid(&priv->cd_gpio)) { int cd_state = dm_gpio_get_value(&priv->cd_gpio); - return cd_state ^ priv->cd_inverted; + if (mmc->cfg->host_caps & MMC_CAP_CD_ACTIVE_HIGH) + return !cd_state; + else + return cd_state; } return 1; } @@ -617,31 +657,29 @@ static int sunxi_mmc_probe(struct udevice *dev) struct mmc_config *cfg = &plat->cfg; struct ofnode_phandle_args args; u32 *ccu_reg; - int bus_width, ret; + int ret; cfg->name = dev->name; - bus_width = dev_read_u32_default(dev, "bus-width", 1); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; - cfg->host_caps = 0; - if (bus_width == 8) - cfg->host_caps |= MMC_MODE_8BIT; - if (bus_width >= 4) - cfg->host_caps |= MMC_MODE_4BIT; - cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->f_min = 400000; cfg->f_max = 52000000; - priv->reg = (void *)dev_read_addr(dev); + ret = mmc_of_parse(dev, cfg); + if (ret) + return ret; + + priv->reg = dev_read_addr_ptr(dev); /* We don't have a sunxi clock driver so find the clock address here */ ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, 1, &args); if (ret) return ret; - ccu_reg = (u32 *)ofnode_get_addr(args.node); + ccu_reg = (u32 *)(uintptr_t)ofnode_get_addr(args.node); priv->mmc_no = ((uintptr_t)priv->reg - SUNXI_MMC0_BASE) / 0x1000; priv->mclkreg = (void *)ccu_reg + get_mclk_offset() + priv->mmc_no * 4; @@ -659,17 +697,13 @@ static int sunxi_mmc_probe(struct udevice *dev) return ret; /* This GPIO is optional */ - if (!dev_read_bool(dev, "non-removable") && - !gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, + if (!gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN)) { int cd_pin = gpio_get_number(&priv->cd_gpio); sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); } - /* Check if card detect is inverted */ - priv->cd_inverted = dev_read_bool(dev, "cd-inverted"); - upriv->mmc = &plat->mmc; /* Reset controller */ diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c index ce66ff781f..99eb7a3bbf 100644 --- a/drivers/net/sandbox-raw.c +++ b/drivers/net/sandbox-raw.c @@ -161,7 +161,7 @@ static int sb_eth_raw_of_to_plat(struct udevice *dev) ifname = dev_read_string(dev, "host-raw-interface"); if (ifname) { - strncpy(priv->host_ifname, ifname, IFNAMSIZ); + strlcpy(priv->host_ifname, ifname, IFNAMSIZ); printf(": Using %s from DT\n", priv->host_ifname); } if (dev_read_u32(dev, "host-raw-interface-idx", 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/pci-aardvark.c b/drivers/pci/pci-aardvark.c index 96aa039bdc..1b9bae7cca 100644 --- a/drivers/pci/pci-aardvark.c +++ b/drivers/pci/pci-aardvark.c @@ -605,25 +605,26 @@ static void pcie_advk_set_ob_region(struct pcie_advk *pcie, int *wins, /* * The n-th PCIe window is configured by tuple (match, remap, mask) - * and an access to address A uses this window it if A matches the + * and an access to address A uses this window if A matches the * match with given mask. * So every PCIe window size must be a power of two and every start * address must be aligned to window size. Minimal size is 64 KiB - * because lower 16 bits of mask must be zero. + * because lower 16 bits of mask must be zero. Remapped address + * may have set only bits from the mask. */ while (*wins < OB_WIN_COUNT && size > 0) { /* Calculate the largest aligned window size */ win_size = (1ULL << (fls64(size) - 1)) | (phys_start ? (1ULL << __ffs64(phys_start)) : 0); win_size = 1ULL << __ffs64(win_size); - if (win_size < 0x10000) + win_mask = ~(win_size - 1); + if (win_size < 0x10000 || (bus_start & ~win_mask)) break; dev_dbg(pcie->dev, "Configuring PCIe window %d: [0x%llx-0x%llx] as 0x%x\n", *wins, (u64)phys_start, (u64)phys_start + win_size, actions); - win_mask = ~(win_size - 1) & ~0xffff; pcie_advk_set_ob_win(pcie, *wins, phys_start, bus_start, win_mask, actions); diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cb9aa81835..fb12732926 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -21,6 +21,7 @@ #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP) #include <asm/fsp/fsp_support.h> #endif +#include <dt-bindings/pci/pci.h> #include <linux/delay.h> #include "pci_internal.h" @@ -164,7 +165,7 @@ int dm_pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp) } static int pci_device_matches_ids(struct udevice *dev, - struct pci_device_id *ids) + const struct pci_device_id *ids) { struct pci_child_plat *pplat; int i; @@ -181,7 +182,7 @@ static int pci_device_matches_ids(struct udevice *dev, return -EINVAL; } -int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids, +int pci_bus_find_devices(struct udevice *bus, const struct pci_device_id *ids, int *indexp, struct udevice **devp) { struct udevice *dev; @@ -201,7 +202,7 @@ int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids, return -ENODEV; } -int pci_find_device_id(struct pci_device_id *ids, int index, +int pci_find_device_id(const struct pci_device_id *ids, int index, struct udevice **devp) { struct udevice *bus; @@ -682,6 +683,34 @@ static bool pci_match_one_id(const struct pci_device_id *id, } /** + * pci_need_device_pre_reloc() - Check if a device should be bound + * + * This checks a list of vendor/device-ID values indicating devices that should + * be bound before relocation. + * + * @bus: Bus to check + * @vendor: Vendor ID to check + * @device: Device ID to check + * @return true if the vendor/device is in the list, false if not + */ +static bool pci_need_device_pre_reloc(struct udevice *bus, uint vendor, + uint device) +{ + u32 vendev; + int index; + + for (index = 0; + !dev_read_u32_index(bus, "u-boot,pci-pre-reloc", index, + &vendev); + index++) { + if (vendev == PCI_VENDEV(vendor, device)) + return true; + } + + return false; +} + +/** * pci_find_and_bind_driver() - Find and bind the right PCI driver * * This only looks at certain fields in the descriptor. @@ -769,7 +798,9 @@ static int pci_find_and_bind_driver(struct udevice *parent, * precious memory space as on some platforms as that space is pretty * limited (ie: using Cache As RAM). */ - if (!(gd->flags & GD_FLG_RELOC) && !bridge) + if (!(gd->flags & GD_FLG_RELOC) && !bridge && + !pci_need_device_pre_reloc(parent, find_id->vendor, + find_id->device)) return log_msg_ret("notbr", -EPERM); /* Bind a generic driver so that the device can be used */ 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 09cb744276..80ae1af329 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 c6ad3b1b26..0f2b63ae3c 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/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 5723c98032..82713b8381 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -313,9 +313,21 @@ static int sun4i_usb_phy_init(struct phy *phy) data->cfg->disc_thresh, PHY_DISCON_TH_LEN); } +#ifdef CONFIG_USB_MUSB_SUNXI + /* Needed for HCI and conflicts with MUSB, keep PHY0 on MUSB */ + if (usb_phy->id != 0) + sun4i_usb_phy_passby(phy, true); + + /* Route PHY0 to MUSB to allow USB gadget */ + if (data->cfg->phy0_dual_route) + sun4i_usb_phy0_reroute(data, true); +#else sun4i_usb_phy_passby(phy, true); - sun4i_usb_phy0_reroute(data, true); + /* Route PHY0 to HCI to allow USB host */ + if (data->cfg->phy0_dual_route) + sun4i_usb_phy0_reroute(data, false); +#endif return 0; } 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/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 7af6c5f0b0..cf9ad3670f 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -471,6 +471,7 @@ static int single_probe(struct udevice *dev) return -ENOMEM; #endif + /* looks like a possible divide by 0, but data->width avoids this */ priv->npins = size / (pdata->width / BITS_PER_BYTE); if (pdata->bits_per_mux) { if (!pdata->mask) { 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-uclass.c b/drivers/reset/reset-uclass.c index 8caa616ed9..c09c009130 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -325,6 +325,8 @@ struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev, bulk = devres_alloc(devm_reset_bulk_release, sizeof(struct reset_ctl_bulk), __GFP_ZERO); + + /* this looks like a leak, but devres takes care of it */ if (unlikely(!bulk)) return ERR_PTR(-ENOMEM); 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/rtc/Makefile b/drivers/rtc/Makefile index f668cf9050..331a49ab59 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_$(SPL_TPL_)DM_RTC) += rtc-uclass.o obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o -obj-y += rtc-lib.o obj-$(CONFIG_RTC_ARMADA38X) += armada38x.o obj-$(CONFIG_RTC_DAVINCI) += davinci.o obj-$(CONFIG_RTC_DS1302) += ds1302.o diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c index 0a4e12d698..8be532c3e3 100644 --- a/drivers/rtc/m41t62.c +++ b/drivers/rtc/m41t62.c @@ -213,13 +213,13 @@ static int m41t62_rtc_restart_osc(struct udevice *dev) /* 1. Set stop bit */ val |= M41T62_SEC_ST; - ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + ret = dm_i2c_write(dev, M41T62_REG_SEC, &val, sizeof(val)); if (ret) return ret; /* 2. Clear stop bit */ val &= ~M41T62_SEC_ST; - ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + ret = dm_i2c_write(dev, M41T62_REG_SEC, &val, sizeof(val)); if (ret) return ret; diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c deleted file mode 100644 index 1f7bdade29..0000000000 --- a/drivers/rtc/rtc-lib.c +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * rtc and date/time utility functions - * - * Copyright (C) 2005-06 Tower Technologies - * Author: Alessandro Zummo <a.zummo@towertech.it> - * - * U-Boot rtc_time differs from Linux rtc_time: - * - The year field takes the actual value, not year - 1900. - * - January is month 1. - */ - -#include <common.h> -#include <rtc.h> -#include <linux/math64.h> - -static const unsigned char rtc_days_in_month[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - -/* - * The number of days in the month. - */ -int rtc_month_days(unsigned int month, unsigned int year) -{ - return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); -} - -/* - * rtc_to_tm - Converts u64 to rtc_time. - * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. - * - * This function is copied from rtc_time64_to_tm() in the Linux kernel. - * But in U-Boot January is month 1 and we do not subtract 1900 from the year. - */ -void rtc_to_tm(u64 time, struct rtc_time *tm) -{ - unsigned int month, year, secs; - int days; - - days = div_u64_rem(time, 86400, &secs); - - /* day of the week, 1970-01-01 was a Thursday */ - tm->tm_wday = (days + 4) % 7; - - year = 1970 + days / 365; - days -= (year - 1970) * 365 - + LEAPS_THRU_END_OF(year - 1) - - LEAPS_THRU_END_OF(1970 - 1); - while (days < 0) { - year -= 1; - days += 365 + is_leap_year(year); - } - tm->tm_year = year; /* Not year - 1900 */ - tm->tm_yday = days + 1; - - for (month = 0; month < 11; month++) { - int newdays; - - newdays = days - rtc_month_days(month, year); - if (newdays < 0) - break; - days = newdays; - } - tm->tm_mon = month + 1; /* January = 1 */ - tm->tm_mday = days + 1; - - tm->tm_hour = secs / 3600; - secs -= tm->tm_hour * 3600; - tm->tm_min = secs / 60; - tm->tm_sec = secs - tm->tm_min * 60; - - /* Zero unused fields */ - tm->tm_isdst = 0; -} diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 961e3fb031..93348c0929 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -443,6 +443,7 @@ config DEBUG_UART_CLOCK int "UART input clock" depends on DEBUG_UART default 0 if DEBUG_UART_SANDBOX + default 0 if DEBUG_MVEBU_A3700_UART help The UART input clock determines the speed of the internal UART circuitry. The baud rate is derived from this by dividing the input diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 1cd410493b..3d49c22a9d 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -114,7 +114,7 @@ static bool ich9_can_do_33mhz(struct udevice *dev) struct ich_spi_priv *priv = dev_get_priv(dev); u32 fdod, speed; - if (!CONFIG_IS_ENABLED(PCI)) + if (!CONFIG_IS_ENABLED(PCI) || !priv->pch) return false; /* Observe SPI Descriptor Component Section 0 */ dm_pci_write_config32(priv->pch, 0xb0, 0x1000); @@ -632,7 +632,7 @@ static int ich_spi_get_basics(struct udevice *bus, bool can_probe, if (device_get_uclass_id(pch) != UCLASS_PCH) { uclass_first_device(UCLASS_PCH, &pch); if (!pch) - return log_msg_ret("uclass", -EPROTOTYPE); + ; /* ignore this error since we don't need it */ } } 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/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 76432bdec1..7a2b5a4faa 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -18,8 +18,6 @@ #include <acpi/acpi_device.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/arch/iomap.h> -#include <asm/arch/pm.h> #include <linux/delay.h> #include <dm/acpi.h> 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/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c index 4c86215bd7..d7c096923b 100644 --- a/drivers/video/pwm_backlight.c +++ b/drivers/video/pwm_backlight.c @@ -235,8 +235,10 @@ static int pwm_backlight_of_to_plat(struct udevice *dev) priv->levels = malloc(len); if (!priv->levels) return log_ret(-ENOMEM); - dev_read_u32_array(dev, "brightness-levels", priv->levels, - count); + ret = dev_read_u32_array(dev, "brightness-levels", priv->levels, + count); + if (ret) + return log_msg_ret("levels", ret); priv->num_levels = count; priv->default_level = priv->levels[index]; priv->max_level = priv->levels[count - 1]; diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c index 9e5487168c..afed81e6c6 100644 --- a/drivers/watchdog/designware_wdt.c +++ b/drivers/watchdog/designware_wdt.c @@ -22,6 +22,7 @@ struct designware_wdt_priv { void __iomem *base; unsigned int clk_khz; + struct reset_ctl_bulk *resets; }; /* @@ -95,6 +96,18 @@ static int designware_wdt_stop(struct udevice *dev) designware_wdt_reset(dev); writel(0, priv->base + DW_WDT_CR); + if (CONFIG_IS_ENABLED(DM_RESET)) { + int ret; + + ret = reset_assert_bulk(priv->resets); + if (ret) + return ret; + + ret = reset_deassert_bulk(priv->resets); + if (ret) + return ret; + } + return 0; } @@ -143,13 +156,11 @@ static int designware_wdt_probe(struct udevice *dev) #endif if (CONFIG_IS_ENABLED(DM_RESET)) { - struct reset_ctl_bulk resets; - - ret = reset_get_bulk(dev, &resets); + ret = reset_get_bulk(dev, priv->resets); if (ret) goto err; - ret = reset_deassert_bulk(&resets); + ret = reset_deassert_bulk(priv->resets); if (ret) goto err; } diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index a0c2429e5a..17334dbda6 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -53,7 +53,7 @@ int initr_watchdog(void) 4 * reset_period) / 4; } - if (!CONFIG_IS_ENABLED(WATCHDOG_AUTOSTART)) { + if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) { printf("WDT: Not starting\n"); return 0; } |