diff options
Diffstat (limited to 'drivers')
101 files changed, 5705 insertions, 1213 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 2e03133c43..613e60235d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -50,6 +50,8 @@ source "drivers/mtd/Kconfig" source "drivers/net/Kconfig" +source "drivers/nvme/Kconfig" + source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index e4a9cb4195..dab5c182c2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -2,33 +2,35 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_$(SPL_TPL_)DM) += core/ -obj-$(CONFIG_$(SPL_)CLK) += clk/ -obj-$(CONFIG_$(SPL_)LED) += led/ -obj-$(CONFIG_$(SPL_)PHY) += phy/ -obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/ -obj-$(CONFIG_$(SPL_)RAM) += ram/ +obj-$(CONFIG_$(SPL_TPL_)CLK) += clk/ +obj-$(CONFIG_$(SPL_TPL_)DM) += core/ +obj-$(CONFIG_$(SPL_TPL_)DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ +obj-$(CONFIG_$(SPL_TPL_)I2C_SUPPORT) += i2c/ +obj-$(CONFIG_$(SPL_TPL_)LED) += led/ +obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/ +obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/ +obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/ +obj-$(CONFIG_$(SPL_TPL_)PINCTRL) += pinctrl/ +obj-$(CONFIG_$(SPL_TPL_)RAM) += ram/ +obj-$(CONFIG_$(SPL_TPL_)SERIAL_SUPPORT) += serial/ +obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/ +obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/ +obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ +ifndef CONFIG_TPL_BUILD ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_CPU_SUPPORT) += cpu/ obj-$(CONFIG_SPL_CRYPTO_SUPPORT) += crypto/ -obj-$(CONFIG_SPL_I2C_SUPPORT) += i2c/ obj-$(CONFIG_SPL_GPIO_SUPPORT) += gpio/ -obj-$(CONFIG_SPL_MMC_SUPPORT) += mmc/ obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/ obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ obj-$(CONFIG_ALTERA_SDRAM) += ddr/altera/ -obj-$(CONFIG_SPL_SERIAL_SUPPORT) += serial/ -obj-$(CONFIG_SPL_SPI_SUPPORT) += spi/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/regulator/ -obj-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd/ -obj-$(CONFIG_SPL_NAND_SUPPORT) += mtd/nand/ obj-$(CONFIG_SPL_ONENAND_SUPPORT) += mtd/onenand/ -obj-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += mtd/spi/ obj-$(CONFIG_SPL_UBI) += mtd/ubispl/ obj-$(CONFIG_SPL_DMA_SUPPORT) += dma/ obj-$(CONFIG_SPL_ETH_SUPPORT) += net/ @@ -37,7 +39,6 @@ obj-$(CONFIG_SPL_USBETH_SUPPORT) += net/phy/ obj-$(CONFIG_SPL_PCI_SUPPORT) += pci/ obj-$(CONFIG_SPL_PCH_SUPPORT) += pch/ obj-$(CONFIG_SPL_RTC_SUPPORT) += rtc/ -obj-$(CONFIG_SPL_TIMER_SUPPORT) += timer/ obj-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += usb/musb-new/ obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += usb/gadget/ obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += usb/gadget/udc/ @@ -49,18 +50,13 @@ obj-$(CONFIG_SPL_SATA_SUPPORT) += ata/ scsi/ obj-$(CONFIG_SPL_USB_HOST_SUPPORT) += block/ obj-$(CONFIG_SPL_MMC_SUPPORT) += block/ obj-$(CONFIG_SPL_FPGA_SUPPORT) += fpga/ + +endif endif ifdef CONFIG_TPL_BUILD -obj-$(CONFIG_TPL_I2C_SUPPORT) += i2c/ -obj-$(CONFIG_TPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ -obj-$(CONFIG_TPL_MMC_SUPPORT) += mmc/ obj-$(CONFIG_TPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/ -obj-$(CONFIG_TPL_NAND_SUPPORT) += mtd/nand/ -obj-$(CONFIG_TPL_SERIAL_SUPPORT) += serial/ -obj-$(CONFIG_TPL_SPI_FLASH_SUPPORT) += mtd/spi/ -obj-$(CONFIG_TPL_SPI_SUPPORT) += spi/ endif @@ -78,6 +74,7 @@ obj-y += firmware/ obj-$(CONFIG_FPGA) += fpga/ obj-y += misc/ obj-$(CONFIG_MMC) += mmc/ +obj-$(CONFIG_NVME) += nvme/ obj-y += pcmcia/ obj-y += dfu/ obj-$(CONFIG_X86) += pch/ @@ -87,7 +84,6 @@ obj-y += scsi/ obj-y += sound/ obj-y += spmi/ obj-y += sysreset/ -obj-y += timer/ obj-y += tpm/ obj-y += video/ obj-y += watchdog/ diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 23f131b7ad..a3737badec 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -22,6 +22,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = { [IF_TYPE_SATA] = "sata", [IF_TYPE_HOST] = "host", [IF_TYPE_SYSTEMACE] = "ace", + [IF_TYPE_NVME] = "nvme", }; static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = { @@ -34,6 +35,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = { [IF_TYPE_SD] = UCLASS_INVALID, [IF_TYPE_SATA] = UCLASS_AHCI, [IF_TYPE_HOST] = UCLASS_ROOT, + [IF_TYPE_NVME] = UCLASS_NVME, [IF_TYPE_SYSTEMACE] = UCLASS_INVALID, }; diff --git a/drivers/bootcount/bootcount_env.c b/drivers/bootcount/bootcount_env.c index 2d6e8db126..c3618d3a59 100644 --- a/drivers/bootcount/bootcount_env.c +++ b/drivers/bootcount/bootcount_env.c @@ -6,24 +6,25 @@ */ #include <common.h> +#include <environment.h> void bootcount_store(ulong a) { - int upgrade_available = getenv_ulong("upgrade_available", 10, 0); + int upgrade_available = env_get_ulong("upgrade_available", 10, 0); if (upgrade_available) { - setenv_ulong("bootcount", a); - saveenv(); + env_set_ulong("bootcount", a); + env_save(); } } ulong bootcount_load(void) { - int upgrade_available = getenv_ulong("upgrade_available", 10, 0); + int upgrade_available = env_get_ulong("upgrade_available", 10, 0); ulong val = 0; if (upgrade_available) - val = getenv_ulong("bootcount", 10, 0); + val = env_get_ulong("bootcount", 10, 0); return val; } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 60bd706fa2..7765148876 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -12,7 +12,7 @@ config CLK config SPL_CLK bool "Enable clock support in SPL" - depends on CLK + depends on CLK && SPL_DM help The clock subsystem adds a small amount of overhead to the image. If this is acceptable and you have a need to use clock drivers in @@ -20,6 +20,16 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +config TPL_CLK + bool "Enable clock support in TPL" + depends on CLK && TPL_DM + help + The clock subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use clock drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up clocks within TPL, and allows the same drivers to be + used as U-Boot proper. + config CLK_BCM6345 bool "Clock controller driver for BCM6345" depends on CLK && ARCH_BMIPS diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 159f285f9a..b7735933be 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o +obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o clk_fixed_rate.o obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c index 5ecf5129d8..83f4ae6ca3 100644 --- a/drivers/clk/rockchip/clk_rk3036.c +++ b/drivers/clk/rockchip/clk_rk3036.c @@ -235,7 +235,7 @@ static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate, } src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate; - return DIV_TO_RATE(src_rate, div); + return DIV_TO_RATE(src_rate, div) / 2; } static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate, @@ -247,10 +247,11 @@ static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate, debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); /* mmc clock auto divide 2 in internal */ - src_clk_div = (clk_general_rate / 2 + freq - 1) / freq; + src_clk_div = DIV_ROUND_UP(clk_general_rate / 2, freq); - if (src_clk_div > 0x7f) { - src_clk_div = (OSC_HZ / 2 + freq - 1) / freq; + if (src_clk_div > 128) { + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq); + assert(src_clk_div - 1 < 128); mux = EMMC_SEL_24M; } else { mux = EMMC_SEL_GPLL; diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c index 6f30332878..8c2c9bc1d8 100644 --- a/drivers/clk/rockchip/clk_rk3188.c +++ b/drivers/clk/rockchip/clk_rk3188.c @@ -71,9 +71,6 @@ enum { SOCSTS_GPLL_LOCK = 1 << 8, }; -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _nr, _no) {\ @@ -287,7 +284,7 @@ static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate, return -EINVAL; } - return DIV_TO_RATE(gclk_rate, div); + return DIV_TO_RATE(gclk_rate, div) / 2; } static ulong rockchip_mmc_set_clk(struct rk3188_cru *cru, uint gclk_rate, @@ -296,7 +293,8 @@ static ulong rockchip_mmc_set_clk(struct rk3188_cru *cru, uint gclk_rate, int src_clk_div; debug("%s: gclk_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq) - 1; assert(src_clk_div <= 0x3f); switch (periph) { @@ -350,8 +348,9 @@ static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate, static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate, int periph, uint freq) { - int src_clk_div = RATE_TO_DIV(gclk_rate, freq); + int src_clk_div = DIV_ROUND_UP(gclk_rate, freq) - 1; + assert(src_clk_div < 128); switch (periph) { case SCLK_SPI0: assert(src_clk_div <= SPI0_DIV_MASK); @@ -400,8 +399,8 @@ static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf, * reparent aclk_cpu_pre from apll to gpll * set up dependent divisors for PCLK/HCLK and ACLK clocks. */ - aclk_div = RATE_TO_DIV(GPLL_HZ, CPU_ACLK_HZ); - assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + aclk_div = DIV_ROUND_UP(GPLL_HZ, CPU_ACLK_HZ) - 1; + assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f); rk_clrsetreg(&cru->cru_clksel_con[0], CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT | diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c index fdeb816e23..d7f6a3c313 100644 --- a/drivers/clk/rockchip/clk_rk322x.c +++ b/drivers/clk/rockchip/clk_rk322x.c @@ -26,9 +26,6 @@ enum { OUTPUT_MIN_HZ = 24 * 1000000, }; -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ @@ -239,7 +236,7 @@ static ulong rockchip_mmc_get_clk(struct rk322x_cru *cru, uint clk_general_rate, } src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate; - return DIV_TO_RATE(src_rate, div); + return DIV_TO_RATE(src_rate, div) / 2; } static ulong rockchip_mmc_set_clk(struct rk322x_cru *cru, uint clk_general_rate, @@ -250,11 +247,12 @@ static ulong rockchip_mmc_set_clk(struct rk322x_cru *cru, uint clk_general_rate, debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); - /* mmc clock auto divide 2 in internal */ - src_clk_div = (clk_general_rate / 2 + freq - 1) / freq; + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(clk_general_rate / 2, freq); - if (src_clk_div > 0x7f) { - src_clk_div = (OSC_HZ / 2 + freq - 1) / freq; + if (src_clk_div > 128) { + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq); + assert(src_clk_div - 1 < 128); mux = EMMC_SEL_24M; } else { mux = EMMC_SEL_GPLL; diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index 792ee76509..478195b10b 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -118,9 +118,6 @@ enum { SOCSTS_NPLL_LOCK = 1 << 9, }; -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _nr, _no) {\ @@ -530,10 +527,12 @@ static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, int mux; debug("%s: gclk_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); + /* mmc clock default div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq); if (src_clk_div > 0x3f) { - src_clk_div = RATE_TO_DIV(OSC_HZ, freq); + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq); + assert(src_clk_div < 0x40); mux = EMMC_PLL_SELECT_24MHZ; assert((int)EMMC_PLL_SELECT_24MHZ == (int)MMC0_PLL_SELECT_24MHZ); @@ -607,7 +606,8 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate, int src_clk_div; debug("%s: clk_general_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); + src_clk_div = DIV_ROUND_UP(gclk_rate, freq) - 1; + assert(src_clk_div < 128); switch (periph) { case SCLK_SPI0: rk_clrsetreg(&cru->cru_clksel_con[25], diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c index 2065a8a65b..c3a6650de0 100644 --- a/drivers/clk/rockchip/clk_rk3328.c +++ b/drivers/clk/rockchip/clk_rk3328.c @@ -412,9 +412,9 @@ static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id) if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT == CLK_EMMC_PLL_SEL_24M) - return DIV_TO_RATE(OSC_HZ, div); + return DIV_TO_RATE(OSC_HZ, div) / 2; else - return DIV_TO_RATE(GPLL_HZ, div); + return DIV_TO_RATE(GPLL_HZ, div) / 2; } static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru, @@ -436,11 +436,12 @@ static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru, return -EINVAL; } /* Select clk_sdmmc/emmc source from GPLL by default */ - src_clk_div = GPLL_HZ / set_rate; + /* mmc clock defaulg div 2 internal, need provide double in cru */ + src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate); if (src_clk_div > 127) { /* use 24MHz source for 400KHz clock */ - src_clk_div = OSC_HZ / set_rate; + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); rk_clrsetreg(&cru->clksel_con[con_id], CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT | diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index e1d9aeb8e5..2be1f572d7 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -1,13 +1,16 @@ /* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * Author: Andy Yan <andy.yan@rock-chips.com> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH * SPDX-License-Identifier: GPL-2.0 */ #include <common.h> #include <clk-uclass.h> #include <dm.h> +#include <dt-structs.h> #include <errno.h> +#include <mapmem.h> #include <syscon.h> #include <asm/arch/clock.h> #include <asm/arch/cru_rk3368.h> @@ -18,6 +21,12 @@ DECLARE_GLOBAL_DATA_PTR; +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct rk3368_clk_plat { + struct dtd_rockchip_rk3368_cru dtd; +}; +#endif + struct pll_div { u32 nr; u32 nf; @@ -30,9 +39,6 @@ struct pll_div { #define GPLL_HZ (576 * 1000 * 1000) #define CPLL_HZ (400 * 1000 * 1000) -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _nr, _no) { \ @@ -41,10 +47,16 @@ struct pll_div { (_nr * _no) == hz, #hz "Hz cannot be hit with PLL " \ "divisors on line " __stringify(__LINE__)); +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static const struct pll_div apll_l_init_cfg = PLL_DIVISORS(APLL_L_HZ, 12, 2); static const struct pll_div apll_b_init_cfg = PLL_DIVISORS(APLL_B_HZ, 1, 2); +#if !defined(CONFIG_TPL_BUILD) static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 2); static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6); +#endif +#endif + +static ulong rk3368_clk_get_rate(struct clk *clk); /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, @@ -73,8 +85,9 @@ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, } } +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, - const struct pll_div *div, bool has_bwadj) + const struct pll_div *div) { struct rk3368_pll *pll = &cru->pll[pll_id]; /* All PLLs have same VCO and output frequency range restrictions*/ @@ -92,6 +105,12 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, ((div->nr - 1) << PLL_NR_SHIFT) | ((div->no - 1) << PLL_OD_SHIFT)); writel((div->nf - 1) << PLL_NF_SHIFT, &pll->con1); + /* + * BWADJ should be set to NF / 2 to ensure the nominal bandwidth. + * Compare the RK3368 TRM, section "3.6.4 PLL Bandwidth Adjustment". + */ + clrsetbits_le32(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); + udelay(10); /* return from reset */ @@ -106,15 +125,23 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, return 0; } +#endif +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static void rkclk_init(struct rk3368_cru *cru) { u32 apllb, aplll, dpll, cpll, gpll; - rkclk_set_pll(cru, APLLB, &apll_b_init_cfg, false); - rkclk_set_pll(cru, APLLL, &apll_l_init_cfg, false); - rkclk_set_pll(cru, GPLL, &gpll_init_cfg, false); - rkclk_set_pll(cru, CPLL, &cpll_init_cfg, false); + rkclk_set_pll(cru, APLLB, &apll_b_init_cfg); + rkclk_set_pll(cru, APLLL, &apll_l_init_cfg); +#if !defined(CONFIG_TPL_BUILD) + /* + * If we plan to return to the boot ROM, we can't increase the + * GPLL rate from the SPL stage. + */ + rkclk_set_pll(cru, GPLL, &gpll_init_cfg); + rkclk_set_pll(cru, CPLL, &cpll_init_cfg); +#endif apllb = rkclk_pll_get_rate(cru, APLLB); aplll = rkclk_pll_get_rate(cru, APLLL); @@ -125,17 +152,19 @@ static void rkclk_init(struct rk3368_cru *cru) debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n", __func__, apllb, aplll, dpll, cpll, gpll); } +#endif +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) { u32 div, con, con_id, rate; u32 pll_rate; switch (clk_id) { - case SCLK_SDMMC: + case HCLK_SDMMC: con_id = 50; break; - case SCLK_EMMC: + case HCLK_EMMC: con_id = 51; break; case SCLK_SDIO0: @@ -146,7 +175,7 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) } con = readl(&cru->clksel_con[con_id]); - switch ((con & MMC_PLL_SEL_MASK) >> MMC_PLL_SEL_SHIFT) { + switch (con & MMC_PLL_SEL_MASK) { case MMC_PLL_SEL_GPLL: pll_rate = rkclk_pll_get_rate(cru, GPLL); break; @@ -154,6 +183,8 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) pll_rate = OSC_HZ; break; case MMC_PLL_SEL_CPLL: + pll_rate = rkclk_pll_get_rate(cru, CPLL); + break; case MMC_PLL_SEL_USBPHY_480M: default: return -EINVAL; @@ -161,23 +192,76 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) div = (con & MMC_CLK_DIV_MASK) >> MMC_CLK_DIV_SHIFT; rate = DIV_TO_RATE(pll_rate, div); + debug("%s: raw rate %d (post-divide by 2)\n", __func__, rate); return rate >> 1; } -static ulong rk3368_mmc_set_clk(struct rk3368_cru *cru, - ulong clk_id, ulong rate) +static ulong rk3368_mmc_find_best_rate_and_parent(struct clk *clk, + ulong rate, + u32 *best_mux, + u32 *best_div) +{ + int i; + ulong best_rate = 0; + const ulong MHz = 1000000; + const struct { + u32 mux; + ulong rate; + } parents[] = { + { .mux = MMC_PLL_SEL_CPLL, .rate = CPLL_HZ }, + { .mux = MMC_PLL_SEL_GPLL, .rate = GPLL_HZ }, + { .mux = MMC_PLL_SEL_24M, .rate = 24 * MHz } + }; + + debug("%s: target rate %ld\n", __func__, rate); + for (i = 0; i < ARRAY_SIZE(parents); ++i) { + /* + * Find the largest rate no larger than the target-rate for + * the current parent. + */ + ulong parent_rate = parents[i].rate; + u32 div = DIV_ROUND_UP(parent_rate, rate); + u32 adj_div = div; + ulong new_rate = parent_rate / adj_div; + + debug("%s: rate %ld, parent-mux %d, parent-rate %ld, div %d\n", + __func__, rate, parents[i].mux, parents[i].rate, div); + + /* Skip, if not representable */ + if ((div - 1) > MMC_CLK_DIV_MASK) + continue; + + /* Skip, if we already have a better (or equal) solution */ + if (new_rate <= best_rate) + continue; + + /* This is our new best rate. */ + best_rate = new_rate; + *best_mux = parents[i].mux; + *best_div = div - 1; + } + + debug("%s: best_mux = %x, best_div = %d, best_rate = %ld\n", + __func__, *best_mux, *best_div, best_rate); + + return best_rate; +} + +static ulong rk3368_mmc_set_clk(struct clk *clk, ulong rate) { - u32 div; - u32 con_id; - u32 gpll_rate = rkclk_pll_get_rate(cru, GPLL); + struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3368_cru *cru = priv->cru; + ulong clk_id = clk->id; + u32 con_id, mux = 0, div = 0; - div = RATE_TO_DIV(gpll_rate, rate << 1); + /* Find the best parent and rate */ + rk3368_mmc_find_best_rate_and_parent(clk, rate << 1, &mux, &div); switch (clk_id) { - case SCLK_SDMMC: + case HCLK_SDMMC: con_id = 50; break; - case SCLK_EMMC: + case HCLK_EMMC: con_id = 51; break; case SCLK_SDIO0: @@ -187,33 +271,154 @@ static ulong rk3368_mmc_set_clk(struct rk3368_cru *cru, return -EINVAL; } - if (div > 0x3f) { - div = RATE_TO_DIV(OSC_HZ, rate); - rk_clrsetreg(&cru->clksel_con[con_id], - MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, - (MMC_PLL_SEL_24M << MMC_PLL_SEL_SHIFT) | - (div << MMC_CLK_DIV_SHIFT)); - } else { - rk_clrsetreg(&cru->clksel_con[con_id], - MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, - (MMC_PLL_SEL_GPLL << MMC_PLL_SEL_SHIFT) | - div << MMC_CLK_DIV_SHIFT); - } + rk_clrsetreg(&cru->clksel_con[con_id], + MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, + mux | div); return rk3368_mmc_get_clk(cru, clk_id); } +#endif + +#if IS_ENABLED(CONFIG_TPL_BUILD) +static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) +{ + const struct pll_div *dpll_cfg = NULL; + const ulong MHz = 1000000; + + /* Fout = ((Fin /NR) * NF )/ NO */ + static const struct pll_div dpll_1200 = PLL_DIVISORS(1200 * MHz, 1, 1); + static const struct pll_div dpll_1332 = PLL_DIVISORS(1332 * MHz, 2, 1); + static const struct pll_div dpll_1600 = PLL_DIVISORS(1600 * MHz, 3, 2); + + switch (set_rate) { + case 1200*MHz: + dpll_cfg = &dpll_1200; + break; + case 1332*MHz: + dpll_cfg = &dpll_1332; + break; + case 1600*MHz: + dpll_cfg = &dpll_1600; + break; + default: + error("Unsupported SDRAM frequency!,%ld\n", set_rate); + } + rkclk_set_pll(cru, DPLL, dpll_cfg); + + return set_rate; +} +#endif + +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) +static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, + ulong clk_id, ulong set_rate) +{ + /* + * This models the 'assigned-clock-parents = <&ext_gmac>' from + * the DTS and switches to the 'ext_gmac' clock parent. + */ + rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK); + return set_rate; +} +#endif + +/* + * RK3368 SPI clocks have a common divider-width (7 bits) and a single bit + * to select either CPLL or GPLL as the clock-parent. The location within + * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable. + */ + +struct spi_clkreg { + uint8_t reg; /* CLKSEL_CON[reg] register in CRU */ + uint8_t div_shift; + uint8_t sel_shift; +}; + +/* + * The entries are numbered relative to their offset from SCLK_SPI0. + */ +static const struct spi_clkreg spi_clkregs[] = { + [0] = { .reg = 45, .div_shift = 0, .sel_shift = 7, }, + [1] = { .reg = 45, .div_shift = 8, .sel_shift = 15, }, + [2] = { .reg = 46, .div_shift = 8, .sel_shift = 15, }, +}; + +static inline u32 extract_bits(u32 val, unsigned width, unsigned shift) +{ + return (val >> shift) & ((1 << width) - 1); +} + +static ulong rk3368_spi_get_clk(struct rk3368_cru *cru, ulong clk_id) +{ + const struct spi_clkreg *spiclk = NULL; + u32 div, val; + + switch (clk_id) { + case SCLK_SPI0 ... SCLK_SPI2: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + val = readl(&cru->clksel_con[spiclk->reg]); + div = extract_bits(val, 7, spiclk->div_shift); + + debug("%s: div 0x%x\n", __func__, div); + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rk3368_spi_set_clk(struct rk3368_cru *cru, ulong clk_id, uint hz) +{ + const struct spi_clkreg *spiclk = NULL; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz); + assert(src_clk_div < 127); + + switch (clk_id) { + case SCLK_SPI0 ... SCLK_SPI2: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[spiclk->reg], + ((0x7f << spiclk->div_shift) | + (0x1 << spiclk->sel_shift)), + ((src_clk_div << spiclk->div_shift) | + (1 << spiclk->sel_shift))); + + return rk3368_spi_get_clk(cru, clk_id); +} static ulong rk3368_clk_get_rate(struct clk *clk) { struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); ulong rate = 0; - debug("%s id:%ld\n", __func__, clk->id); + debug("%s: id %ld\n", __func__, clk->id); switch (clk->id) { + case PLL_CPLL: + rate = rkclk_pll_get_rate(priv->cru, CPLL); + break; + case PLL_GPLL: + rate = rkclk_pll_get_rate(priv->cru, GPLL); + break; + case SCLK_SPI0 ... SCLK_SPI2: + rate = rk3368_spi_get_clk(priv->cru, clk->id); + break; +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) case HCLK_SDMMC: case HCLK_EMMC: rate = rk3368_mmc_get_clk(priv->cru, clk->id); break; +#endif default: return -ENOENT; } @@ -223,15 +428,31 @@ static ulong rk3368_clk_get_rate(struct clk *clk) static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { - struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); + __maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); ulong ret = 0; debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate); switch (clk->id) { - case SCLK_SDMMC: - case SCLK_EMMC: - ret = rk3368_mmc_set_clk(priv->cru, clk->id, rate); + case SCLK_SPI0 ... SCLK_SPI2: + ret = rk3368_spi_set_clk(priv->cru, clk->id, rate); break; +#if IS_ENABLED(CONFIG_TPL_BUILD) + case CLK_DDR: + ret = rk3368_ddr_set_clk(priv->cru, rate); + break; +#endif +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) + case HCLK_SDMMC: + case HCLK_EMMC: + ret = rk3368_mmc_set_clk(clk, rate); + break; +#endif +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) + case SCLK_MAC: + /* select the external clock */ + ret = rk3368_gmac_set_clk(priv->cru, clk->id, rate); + break; +#endif default: return -ENOENT; } @@ -246,18 +467,26 @@ static struct clk_ops rk3368_clk_ops = { static int rk3368_clk_probe(struct udevice *dev) { - struct rk3368_clk_priv *priv = dev_get_priv(dev); + struct rk3368_clk_priv __maybe_unused *priv = dev_get_priv(dev); +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3368_clk_plat *plat = dev_get_platdata(dev); + priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]); +#endif +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) rkclk_init(priv->cru); +#endif return 0; } static int rk3368_clk_ofdata_to_platdata(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3368_clk_priv *priv = dev_get_priv(dev); priv->cru = (struct rk3368_cru *)devfdt_get_addr(dev); +#endif return 0; } @@ -284,6 +513,9 @@ U_BOOT_DRIVER(rockchip_rk3368_cru) = { .id = UCLASS_CLK, .of_match = rk3368_clk_ids, .priv_auto_alloc_size = sizeof(struct rk3368_clk_priv), +#if CONFIG_IS_ENABLED(OF_PLATDATA) + .platdata_auto_alloc_size = sizeof(struct rk3368_clk_plat), +#endif .ofdata_to_platdata = rk3368_clk_ofdata_to_platdata, .ops = &rk3368_clk_ops, .bind = rk3368_clk_bind, diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index 53d2a3f85d..3edafea140 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -676,8 +676,8 @@ static ulong rk3399_spi_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz) const struct spi_clkreg *spiclk = NULL; int src_clk_div; - src_clk_div = RATE_TO_DIV(GPLL_HZ, hz); - assert(src_clk_div < 127); + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; + assert(src_clk_div < 128); switch (clk_id) { case SCLK_SPI1 ... SCLK_SPI5: @@ -750,18 +750,21 @@ static ulong rk3399_mmc_get_clk(struct rk3399_cru *cru, uint clk_id) case HCLK_SDMMC: case SCLK_SDMMC: con = readl(&cru->clksel_con[16]); + /* dwmmc controller have internal div 2 */ + div = 2; break; case SCLK_EMMC: con = readl(&cru->clksel_con[21]); + div = 1; break; default: return -EINVAL; } - div = (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT; + div *= (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT; if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT == CLK_EMMC_PLL_SEL_24M) - return DIV_TO_RATE(24*1000*1000, div); + return DIV_TO_RATE(OSC_HZ, div); else return DIV_TO_RATE(GPLL_HZ, div); } @@ -776,11 +779,13 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, case HCLK_SDMMC: case SCLK_SDMMC: /* Select clk_sdmmc source from GPLL by default */ - src_clk_div = GPLL_HZ / set_rate; + /* mmc clock defaulg div 2 internal, provide double in cru */ + src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate); - if (src_clk_div > 127) { + if (src_clk_div > 128) { /* use 24MHz source for 400KHz clock */ - src_clk_div = 24*1000*1000 / set_rate; + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); + assert(src_clk_div - 1 < 128); rk_clrsetreg(&cru->clksel_con[16], CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT | @@ -794,8 +799,8 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, break; case SCLK_EMMC: /* Select aclk_emmc source from GPLL */ - src_clk_div = GPLL_HZ / aclk_emmc; - assert(src_clk_div - 1 < 31); + src_clk_div = DIV_ROUND_UP(GPLL_HZ , aclk_emmc); + assert(src_clk_div - 1 < 32); rk_clrsetreg(&cru->clksel_con[21], ACLK_EMMC_PLL_SEL_MASK | ACLK_EMMC_DIV_CON_MASK, @@ -803,8 +808,8 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, (src_clk_div - 1) << ACLK_EMMC_DIV_CON_SHIFT); /* Select clk_emmc source from GPLL too */ - src_clk_div = GPLL_HZ / set_rate; - assert(src_clk_div - 1 < 127); + src_clk_div = DIV_ROUND_UP(GPLL_HZ, set_rate); + assert(src_clk_div - 1 < 128); rk_clrsetreg(&cru->clksel_con[22], CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index 818293dfe8..cf966bbdc3 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -25,9 +25,6 @@ enum { OUTPUT_MIN_HZ = 24 * 1000000, }; -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index f8b19a48cc..7afef1f9a3 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -97,7 +97,17 @@ config REGMAP config SPL_REGMAP bool "Support register maps in SPL" - depends on DM + depends on SPL_DM + help + Hardware peripherals tend to have one or more sets of registers + which can be accessed to control the hardware. A register map + models this with a simple read/write interface. It can in principle + support any bus type (I2C, SPI) but so far this only supports + direct memory access. + +config TPL_REGMAP + bool "Support register maps in TPL" + depends on TPL_DM help Hardware peripherals tend to have one or more sets of registers which can be accessed to control the hardware. A register map @@ -116,7 +126,16 @@ config SYSCON config SPL_SYSCON bool "Support system controllers in SPL" - depends on REGMAP + depends on SPL_REGMAP + help + Many SoCs have a number of system controllers which are dealt with + as a group by a single driver. Some common functionality is provided + by this uclass, including accessing registers via regmap and + assigning a unique number to each. + +config TPL_SYSCON + bool "Support system controllers in TPL" + depends on TPL_REGMAP help Many SoCs have a number of system controllers which are dealt with as a group by a single driver. Some common functionality is provided diff --git a/drivers/core/Makefile b/drivers/core/Makefile index fd2d4de0c8..3d68c70b57 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -9,8 +9,8 @@ obj-$(CONFIG_DEVRES) += devres.o obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE) += device-remove.o obj-$(CONFIG_$(SPL_)SIMPLE_BUS) += simple-bus.o obj-$(CONFIG_DM) += dump.o -obj-$(CONFIG_$(SPL_)REGMAP) += regmap.o -obj-$(CONFIG_$(SPL_)SYSCON) += syscon-uclass.o +obj-$(CONFIG_$(SPL_TPL_)REGMAP) += regmap.o +obj-$(CONFIG_$(SPL_TPL_)SYSCON) += syscon-uclass.o obj-$(CONFIG_OF_LIVE) += of_access.o of_addr.o ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index 3349fc5c3b..058c9b9da8 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -469,7 +469,7 @@ step2: #define CTLR_INTLV_MASK 0x20000000 /* Perform build-in test on memory. Three-way interleaving is not yet * supported by this code. */ - if (getenv_f("ddr_bist", buffer, CONFIG_SYS_CBSIZE) >= 0) { + if (env_get_f("ddr_bist", buffer, CONFIG_SYS_CBSIZE) >= 0) { puts("Running BIST test. This will take a while..."); cs0_config = ddr_in32(&ddr->cs0_config); cs0_bnds = ddr_in32(&ddr->cs0_bnds); diff --git a/drivers/ddr/fsl/interactive.c b/drivers/ddr/fsl/interactive.c index 653bbabc95..c99bd2fb6d 100644 --- a/drivers/ddr/fsl/interactive.c +++ b/drivers/ddr/fsl/interactive.c @@ -1861,7 +1861,7 @@ int fsl_ddr_interactive_env_var_exists(void) { char buffer[CONFIG_SYS_CBSIZE]; - if (getenv_f("ddr_interactive", buffer, CONFIG_SYS_CBSIZE) >= 0) + if (env_get_f("ddr_interactive", buffer, CONFIG_SYS_CBSIZE) >= 0) return 1; return 0; @@ -1891,11 +1891,11 @@ unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo, int var_is_set) }; if (var_is_set) { - if (getenv_f("ddr_interactive", buffer2, CONFIG_SYS_CBSIZE) > 0) { + if (env_get_f("ddr_interactive", buffer2, + CONFIG_SYS_CBSIZE) > 0) p = buffer2; - } else { + else var_is_set = 0; - } } /* diff --git a/drivers/ddr/fsl/options.c b/drivers/ddr/fsl/options.c index 20edd2dc28..a7eaed1bd7 100644 --- a/drivers/ddr/fsl/options.c +++ b/drivers/ddr/fsl/options.c @@ -18,7 +18,7 @@ * Use our own stack based buffer before relocation to allow accessing longer * hwconfig strings that might be in the environment before we've relocated. * This is pretty fragile on both the use of stack and if the buffer is big - * enough. However we will get a warning from getenv_f for the later. + * enough. However we will get a warning from env_get_f() for the latter. */ /* Board-specific functions defined in each board's ddr.c */ @@ -755,7 +755,7 @@ unsigned int populate_memctl_options(const common_timing_params_t *common_dimm, * Extract hwconfig from environment since we have not properly setup * the environment but need it for ddr config params */ - if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0) + if (env_get_f("hwconfig", buffer, sizeof(buffer)) > 0) buf = buffer; #if defined(CONFIG_SYS_FSL_DDR3) || \ @@ -1399,7 +1399,7 @@ int fsl_use_spd(void) * Extract hwconfig from environment since we have not properly setup * the environment but need it for ddr config params */ - if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0) + if (env_get_f("hwconfig", buffer, sizeof(buffer)) > 0) buf = buffer; /* if hwconfig is not enabled, or "sdram" is not defined, use spd */ diff --git a/drivers/ddr/marvell/a38x/ddr3_debug.c b/drivers/ddr/marvell/a38x/ddr3_debug.c index 12b5b04109..a704a3e9d3 100644 --- a/drivers/ddr/marvell/a38x/ddr3_debug.c +++ b/drivers/ddr/marvell/a38x/ddr3_debug.c @@ -327,8 +327,6 @@ int ddr3_tip_print_log(u32 dev_num, u32 mem_addr) u32 if_id = 0; struct hws_topology_map *tm = ddr3_get_topology_map(); - mem_addr = mem_addr; - #ifndef EXCLUDE_SWITCH_DEBUG if ((is_validate_window_per_if != 0) || (is_validate_window_per_pup != 0)) { @@ -820,7 +818,6 @@ static int ddr3_tip_access_atr(u32 dev_num, u32 flag_id, u32 value, u32 **ptr) u32 tmp_val = 0, if_id = 0, pup_id = 0; struct hws_topology_map *tm = ddr3_get_topology_map(); - dev_num = dev_num; *ptr = NULL; switch (flag_id) { @@ -1169,8 +1166,6 @@ int print_adll(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]) u32 i, j; struct hws_topology_map *tm = ddr3_get_topology_map(); - dev_num = dev_num; - for (j = 0; j < tm->num_of_bus_per_interface; j++) { VALIDATE_ACTIVE(tm->bus_act_mask, j); for (i = 0; i < MAX_INTERFACE_NUM; i++) { @@ -1229,8 +1224,6 @@ int ddr3_tip_sweep_test(u32 dev_num, u32 test_type, u32 reg_addr = 0; struct hws_topology_map *tm = ddr3_get_topology_map(); - mem_addr = mem_addr; - if (test_type == 0) { reg_addr = 1; ui_mask_bit = 0x3f; @@ -1301,8 +1294,6 @@ int ddr3_tip_run_sweep_test(int dev_num, u32 repeat_num, u32 direction, u32 max_cs = hws_ddr3_tip_max_cs_get(); struct hws_topology_map *tm = ddr3_get_topology_map(); - repeat_num = repeat_num; - if (mode == 1) { /* per pup */ start_pup = 0; diff --git a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c index 9d216da96d..2909ae3c6f 100644 --- a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c +++ b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c @@ -697,8 +697,6 @@ int ddr3_tip_print_centralization_result(u32 dev_num) u32 if_id = 0, bus_id = 0; struct hws_topology_map *tm = ddr3_get_topology_map(); - dev_num = dev_num; - printf("Centralization Results\n"); printf("I/F0 Result[0 - success 1-fail 2 - state_2 3 - state_3] ...\n"); for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index f6281f4baa..ff732ac309 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -62,7 +62,7 @@ int dfu_init_env_entities(char *interface, char *devstr) #ifdef CONFIG_SET_DFU_ALT_INFO set_dfu_alt_info(interface, devstr); #endif - str_env = getenv("dfu_alt_info"); + str_env = env_get("dfu_alt_info"); if (!str_env) { error("\"dfu_alt_info\" env variable not defined!\n"); return -EINVAL; @@ -101,7 +101,7 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) if (dfu_buf != NULL) return dfu_buf; - s = getenv("dfu_bufsiz"); + s = env_get("dfu_bufsiz"); if (s) dfu_buf_size = (unsigned long)simple_strtol(s, NULL, 0); @@ -123,7 +123,7 @@ static char *dfu_get_hash_algo(void) { char *s; - s = getenv("dfu_hash_algo"); + s = env_get("dfu_hash_algo"); if (!s) return NULL; diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index bb23e7fdcb..39e10b1a5a 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -161,7 +161,7 @@ static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, } if (op != DFU_OP_WRITE) { - str_env = getenv("filesize"); + str_env = env_get("filesize"); if (str_env == NULL) { puts("dfu: Wrong file size!\n"); return -1; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 63951e0dbe..ffeda9425a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -109,6 +109,15 @@ config OMAP_GPIO Support GPIO controllers on the TI OMAP3/4/5 and related (such as AM335x/AM43xx/AM57xx/DRA7xx/etc) families of SoCs. +config CMD_PCA953X + bool "Enable the pca953x command" + help + Deprecated: This should be converted to driver model. + + This command provides access to a pca953x GPIO device using the + legacy GPIO interface. Several subcommands are provided which mirror + the standard 'gpio' command. It should use that instead. + config PM8916_GPIO bool "Qualcomm PM8916 PMIC GPIO/keypad driver" depends on DM_GPIO && PMIC_PM8916 @@ -156,6 +165,15 @@ config SANDBOX_GPIO_COUNT of 'anonymous' GPIOs that do not belong to any device or bank. Select a suitable value depending on your needs. +config CMD_TCA642X + bool "tca642x - Command to access tca642x state" + help + DEPRECATED - This needs conversion to driver model + + This provides a way to looking at the pin state of this device. + This mirrors the 'gpio' command and that should be used in preference + to custom code. + config TEGRA_GPIO bool "Tegra20..210 GPIO driver" depends on DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8937e99b47..1396467ab6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,7 +45,6 @@ obj-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o obj-$(CONFIG_ADI_GPIO2) += adi_gpio2.o obj-$(CONFIG_TCA642X) += tca642x.o -oby-$(CONFIG_SX151X) += sx151x.o obj-$(CONFIG_SUNXI_GPIO) += sunxi_gpio.o obj-$(CONFIG_LPC32XX_GPIO) += lpc32xx_gpio.o obj-$(CONFIG_STM32_GPIO) += stm32_gpio.o diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 238e02805c..d1c1ae1411 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -143,7 +143,6 @@ int pca953x_get_val(uint8_t chip) } #ifdef CONFIG_CMD_PCA953X -#ifdef CONFIG_CMD_PCA953X_INFO /* * Display pca953x information */ @@ -193,16 +192,13 @@ static int pca953x_info(uint8_t chip) return 0; } -#endif /* CONFIG_CMD_PCA953X_INFO */ cmd_tbl_t cmd_pca953x[] = { U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""), U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""), U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""), U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""), -#ifdef CONFIG_CMD_PCA953X_INFO U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""), -#endif }; int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -231,13 +227,11 @@ int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1; switch ((long)c->cmd) { -#ifdef CONFIG_CMD_PCA953X_INFO case PCA953X_CMD_INFO: ret = pca953x_info(chip); if (ret) ret = CMD_RET_FAILURE; break; -#endif case PCA953X_CMD_DEVICE: if (argc == 3) @@ -287,10 +281,8 @@ U_BOOT_CMD( "pca953x gpio access", "device [dev]\n" " - show or set current device address\n" -#ifdef CONFIG_CMD_PCA953X_INFO "pca953x info\n" " - display info for current chip\n" -#endif "pca953x output pin 0|1\n" " - set pin as output and drive low or high\n" "pca953x invert pin 0|1\n" diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c index ff245db91d..c04cef4cb9 100644 --- a/drivers/gpio/stm32_gpio.c +++ b/drivers/gpio/stm32_gpio.c @@ -19,7 +19,6 @@ DECLARE_GLOBAL_DATA_PTR; -#if defined(CONFIG_STM32F4) || defined(CONFIG_STM32F7) static const unsigned long io_base[] = { STM32_GPIOA_BASE, STM32_GPIOB_BASE, STM32_GPIOC_BASE, STM32_GPIOD_BASE, STM32_GPIOE_BASE, STM32_GPIOF_BASE, @@ -74,81 +73,6 @@ int stm32_gpio_config(const struct stm32_gpio_dsc *dsc, out: return rv; } -#elif defined(CONFIG_STM32F1) -static const unsigned long io_base[] = { - STM32_GPIOA_BASE, STM32_GPIOB_BASE, STM32_GPIOC_BASE, - STM32_GPIOD_BASE, STM32_GPIOE_BASE, STM32_GPIOF_BASE, - STM32_GPIOG_BASE -}; - -#define STM32_GPIO_CR_MODE_MASK 0x3 -#define STM32_GPIO_CR_MODE_SHIFT(p) (p * 4) -#define STM32_GPIO_CR_CNF_MASK 0x3 -#define STM32_GPIO_CR_CNF_SHIFT(p) (p * 4 + 2) - -struct stm32_gpio_regs { - u32 crl; /* GPIO port configuration low */ - u32 crh; /* GPIO port configuration high */ - u32 idr; /* GPIO port input data */ - u32 odr; /* GPIO port output data */ - u32 bsrr; /* GPIO port bit set/reset */ - u32 brr; /* GPIO port bit reset */ - u32 lckr; /* GPIO port configuration lock */ -}; - -#define CHECK_DSC(x) (!x || x->port > 6 || x->pin > 15) -#define CHECK_CTL(x) (!x || x->mode > 3 || x->icnf > 3 || x->ocnf > 3 || \ - x->pupd > 1) - -int stm32_gpio_config(const struct stm32_gpio_dsc *dsc, - const struct stm32_gpio_ctl *ctl) -{ - struct stm32_gpio_regs *gpio_regs; - u32 *cr; - int p, crp; - int rv; - - if (CHECK_DSC(dsc)) { - rv = -EINVAL; - goto out; - } - if (CHECK_CTL(ctl)) { - rv = -EINVAL; - goto out; - } - - p = dsc->pin; - - gpio_regs = (struct stm32_gpio_regs *)io_base[dsc->port]; - - if (p < 8) { - cr = &gpio_regs->crl; - crp = p; - } else { - cr = &gpio_regs->crh; - crp = p - 8; - } - - clrbits_le32(cr, 0x3 << STM32_GPIO_CR_MODE_SHIFT(crp)); - setbits_le32(cr, ctl->mode << STM32_GPIO_CR_MODE_SHIFT(crp)); - - clrbits_le32(cr, 0x3 << STM32_GPIO_CR_CNF_SHIFT(crp)); - /* Inputs set the optional pull up / pull down */ - if (ctl->mode == STM32_GPIO_MODE_IN) { - setbits_le32(cr, ctl->icnf << STM32_GPIO_CR_CNF_SHIFT(crp)); - clrbits_le32(&gpio_regs->odr, 0x1 << p); - setbits_le32(&gpio_regs->odr, ctl->pupd << p); - } else { - setbits_le32(cr, ctl->ocnf << STM32_GPIO_CR_CNF_SHIFT(crp)); - } - - rv = 0; -out: - return rv; -} -#else -#error STM32 family not supported -#endif int stm32_gpout_set(const struct stm32_gpio_dsc *dsc, int state) { @@ -207,20 +131,11 @@ int gpio_direction_input(unsigned gpio) dsc.port = stm32_gpio_to_port(gpio); dsc.pin = stm32_gpio_to_pin(gpio); -#if defined(CONFIG_STM32F4) || defined(CONFIG_STM32F7) ctl.af = STM32_GPIO_AF0; ctl.mode = STM32_GPIO_MODE_IN; ctl.otype = STM32_GPIO_OTYPE_PP; ctl.pupd = STM32_GPIO_PUPD_NO; ctl.speed = STM32_GPIO_SPEED_50M; -#elif defined(CONFIG_STM32F1) - ctl.mode = STM32_GPIO_MODE_IN; - ctl.icnf = STM32_GPIO_ICNF_IN_FLT; - ctl.ocnf = STM32_GPIO_OCNF_GP_PP; /* ignored for input */ - ctl.pupd = STM32_GPIO_PUPD_UP; /* ignored for floating */ -#else -#error STM32 family not supported -#endif return stm32_gpio_config(&dsc, &ctl); } @@ -233,19 +148,10 @@ int gpio_direction_output(unsigned gpio, int value) dsc.port = stm32_gpio_to_port(gpio); dsc.pin = stm32_gpio_to_pin(gpio); -#if defined(CONFIG_STM32F4) || defined(CONFIG_STM32F7) ctl.af = STM32_GPIO_AF0; ctl.mode = STM32_GPIO_MODE_OUT; ctl.pupd = STM32_GPIO_PUPD_NO; ctl.speed = STM32_GPIO_SPEED_50M; -#elif defined(CONFIG_STM32F1) - ctl.mode = STM32_GPIO_MODE_OUT_50M; - ctl.ocnf = STM32_GPIO_OCNF_GP_PP; - ctl.icnf = STM32_GPIO_ICNF_IN_FLT; /* ignored for output */ - ctl.pupd = STM32_GPIO_PUPD_UP; /* ignored for output */ -#else -#error STM32 family not supported -#endif res = stm32_gpio_config(&dsc, &ctl); if (res < 0) diff --git a/drivers/gpio/sx151x.c b/drivers/gpio/sx151x.c deleted file mode 100644 index 167cf40c71..0000000000 --- a/drivers/gpio/sx151x.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * (C) Copyright 2013 - * Viktar Palstsiuk, Promwad, viktar.palstsiuk@promwad.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Driver for Semtech SX151x SPI GPIO Expanders - */ - -#include <common.h> -#include <spi.h> -#include <sx151x.h> - -#ifndef CONFIG_SX151X_SPI_BUS -#define CONFIG_SX151X_SPI_BUS 0 -#endif - -/* - * The SX151x registers - */ - -#ifdef CONFIG_SX151X_GPIO_COUNT_8 -/* 8bit: SX1511 */ -#define SX151X_REG_DIR 0x07 -#define SX151X_REG_DATA 0x08 -#else -/* 16bit: SX1512 */ -#define SX151X_REG_DIR 0x0F -#define SX151X_REG_DATA 0x11 -#endif -#define SX151X_REG_RESET 0x7D - -static int sx151x_spi_write(int chip, unsigned char reg, unsigned char val) -{ - struct spi_slave *slave; - unsigned char buf[2]; - int ret; - - slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, - SPI_MODE_0); - if (!slave) - return 0; - - spi_claim_bus(slave); - - buf[0] = reg; - buf[1] = val; - - ret = spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); - if (ret < 0) - printf("spi%d.%d write fail: can't write %02x to %02x: %d\n", - CONFIG_SX151X_SPI_BUS, chip, val, reg, ret); - else - printf("spi%d.%d write 0x%02x to register 0x%02x\n", - CONFIG_SX151X_SPI_BUS, chip, val, reg); - spi_release_bus(slave); - spi_free_slave(slave); - - return ret; -} - -static int sx151x_spi_read(int chip, unsigned char reg) -{ - struct spi_slave *slave; - int ret; - - slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, - SPI_MODE_0); - if (!slave) - return 0; - - spi_claim_bus(slave); - - ret = spi_w8r8(slave, reg | 0x80); - if (ret < 0) - printf("spi%d.%d read fail: can't read %02x: %d\n", - CONFIG_SX151X_SPI_BUS, chip, reg, ret); - else - printf("spi%d.%d read register 0x%02x: 0x%02x\n", - CONFIG_SX151X_SPI_BUS, chip, reg, ret); - - spi_release_bus(slave); - spi_free_slave(slave); - - return ret; -} - -static inline void sx151x_find_cfg(int gpio, unsigned char *reg, unsigned char *mask) -{ - *reg -= gpio / 8; - *mask = 1 << (gpio % 8); -} - -static int sx151x_write_cfg(int chip, unsigned char gpio, unsigned char reg, int val) -{ - unsigned char mask; - unsigned char data; - int ret; - - sx151x_find_cfg(gpio, ®, &mask); - ret = sx151x_spi_read(chip, reg); - if (ret < 0) - return ret; - else - data = ret; - data &= ~mask; - data |= (val << (gpio % 8)) & mask; - return sx151x_spi_write(chip, reg, data); -} - -int sx151x_get_value(int chip, int gpio) -{ - unsigned char reg = SX151X_REG_DATA; - unsigned char mask; - int ret; - - sx151x_find_cfg(gpio, ®, &mask); - ret = sx151x_spi_read(chip, reg); - if (ret >= 0) - ret = (ret & mask) != 0 ? 1 : 0; - - return ret; -} - -int sx151x_set_value(int chip, int gpio, int val) -{ - return sx151x_write_cfg(chip, gpio, SX151X_REG_DATA, (val ? 1 : 0)); -} - -int sx151x_direction_input(int chip, int gpio) -{ - return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 1); -} - -int sx151x_direction_output(int chip, int gpio) -{ - return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 0); -} - -int sx151x_reset(int chip) -{ - int err; - - err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x12); - if (err < 0) - return err; - - err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x34); - return err; -} - -#ifdef CONFIG_CMD_SX151X - -int do_sx151x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - int ret = CMD_RET_USAGE, chip = 0, gpio = 0, val = 0; - - if (argc < 3) - return CMD_RET_USAGE; - - /* arg2 used as chip number */ - chip = simple_strtoul(argv[2], NULL, 10); - - if (strcmp(argv[1], "reset") == 0) { - ret = sx151x_reset(chip); - if (!ret) { - printf("Device at spi%d.%d was reset\n", - CONFIG_SX151X_SPI_BUS, chip); - } - return ret; - } - - if (argc < 4) - return CMD_RET_USAGE; - - /* arg3 used as gpio number */ - gpio = simple_strtoul(argv[3], NULL, 10); - - if (strcmp(argv[1], "get") == 0) { - ret = sx151x_get_value(chip, gpio); - if (ret < 0) - printf("Failed to get value at spi%d.%d gpio %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio); - else { - printf("Value at spi%d.%d gpio %d is %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio, ret); - ret = 0; - } - return ret; - } - - if (argc < 5) - return CMD_RET_USAGE; - - /* arg4 used as value or direction */ - val = simple_strtoul(argv[4], NULL, 10); - - if (strcmp(argv[1], "set") == 0) { - ret = sx151x_set_value(chip, gpio, val); - if (ret < 0) - printf("Failed to set value at spi%d.%d gpio %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio); - else - printf("New value at spi%d.%d gpio %d is %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio, val); - return ret; - } else if (strcmp(argv[1], "dir") == 0) { - if (val == 0) - ret = sx151x_direction_output(chip, gpio); - else - ret = sx151x_direction_input(chip, gpio); - - if (ret < 0) - printf("Failed to set direction of spi%d.%d gpio %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio); - else - printf("New direction of spi%d.%d gpio %d is %d\n", - CONFIG_SX151X_SPI_BUS, chip, gpio, val); - return ret; - } - - printf("Please see usage\n"); - - return ret; -} - -U_BOOT_CMD( - sx151x, 5, 1, do_sx151x, - "sx151x gpio access", - "dir chip gpio 0|1\n" - " - set gpio direction (0 for output, 1 for input)\n" - "sx151x get chip gpio\n" - " - get gpio value\n" - "sx151x set chip gpio 0|1\n" - " - set gpio value\n" - "sx151x reset chip\n" - " - reset chip" -); - -#endif /* CONFIG_CMD_SX151X */ diff --git a/drivers/input/i8042.c b/drivers/input/i8042.c index 0fd25b17ec..18476e97d1 100644 --- a/drivers/input/i8042.c +++ b/drivers/input/i8042.c @@ -274,7 +274,7 @@ static int i8042_start(struct udevice *dev) /* Init keyboard device (default US layout) */ keymap = KBD_US; - penv = getenv("keymap"); + penv = env_get("keymap"); if (penv != NULL) { if (strncmp(penv, "de", 3) == 0) keymap = KBD_GER; diff --git a/drivers/input/input.c b/drivers/input/input.c index 011667fedd..26da3a95ff 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -652,7 +652,7 @@ int input_stdio_register(struct stdio_dev *dev) error = stdio_register(dev); /* check if this is the standard input device */ - if (!error && strcmp(getenv("stdin"), dev->name) == 0) { + if (!error && strcmp(env_get("stdin"), dev->name) == 0) { /* reassign the console */ if (OVERWRITE_CONSOLE || console_assign(stdin, dev->name)) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 3abd2d30af..b69c9b71e4 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -930,8 +930,6 @@ __weak int esdhc_status_fixup(void *blob, const char *compat) return 1; } #endif - do_fixup_by_compat(blob, compat, "status", "okay", - sizeof("okay"), 1); return 0; } @@ -968,7 +966,9 @@ static int fsl_esdhc_probe(struct udevice *dev) struct fsl_esdhc_priv *priv = dev_get_priv(dev); const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); +#ifdef CONFIG_DM_REGULATOR struct udevice *vqmmc_dev; +#endif fdt_addr_t addr; unsigned int val; int ret; diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 048a51785e..42bc2efd90 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -2353,7 +2353,7 @@ unsigned long flash_init (void) #ifdef CONFIG_SYS_FLASH_PROTECTION /* read environment from EEPROM */ char s[64]; - getenv_f("unlock", s, sizeof(s)); + env_get_f("unlock", s, sizeof(s)); #endif #ifdef CONFIG_CFI_FLASH /* for driver model */ diff --git a/drivers/mtd/dataflash.c b/drivers/mtd/dataflash.c index 2d2c318adf..e961f518b0 100644 --- a/drivers/mtd/dataflash.c +++ b/drivers/mtd/dataflash.c @@ -155,7 +155,7 @@ int AT91F_DataflashInit (void) return found[0]; } -void AT91F_DataflashSetEnv (void) +void AT91F_Dataflashenv_set(void) { int i, j; int part; @@ -169,8 +169,9 @@ void AT91F_DataflashSetEnv (void) /* Set the environment according to the label...*/ if((env & FLAG_SETENV) == FLAG_SETENV) { start = dataflash_info[i].Device.area_list[j].start; - sprintf((char*) s,"%lX",start); - setenv((char*) area_list[part].label,(char*) s); + sprintf((char *)s, "%lX", start); + env_set((char *)area_list[part].label, + (char *)s); } part++; } diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3508c622e3..71d678fc66 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,4 +1,7 @@ -menu "NAND Device Support" + +menuconfig NAND + bool "NAND Device Support" +if NAND config SYS_NAND_SELF_INIT bool @@ -173,4 +176,4 @@ config SPL_NAND_DENALI endif -endmenu +endif # if NAND diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c index bcddfa0755..e5c0e12faa 100644 --- a/drivers/mtd/spi/sf_dataflash.c +++ b/drivers/mtd/spi/sf_dataflash.c @@ -134,11 +134,17 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len) debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len); div_u64_rem(len, spi_flash->page_size, &rem); - if (rem) + if (rem) { + printf("%s: len(0x%x) isn't the multiple of page size(0x%x)\n", + dev->name, len, spi_flash->page_size); return -EINVAL; + } div_u64_rem(offset, spi_flash->page_size, &rem); - if (rem) + if (rem) { + printf("%s: offset(0x%x) isn't the multiple of page size(0x%x)\n", + dev->name, offset, spi_flash->page_size); return -EINVAL; + } status = spi_claim_bus(spi); if (status) { diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 0034a28d5f..34f68881ed 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -947,11 +947,25 @@ int spi_flash_scan(struct spi_flash *flash) if (IS_ERR_OR_NULL(info)) return -ENOENT; - /* Flash powers up read-only, so clear BP# bits */ + /* + * Flash powers up read-only, so clear BP# bits. + * + * Note on some flash (like Macronix), QE (quad enable) bit is in the + * same status register as BP# bits, and we need preserve its original + * value during a reboot cycle as this is required by some platforms + * (like Intel ICH SPI controller working under descriptor mode). + */ if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_ATMEL || - JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX || - JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) - write_sr(flash, 0); + (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) || + (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX)) { + u8 sr = 0; + + if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX) { + read_sr(flash, &sr); + sr &= STATUS_QEB_MXIC; + } + write_sr(flash, sr); + } flash->name = info->name; flash->memory_map = spi->memory_map; diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c index edca94e30c..c4ccf48af4 100644 --- a/drivers/mtd/spi/spi_flash_ids.c +++ b/drivers/mtd/spi/spi_flash_ids.c @@ -81,6 +81,7 @@ const struct spi_flash_info spi_flash_ids[] = { {"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, {"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP) }, {"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx25u6435f", INFO(0xc22537, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP) }, {"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, {"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, {"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP) }, diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c index 8245cf51cc..f38f36beb1 100644 --- a/drivers/net/dc2114x.c +++ b/drivers/net/dc2114x.c @@ -750,7 +750,7 @@ static void update_srom(struct eth_device *dev, bd_t *bis) uchar enetaddr[6]; /* Ethernet Addr... */ - if (!eth_getenv_enetaddr("ethaddr", enetaddr)) + if (!eth_env_get_enetaddr("ethaddr", enetaddr)) return; eeprom[0x0a] = (enetaddr[1] << 8) | enetaddr[0]; eeprom[0x0b] = (enetaddr[3] << 8) | enetaddr[2]; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 40be52070e..f16b2990d7 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -985,9 +985,18 @@ static void fec_free_descs(struct fec_priv *fec) free(fec->tbd_base); } +#ifdef CONFIG_DM_ETH +struct mii_dev *fec_get_miibus(struct udevice *dev, int dev_id) +#else struct mii_dev *fec_get_miibus(uint32_t base_addr, int dev_id) +#endif { +#ifdef CONFIG_DM_ETH + struct fec_priv *priv = dev_get_priv(dev); + struct ethernet_regs *eth = priv->eth; +#else struct ethernet_regs *eth = (struct ethernet_regs *)base_addr; +#endif struct mii_dev *bus; int ret; @@ -1096,8 +1105,8 @@ static int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr, sprintf(mac, "eth%daddr", fec->dev_id); else strcpy(mac, "ethaddr"); - if (!getenv(mac)) - eth_setenv_enetaddr(mac, ethaddr); + if (!env_get(mac)) + eth_env_set_enetaddr(mac, ethaddr); } return ret; err4: diff --git a/drivers/net/fm/b4860.c b/drivers/net/fm/b4860.c index 5aeeb87282..3245bee473 100644 --- a/drivers/net/fm/b4860.c +++ b/drivers/net/fm/b4860.c @@ -97,7 +97,7 @@ phy_interface_t fman_port_enet_if(enum fm_port port) * Extract hwconfig from environment since environment * is not setup yet */ - getenv_f("hwconfig", buffer, sizeof(buffer)); + env_get_f("hwconfig", buffer, sizeof(buffer)); buf = buffer; /* check if XFI interface enable in hwconfig for 10g */ diff --git a/drivers/net/fm/fdt.c b/drivers/net/fm/fdt.c index 9918d8089a..5920fecea1 100644 --- a/drivers/net/fm/fdt.c +++ b/drivers/net/fm/fdt.c @@ -36,7 +36,7 @@ void fdt_fixup_fman_firmware(void *blob) return; /* If the environment variable is not set, then exit silently */ - p = getenv("fman_ucode"); + p = env_get("fman_ucode"); if (!p) return; diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index 9fe34ad4a9..451dfded77 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -418,7 +418,7 @@ int fm_init_common(int index, struct ccsr_fman *reg) rc = fman_upload_firmware(index, ®->fm_imem, addr); if (rc) return rc; - setenv_addr("fman_ucode", addr); + env_set_addr("fman_ucode", addr); fm_init_muram(index, ®->muram); fm_init_qmi(®->fm_qmi_common); diff --git a/drivers/net/fm/memac.c b/drivers/net/fm/memac.c index 1b5779cece..ea50ed38f4 100644 --- a/drivers/net/fm/memac.c +++ b/drivers/net/fm/memac.c @@ -84,6 +84,7 @@ static void memac_set_interface_mode(struct fsl_enet_mac *mac, if_mode |= IF_MODE_GMII; break; case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_TXID: if_mode |= (IF_MODE_GMII | IF_MODE_RG); break; case PHY_INTERFACE_MODE_RMII: @@ -106,7 +107,8 @@ static void memac_set_interface_mode(struct fsl_enet_mac *mac, if (type != PHY_INTERFACE_MODE_XGMII) if_mode |= IF_MODE_EN_AUTO; - if (type == PHY_INTERFACE_MODE_RGMII) { + if (type == PHY_INTERFACE_MODE_RGMII || + type == PHY_INTERFACE_MODE_RGMII_TXID) { if_mode &= ~IF_MODE_EN_AUTO; if_mode &= ~IF_MODE_SETSP_MASK; switch (speed) { diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 8bf25c7040..bdb6792c72 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -190,8 +190,8 @@ static int mc_fixup_mac_addr(void *blob, int nodeoffset, /* MAC address property present */ if (fdt_get_property(blob, nodeoffset, propname, NULL)) { /* u-boot MAC addr randomly assigned - leave the present one */ - if (!eth_getenv_enetaddr_by_index("eth", eth_dev->index, - env_enetaddr)) + if (!eth_env_get_enetaddr_by_index("eth", eth_dev->index, + env_enetaddr)) return err; } else { size = MC_DT_INCREASE_SIZE + strlen(propname) + len; @@ -530,7 +530,7 @@ static unsigned long get_mc_boot_timeout_ms(void) { unsigned long timeout_ms = CONFIG_SYS_LS_MC_BOOT_TIMEOUT_MS; - char *timeout_ms_env_var = getenv(MC_BOOT_TIMEOUT_ENV_VAR); + char *timeout_ms_env_var = env_get(MC_BOOT_TIMEOUT_ENV_VAR); if (timeout_ms_env_var) { timeout_ms = simple_strtoul(timeout_ms_env_var, NULL, 10); @@ -815,7 +815,7 @@ unsigned long mc_get_dram_block_size(void) { unsigned long dram_block_size = CONFIG_SYS_LS_MC_DRAM_BLOCK_MIN_SIZE; - char *dram_block_size_env_var = getenv(MC_MEM_SIZE_ENV_VAR); + char *dram_block_size_env_var = env_get(MC_MEM_SIZE_ENV_VAR); if (dram_block_size_env_var) { dram_block_size = simple_strtoul(dram_block_size_env_var, NULL, @@ -1336,14 +1336,18 @@ int fsl_mc_ldpaa_exit(bd_t *bd) { int err = 0; bool is_dpl_apply_status = false; + bool mc_boot_status = false; if (bd && mc_lazy_dpl_addr && !fsl_mc_ldpaa_exit(NULL)) { mc_apply_dpl(mc_lazy_dpl_addr); mc_lazy_dpl_addr = 0; } + if (!get_mc_boot_status()) + mc_boot_status = true; + /* MC is not loaded intentionally, So return success. */ - if (bd && get_mc_boot_status() != 0) + if (bd && !mc_boot_status) return 0; /* If DPL is deployed, set is_dpl_apply_status as TRUE. */ @@ -1354,11 +1358,14 @@ int fsl_mc_ldpaa_exit(bd_t *bd) * For case MC is loaded but DPL is not deployed, return success and * print message on console. Else FDT fix-up code execution hanged. */ - if (bd && !get_mc_boot_status() && !is_dpl_apply_status) { + if (bd && mc_boot_status && !is_dpl_apply_status) { printf("fsl-mc: DPL not deployed, DPAA2 ethernet not work\n"); return 0; } + if (bd && mc_boot_status && is_dpl_apply_status) + return 0; + err = dpbp_exit(); if (err < 0) { printf("dpbp_exit() failed: %d\n", err); @@ -1511,7 +1518,7 @@ void mc_env_boot(void) * address info properly. Without MAC addresses, the MC code * can not properly initialize the DPC. */ - mc_boot_env_var = getenv(MC_BOOT_ENV_VAR); + mc_boot_env_var = env_get(MC_BOOT_ENV_VAR); if (mc_boot_env_var) run_command_list(mc_boot_env_var, -1, 0); #endif /* CONFIG_FSL_MC_ENET */ diff --git a/drivers/net/fsl_mcdmafec.c b/drivers/net/fsl_mcdmafec.c index 628b420add..26c714cc04 100644 --- a/drivers/net/fsl_mcdmafec.c +++ b/drivers/net/fsl_mcdmafec.c @@ -383,9 +383,9 @@ static int fec_init(struct eth_device *dev, bd_t * bd) /* Set station address */ if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) - eth_getenv_enetaddr("ethaddr", enetaddr); + eth_env_get_enetaddr("ethaddr", enetaddr); else - eth_getenv_enetaddr("eth1addr", enetaddr); + eth_env_get_enetaddr("eth1addr", enetaddr); fec_set_hwaddr(fecp, enetaddr); /* Set Opcode/Pause Duration Register */ diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c index 5ccc4beda8..d0939e93dc 100644 --- a/drivers/net/ftgmac100.c +++ b/drivers/net/ftgmac100.c @@ -346,7 +346,7 @@ static void ftgmac100_set_mac(struct eth_device *dev, static void ftgmac100_set_mac_from_env(struct eth_device *dev) { - eth_getenv_enetaddr("ethaddr", dev->enetaddr); + eth_env_get_enetaddr("ethaddr", dev->enetaddr); ftgmac100_set_mac(dev, dev->enetaddr); } diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c index cd24a21f04..f231e6b33b 100644 --- a/drivers/net/ftmac100.c +++ b/drivers/net/ftmac100.c @@ -355,7 +355,7 @@ static int ftmac100_free_pkt(struct udevice *dev, uchar *packet, int length) int ftmac100_read_rom_hwaddr(struct udevice *dev) { struct eth_pdata *pdata = dev_get_platdata(dev); - eth_getenv_enetaddr("ethaddr", pdata->enetaddr); + eth_env_get_enetaddr("ethaddr", pdata->enetaddr); return 0; } diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c index c9f9e839ba..586ccbff0a 100644 --- a/drivers/net/gmac_rockchip.c +++ b/drivers/net/gmac_rockchip.c @@ -16,6 +16,7 @@ #include <asm/arch/clock.h> #include <asm/arch/hardware.h> #include <asm/arch/grf_rk3288.h> +#include <asm/arch/grf_rk3368.h> #include <asm/arch/grf_rk3399.h> #include <dm/pinctrl.h> #include <dt-bindings/clock/rk3288-cru.h> @@ -83,6 +84,38 @@ static int rk3288_gmac_fix_mac_speed(struct dw_eth_dev *priv) return 0; } +static int rk3368_gmac_fix_mac_speed(struct dw_eth_dev *priv) +{ + struct rk3368_grf *grf; + int clk; + enum { + RK3368_GMAC_CLK_SEL_2_5M = 2 << 4, + RK3368_GMAC_CLK_SEL_25M = 3 << 4, + RK3368_GMAC_CLK_SEL_125M = 0 << 4, + RK3368_GMAC_CLK_SEL_MASK = GENMASK(5, 4), + }; + + switch (priv->phydev->speed) { + case 10: + clk = RK3368_GMAC_CLK_SEL_2_5M; + break; + case 100: + clk = RK3368_GMAC_CLK_SEL_25M; + break; + case 1000: + clk = RK3368_GMAC_CLK_SEL_125M; + break; + default: + debug("Unknown phy speed: %d\n", priv->phydev->speed); + return -EINVAL; + } + + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + rk_clrsetreg(&grf->soc_con15, RK3368_GMAC_CLK_SEL_MASK, clk); + + return 0; +} + static int rk3399_gmac_fix_mac_speed(struct dw_eth_dev *priv) { struct rk3399_grf_regs *grf; @@ -129,6 +162,44 @@ static void rk3288_gmac_set_to_rgmii(struct gmac_rockchip_platdata *pdata) pdata->tx_delay << RK3288_CLK_TX_DL_CFG_GMAC_SHIFT); } +static void rk3368_gmac_set_to_rgmii(struct gmac_rockchip_platdata *pdata) +{ + struct rk3368_grf *grf; + enum { + RK3368_GMAC_PHY_INTF_SEL_RGMII = 1 << 9, + RK3368_GMAC_PHY_INTF_SEL_MASK = GENMASK(11, 9), + RK3368_RMII_MODE_MASK = BIT(6), + RK3368_RMII_MODE = BIT(6), + }; + enum { + RK3368_RXCLK_DLY_ENA_GMAC_MASK = BIT(15), + RK3368_RXCLK_DLY_ENA_GMAC_DISABLE = 0, + RK3368_RXCLK_DLY_ENA_GMAC_ENABLE = BIT(15), + RK3368_TXCLK_DLY_ENA_GMAC_MASK = BIT(7), + RK3368_TXCLK_DLY_ENA_GMAC_DISABLE = 0, + RK3368_TXCLK_DLY_ENA_GMAC_ENABLE = BIT(7), + RK3368_CLK_RX_DL_CFG_GMAC_SHIFT = 8, + RK3368_CLK_RX_DL_CFG_GMAC_MASK = GENMASK(14, 8), + RK3368_CLK_TX_DL_CFG_GMAC_SHIFT = 0, + RK3368_CLK_TX_DL_CFG_GMAC_MASK = GENMASK(6, 0), + }; + + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + rk_clrsetreg(&grf->soc_con15, + RK3368_RMII_MODE_MASK | RK3368_GMAC_PHY_INTF_SEL_MASK, + RK3368_GMAC_PHY_INTF_SEL_RGMII); + + rk_clrsetreg(&grf->soc_con16, + RK3368_RXCLK_DLY_ENA_GMAC_MASK | + RK3368_TXCLK_DLY_ENA_GMAC_MASK | + RK3368_CLK_RX_DL_CFG_GMAC_MASK | + RK3368_CLK_TX_DL_CFG_GMAC_MASK, + RK3368_RXCLK_DLY_ENA_GMAC_ENABLE | + RK3368_TXCLK_DLY_ENA_GMAC_ENABLE | + pdata->rx_delay << RK3368_CLK_RX_DL_CFG_GMAC_SHIFT | + pdata->tx_delay << RK3368_CLK_TX_DL_CFG_GMAC_SHIFT); +} + static void rk3399_gmac_set_to_rgmii(struct gmac_rockchip_platdata *pdata) { struct rk3399_grf_regs *grf; @@ -208,6 +279,11 @@ const struct rk_gmac_ops rk3288_gmac_ops = { .set_to_rgmii = rk3288_gmac_set_to_rgmii, }; +const struct rk_gmac_ops rk3368_gmac_ops = { + .fix_mac_speed = rk3368_gmac_fix_mac_speed, + .set_to_rgmii = rk3368_gmac_set_to_rgmii, +}; + const struct rk_gmac_ops rk3399_gmac_ops = { .fix_mac_speed = rk3399_gmac_fix_mac_speed, .set_to_rgmii = rk3399_gmac_set_to_rgmii, @@ -216,6 +292,8 @@ const struct rk_gmac_ops rk3399_gmac_ops = { static const struct udevice_id rockchip_gmac_ids[] = { { .compatible = "rockchip,rk3288-gmac", .data = (ulong)&rk3288_gmac_ops }, + { .compatible = "rockchip,rk3368-gmac", + .data = (ulong)&rk3368_gmac_ops }, { .compatible = "rockchip,rk3399-gmac", .data = (ulong)&rk3399_gmac_ops }, { } diff --git a/drivers/net/lan91c96.c b/drivers/net/lan91c96.c index 3526876d14..a9fc74bdee 100644 --- a/drivers/net/lan91c96.c +++ b/drivers/net/lan91c96.c @@ -704,13 +704,13 @@ static int smc_get_ethaddr(bd_t *bd, struct eth_device *dev) { uchar v_mac[6]; - if (!eth_getenv_enetaddr("ethaddr", v_mac)) { + if (!eth_env_get_enetaddr("ethaddr", v_mac)) { /* get ROM mac value if any */ if (!get_rom_mac(dev, v_mac)) { printf("\n*** ERROR: ethaddr is NOT set !!\n"); return -1; } - eth_setenv_enetaddr("ethaddr", v_mac); + eth_env_set_enetaddr("ethaddr", v_mac); } smc_set_mac_addr(v_mac); /* use old function to update smc default */ diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 4e61700d5d..f235b622b6 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -587,8 +587,10 @@ static void ldpaa_eth_stop(struct eth_device *net_dev) #ifdef CONFIG_PHYLIB if (priv->phydev && bus != NULL) phy_shutdown(priv->phydev); - else + else { free(priv->phydev); + priv->phydev = NULL; + } #endif ldpaa_dpbp_free(); diff --git a/drivers/net/mcffec.c b/drivers/net/mcffec.c index e1b06b25d7..39a6747320 100644 --- a/drivers/net/mcffec.c +++ b/drivers/net/mcffec.c @@ -428,25 +428,25 @@ int fec_init(struct eth_device *dev, bd_t * bd) if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) { #ifdef CONFIG_SYS_FEC1_IOBASE volatile fec_t *fecp1 = (fec_t *) (CONFIG_SYS_FEC1_IOBASE); - eth_getenv_enetaddr("eth1addr", ea); + eth_env_get_enetaddr("eth1addr", ea); fecp1->palr = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); fecp1->paur = (ea[4] << 24) | (ea[5] << 16); #endif - eth_getenv_enetaddr("ethaddr", ea); + eth_env_get_enetaddr("ethaddr", ea); fecp->palr = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); fecp->paur = (ea[4] << 24) | (ea[5] << 16); } else { #ifdef CONFIG_SYS_FEC0_IOBASE volatile fec_t *fecp0 = (fec_t *) (CONFIG_SYS_FEC0_IOBASE); - eth_getenv_enetaddr("ethaddr", ea); + eth_env_get_enetaddr("ethaddr", ea); fecp0->palr = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); fecp0->paur = (ea[4] << 24) | (ea[5] << 16); #endif #ifdef CONFIG_SYS_FEC1_IOBASE - eth_getenv_enetaddr("eth1addr", ea); + eth_env_get_enetaddr("eth1addr", ea); fecp->palr = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); fecp->paur = (ea[4] << 24) | (ea[5] << 16); diff --git a/drivers/net/ne2000_base.c b/drivers/net/ne2000_base.c index 377d87f04b..fb088e06a4 100644 --- a/drivers/net/ne2000_base.c +++ b/drivers/net/ne2000_base.c @@ -715,15 +715,15 @@ static int ne2k_setup_driver(struct eth_device *dev) * to the MAC address value in the environment, so we do not read * it from the prom or eeprom if it is specified in the environment. */ - if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr)) { + if (!eth_env_get_enetaddr("ethaddr", dev->enetaddr)) { /* If the MAC address is not in the environment, get it: */ if (!get_prom(dev->enetaddr, nic.base)) /* get MAC from prom */ dp83902a_init(dev->enetaddr); /* fallback: seeprom */ /* And write it into the environment otherwise eth_write_hwaddr - * returns -1 due to eth_getenv_enetaddr_by_index() failing, + * returns -1 due to eth_env_get_enetaddr_by_index() failing, * and this causes "Warning: failed to set MAC address", and * cmd_bdinfo has no ethaddr value which it can show: */ - eth_setenv_enetaddr("ethaddr", dev->enetaddr); + eth_env_set_enetaddr("ethaddr", dev->enetaddr); } return 0; } diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 3500047577..e9dbedf326 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -62,8 +62,8 @@ static int is_broadcast(struct in_addr ip) /* update only when the environment has changed */ if (env_changed_id != env_id) { - netmask = getenv_ip("netmask"); - our_ip = getenv_ip("ipaddr"); + netmask = env_get_ip("netmask"); + our_ip = env_get_ip("ipaddr"); env_changed_id = env_id; } @@ -82,11 +82,11 @@ static int refresh_settings_from_env(void) /* update only when the environment has changed */ if (env_changed_id != env_id) { - if (getenv("ncip")) { - nc_ip = getenv_ip("ncip"); + if (env_get("ncip")) { + nc_ip = env_get_ip("ncip"); if (!nc_ip.s_addr) return -1; /* ncip is 0.0.0.0 */ - p = strchr(getenv("ncip"), ':'); + p = strchr(env_get("ncip"), ':'); if (p != NULL) { nc_out_port = simple_strtoul(p + 1, NULL, 10); nc_in_port = nc_out_port; @@ -95,10 +95,10 @@ static int refresh_settings_from_env(void) nc_ip.s_addr = ~0; /* ncip is not set, so broadcast */ } - p = getenv("ncoutport"); + p = env_get("ncoutport"); if (p != NULL) nc_out_port = simple_strtoul(p, NULL, 10); - p = getenv("ncinport"); + p = env_get("ncinport"); if (p != NULL) nc_in_port = simple_strtoul(p, NULL, 10); diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 20f8a55196..0bb99e6bc6 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -258,7 +258,7 @@ static int ksz9021_config(struct phy_device *phydev) if (ret) return ret; - if (getenv("disable_giga")) + if (env_get("disable_giga")) features &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); /* force master mode for 1000BaseT due to chip errata */ @@ -344,7 +344,7 @@ static int ksz9031_config(struct phy_device *phydev) return ret; /* add an option to disable the gigabit feature of this PHY */ - if (getenv("disable_giga")) { + if (env_get("disable_giga")) { unsigned features; unsigned bmcr; diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c index f5fa0e8533..590ae0c0fc 100644 --- a/drivers/net/sandbox-raw.c +++ b/drivers/net/sandbox-raw.c @@ -33,8 +33,8 @@ static int sb_eth_raw_start(struct udevice *dev) if (strcmp(interface, "lo") == 0) { priv->local = 1; - setenv("ipaddr", "127.0.0.1"); - setenv("serverip", "127.0.0.1"); + env_set("ipaddr", "127.0.0.1"); + env_set("serverip", "127.0.0.1"); } return sandbox_eth_raw_os_start(interface, pdata->enetaddr, priv); } diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index a7c265b980..970d730e56 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -578,7 +578,7 @@ int sh_eth_initialize(bd_t *bd) if (retval < 0) return retval; - if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr)) + if (!eth_env_get_enetaddr("ethaddr", dev->enetaddr)) puts("Please set MAC address\n"); return ret; diff --git a/drivers/nvme/Kconfig b/drivers/nvme/Kconfig new file mode 100644 index 0000000000..cad8dbc97b --- /dev/null +++ b/drivers/nvme/Kconfig @@ -0,0 +1,12 @@ +# +# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +config NVME + bool "NVM Express device support" + depends on BLK && PCI + help + This option enables support for NVM Express devices. + It supports basic functions of NVMe (read/write). diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile new file mode 100644 index 0000000000..1f3010a8c2 --- /dev/null +++ b/drivers/nvme/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += nvme-uclass.o nvme.o nvme_show.o diff --git a/drivers/nvme/nvme-uclass.c b/drivers/nvme/nvme-uclass.c new file mode 100644 index 0000000000..0895bc9c24 --- /dev/null +++ b/drivers/nvme/nvme-uclass.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 NXP Semiconductors + * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <dm/device.h> +#include "nvme.h" + +static int nvme_info_init(struct uclass *uc) +{ + struct nvme_info *info = (struct nvme_info *)uc->priv; + + info->ns_num = 0; + info->ndev_num = 0; + INIT_LIST_HEAD(&info->dev_list); + nvme_info = info; + + return 0; +} + +static int nvme_uclass_post_probe(struct udevice *udev) +{ + char name[20]; + char *str; + struct udevice *ns_udev; + int i, ret; + struct nvme_dev *ndev = dev_get_priv(udev); + + /* Create a blk device for each namespace */ + for (i = 0; i < ndev->nn; i++) { + sprintf(name, "nvme-blk#%d", nvme_info->ns_num); + str = strdup(name); + if (!str) + return -ENOMEM; + + /* The real blksz and size will be set by nvme_blk_probe() */ + ret = blk_create_device(udev, "nvme-blk", str, IF_TYPE_NVME, + nvme_info->ns_num++, 512, 0, &ns_udev); + if (ret) { + free(str); + nvme_info->ns_num--; + + return ret; + } + device_set_name_alloced(ns_udev); + } + + return 0; +} + +UCLASS_DRIVER(nvme) = { + .name = "nvme", + .id = UCLASS_NVME, + .init = nvme_info_init, + .post_probe = nvme_uclass_post_probe, + .priv_auto_alloc_size = sizeof(struct nvme_info), +}; diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c new file mode 100644 index 0000000000..151fe92479 --- /dev/null +++ b/drivers/nvme/nvme.c @@ -0,0 +1,860 @@ +/* + * Copyright (C) 2017 NXP Semiconductors + * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <memalign.h> +#include <pci.h> +#include <dm/device-internal.h> +#include "nvme.h" + +struct nvme_info *nvme_info; + +#define NVME_Q_DEPTH 2 +#define NVME_AQ_DEPTH 2 +#define NVME_SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) +#define NVME_CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) +#define ADMIN_TIMEOUT 60 +#define IO_TIMEOUT 30 +#define MAX_PRP_POOL 512 + +/* + * An NVM Express queue. Each device has at least two (one for admin + * commands and one for I/O commands). + */ +struct nvme_queue { + struct nvme_dev *dev; + struct nvme_command *sq_cmds; + struct nvme_completion *cqes; + wait_queue_head_t sq_full; + u32 __iomem *q_db; + u16 q_depth; + s16 cq_vector; + u16 sq_head; + u16 sq_tail; + u16 cq_head; + u16 qid; + u8 cq_phase; + u8 cqe_seen; + unsigned long cmdid_data[]; +}; + +static int nvme_wait_ready(struct nvme_dev *dev, bool enabled) +{ + u32 bit = enabled ? NVME_CSTS_RDY : 0; + + while ((readl(&dev->bar->csts) & NVME_CSTS_RDY) != bit) + udelay(10000); + + return 0; +} + +static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, + int total_len, u64 dma_addr) +{ + u32 page_size = dev->page_size; + int offset = dma_addr & (page_size - 1); + u64 *prp_pool; + int length = total_len; + int i, nprps; + length -= (page_size - offset); + + if (length <= 0) { + *prp2 = 0; + return 0; + } + + if (length) + dma_addr += (page_size - offset); + + if (length <= page_size) { + *prp2 = dma_addr; + return 0; + } + + nprps = DIV_ROUND_UP(length, page_size); + + if (nprps > dev->prp_entry_num) { + free(dev->prp_pool); + dev->prp_pool = malloc(nprps << 3); + if (!dev->prp_pool) { + printf("Error: malloc prp_pool fail\n"); + return -ENOMEM; + } + dev->prp_entry_num = nprps; + } + + prp_pool = dev->prp_pool; + i = 0; + while (nprps) { + if (i == ((page_size >> 3) - 1)) { + *(prp_pool + i) = cpu_to_le64((ulong)prp_pool + + page_size); + i = 0; + prp_pool += page_size; + } + *(prp_pool + i++) = cpu_to_le64(dma_addr); + dma_addr += page_size; + nprps--; + } + *prp2 = (ulong)dev->prp_pool; + + return 0; +} + +static __le16 nvme_get_cmd_id(void) +{ + static unsigned short cmdid; + + return cpu_to_le16((cmdid < USHRT_MAX) ? cmdid++ : 0); +} + +static u16 nvme_read_completion_status(struct nvme_queue *nvmeq, u16 index) +{ + u64 start = (ulong)&nvmeq->cqes[index]; + u64 stop = start + sizeof(struct nvme_completion); + + invalidate_dcache_range(start, stop); + + return le16_to_cpu(readw(&(nvmeq->cqes[index].status))); +} + +/** + * nvme_submit_cmd() - copy a command into a queue and ring the doorbell + * + * @nvmeq: The queue to use + * @cmd: The command to send + */ +static void nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) +{ + u16 tail = nvmeq->sq_tail; + + memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); + flush_dcache_range((ulong)&nvmeq->sq_cmds[tail], + (ulong)&nvmeq->sq_cmds[tail] + sizeof(*cmd)); + + if (++tail == nvmeq->q_depth) + tail = 0; + writel(tail, nvmeq->q_db); + nvmeq->sq_tail = tail; +} + +static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, + struct nvme_command *cmd, + u32 *result, unsigned timeout) +{ + u16 head = nvmeq->cq_head; + u16 phase = nvmeq->cq_phase; + u16 status; + ulong start_time; + ulong timeout_us = timeout * 100000; + + cmd->common.command_id = nvme_get_cmd_id(); + nvme_submit_cmd(nvmeq, cmd); + + start_time = timer_get_us(); + + for (;;) { + status = nvme_read_completion_status(nvmeq, head); + if ((status & 0x01) == phase) + break; + if (timeout_us > 0 && (timer_get_us() - start_time) + >= timeout_us) + return -ETIMEDOUT; + } + + status >>= 1; + if (status) { + printf("ERROR: status = %x, phase = %d, head = %d\n", + status, phase, head); + status = 0; + if (++head == nvmeq->q_depth) { + head = 0; + phase = !phase; + } + writel(head, nvmeq->q_db + nvmeq->dev->db_stride); + nvmeq->cq_head = head; + nvmeq->cq_phase = phase; + + return -EIO; + } + + if (result) + *result = le32_to_cpu(readl(&(nvmeq->cqes[head].result))); + + if (++head == nvmeq->q_depth) { + head = 0; + phase = !phase; + } + writel(head, nvmeq->q_db + nvmeq->dev->db_stride); + nvmeq->cq_head = head; + nvmeq->cq_phase = phase; + + return status; +} + +static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd, + u32 *result) +{ + return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT); +} + +static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, + int qid, int depth) +{ + struct nvme_queue *nvmeq = malloc(sizeof(*nvmeq)); + if (!nvmeq) + return NULL; + memset(nvmeq, 0, sizeof(*nvmeq)); + + nvmeq->cqes = (void *)memalign(4096, NVME_CQ_SIZE(depth)); + if (!nvmeq->cqes) + goto free_nvmeq; + memset((void *)nvmeq->cqes, 0, NVME_CQ_SIZE(depth)); + + nvmeq->sq_cmds = (void *)memalign(4096, NVME_SQ_SIZE(depth)); + if (!nvmeq->sq_cmds) + goto free_queue; + memset((void *)nvmeq->sq_cmds, 0, NVME_SQ_SIZE(depth)); + + nvmeq->dev = dev; + + nvmeq->cq_head = 0; + nvmeq->cq_phase = 1; + nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride]; + nvmeq->q_depth = depth; + nvmeq->qid = qid; + dev->queue_count++; + dev->queues[qid] = nvmeq; + + return nvmeq; + + free_queue: + free((void *)nvmeq->cqes); + free_nvmeq: + free(nvmeq); + + return NULL; +} + +static int nvme_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.delete_queue.opcode = opcode; + c.delete_queue.qid = cpu_to_le16(id); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +static int nvme_delete_sq(struct nvme_dev *dev, u16 sqid) +{ + return nvme_delete_queue(dev, nvme_admin_delete_sq, sqid); +} + +static int nvme_delete_cq(struct nvme_dev *dev, u16 cqid) +{ + return nvme_delete_queue(dev, nvme_admin_delete_cq, cqid); +} + +static int nvme_enable_ctrl(struct nvme_dev *dev) +{ + dev->ctrl_config &= ~NVME_CC_SHN_MASK; + dev->ctrl_config |= NVME_CC_ENABLE; + writel(cpu_to_le32(dev->ctrl_config), &dev->bar->cc); + + return nvme_wait_ready(dev, true); +} + +static int nvme_disable_ctrl(struct nvme_dev *dev) +{ + dev->ctrl_config &= ~NVME_CC_SHN_MASK; + dev->ctrl_config &= ~NVME_CC_ENABLE; + writel(cpu_to_le32(dev->ctrl_config), &dev->bar->cc); + + return nvme_wait_ready(dev, false); +} + +static void nvme_free_queue(struct nvme_queue *nvmeq) +{ + free((void *)nvmeq->cqes); + free(nvmeq->sq_cmds); + free(nvmeq); +} + +static void nvme_free_queues(struct nvme_dev *dev, int lowest) +{ + int i; + + for (i = dev->queue_count - 1; i >= lowest; i--) { + struct nvme_queue *nvmeq = dev->queues[i]; + dev->queue_count--; + dev->queues[i] = NULL; + nvme_free_queue(nvmeq); + } +} + +static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid) +{ + struct nvme_dev *dev = nvmeq->dev; + + nvmeq->sq_tail = 0; + nvmeq->cq_head = 0; + nvmeq->cq_phase = 1; + nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride]; + memset((void *)nvmeq->cqes, 0, NVME_CQ_SIZE(nvmeq->q_depth)); + flush_dcache_range((ulong)nvmeq->cqes, + (ulong)nvmeq->cqes + NVME_CQ_SIZE(nvmeq->q_depth)); + dev->online_queues++; +} + +static int nvme_configure_admin_queue(struct nvme_dev *dev) +{ + int result; + u32 aqa; + u64 cap = nvme_readq(&dev->bar->cap); + struct nvme_queue *nvmeq; + /* most architectures use 4KB as the page size */ + unsigned page_shift = 12; + unsigned dev_page_min = NVME_CAP_MPSMIN(cap) + 12; + unsigned dev_page_max = NVME_CAP_MPSMAX(cap) + 12; + + if (page_shift < dev_page_min) { + debug("Device minimum page size (%u) too large for host (%u)\n", + 1 << dev_page_min, 1 << page_shift); + return -ENODEV; + } + + if (page_shift > dev_page_max) { + debug("Device maximum page size (%u) smaller than host (%u)\n", + 1 << dev_page_max, 1 << page_shift); + page_shift = dev_page_max; + } + + result = nvme_disable_ctrl(dev); + if (result < 0) + return result; + + nvmeq = dev->queues[0]; + if (!nvmeq) { + nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH); + if (!nvmeq) + return -ENOMEM; + } + + aqa = nvmeq->q_depth - 1; + aqa |= aqa << 16; + aqa |= aqa << 16; + + dev->page_size = 1 << page_shift; + + dev->ctrl_config = NVME_CC_CSS_NVM; + dev->ctrl_config |= (page_shift - 12) << NVME_CC_MPS_SHIFT; + dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE; + dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES; + + writel(aqa, &dev->bar->aqa); + nvme_writeq((ulong)nvmeq->sq_cmds, &dev->bar->asq); + nvme_writeq((ulong)nvmeq->cqes, &dev->bar->acq); + + result = nvme_enable_ctrl(dev); + if (result) + goto free_nvmeq; + + nvmeq->cq_vector = 0; + + nvme_init_queue(dev->queues[0], 0); + + return result; + + free_nvmeq: + nvme_free_queues(dev, 0); + + return result; +} + +static int nvme_alloc_cq(struct nvme_dev *dev, u16 qid, + struct nvme_queue *nvmeq) +{ + struct nvme_command c; + int flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED; + + memset(&c, 0, sizeof(c)); + c.create_cq.opcode = nvme_admin_create_cq; + c.create_cq.prp1 = cpu_to_le64((ulong)nvmeq->cqes); + c.create_cq.cqid = cpu_to_le16(qid); + c.create_cq.qsize = cpu_to_le16(nvmeq->q_depth - 1); + c.create_cq.cq_flags = cpu_to_le16(flags); + c.create_cq.irq_vector = cpu_to_le16(nvmeq->cq_vector); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +static int nvme_alloc_sq(struct nvme_dev *dev, u16 qid, + struct nvme_queue *nvmeq) +{ + struct nvme_command c; + int flags = NVME_QUEUE_PHYS_CONTIG | NVME_SQ_PRIO_MEDIUM; + + memset(&c, 0, sizeof(c)); + c.create_sq.opcode = nvme_admin_create_sq; + c.create_sq.prp1 = cpu_to_le64((ulong)nvmeq->sq_cmds); + c.create_sq.sqid = cpu_to_le16(qid); + c.create_sq.qsize = cpu_to_le16(nvmeq->q_depth - 1); + c.create_sq.sq_flags = cpu_to_le16(flags); + c.create_sq.cqid = cpu_to_le16(qid); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +int nvme_identify(struct nvme_dev *dev, unsigned nsid, + unsigned cns, dma_addr_t dma_addr) +{ + struct nvme_command c; + u32 page_size = dev->page_size; + int offset = dma_addr & (page_size - 1); + int length = sizeof(struct nvme_id_ctrl); + + memset(&c, 0, sizeof(c)); + c.identify.opcode = nvme_admin_identify; + c.identify.nsid = cpu_to_le32(nsid); + c.identify.prp1 = cpu_to_le64(dma_addr); + + length -= (page_size - offset); + if (length <= 0) { + c.identify.prp2 = 0; + } else { + dma_addr += (page_size - offset); + c.identify.prp2 = dma_addr; + } + + c.identify.cns = cpu_to_le32(cns); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid, + dma_addr_t dma_addr, u32 *result) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_get_features; + c.features.nsid = cpu_to_le32(nsid); + c.features.prp1 = cpu_to_le64(dma_addr); + c.features.fid = cpu_to_le32(fid); + + return nvme_submit_admin_cmd(dev, &c, result); +} + +int nvme_set_features(struct nvme_dev *dev, unsigned fid, unsigned dword11, + dma_addr_t dma_addr, u32 *result) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_set_features; + c.features.prp1 = cpu_to_le64(dma_addr); + c.features.fid = cpu_to_le32(fid); + c.features.dword11 = cpu_to_le32(dword11); + + return nvme_submit_admin_cmd(dev, &c, result); +} + +static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) +{ + struct nvme_dev *dev = nvmeq->dev; + int result; + + nvmeq->cq_vector = qid - 1; + result = nvme_alloc_cq(dev, qid, nvmeq); + if (result < 0) + goto release_cq; + + result = nvme_alloc_sq(dev, qid, nvmeq); + if (result < 0) + goto release_sq; + + nvme_init_queue(nvmeq, qid); + + return result; + + release_sq: + nvme_delete_sq(dev, qid); + release_cq: + nvme_delete_cq(dev, qid); + + return result; +} + +static int nvme_set_queue_count(struct nvme_dev *dev, int count) +{ + int status; + u32 result; + u32 q_count = (count - 1) | ((count - 1) << 16); + + status = nvme_set_features(dev, NVME_FEAT_NUM_QUEUES, + q_count, 0, &result); + + if (status < 0) + return status; + if (status > 1) + return 0; + + return min(result & 0xffff, result >> 16) + 1; +} + +static void nvme_create_io_queues(struct nvme_dev *dev) +{ + unsigned int i; + + for (i = dev->queue_count; i <= dev->max_qid; i++) + if (!nvme_alloc_queue(dev, i, dev->q_depth)) + break; + + for (i = dev->online_queues; i <= dev->queue_count - 1; i++) + if (nvme_create_queue(dev->queues[i], i)) + break; +} + +static int nvme_setup_io_queues(struct nvme_dev *dev) +{ + int nr_io_queues; + int result; + + nr_io_queues = 1; + result = nvme_set_queue_count(dev, nr_io_queues); + if (result <= 0) + return result; + + if (result < nr_io_queues) + nr_io_queues = result; + + dev->max_qid = nr_io_queues; + + /* Free previously allocated queues */ + nvme_free_queues(dev, nr_io_queues + 1); + nvme_create_io_queues(dev); + + return 0; +} + +static int nvme_get_info_from_identify(struct nvme_dev *dev) +{ + u16 vendor, device; + struct nvme_id_ctrl buf, *ctrl = &buf; + int ret; + int shift = NVME_CAP_MPSMIN(nvme_readq(&dev->bar->cap)) + 12; + + ret = nvme_identify(dev, 0, 1, (dma_addr_t)ctrl); + if (ret) + return -EIO; + + dev->nn = le32_to_cpu(ctrl->nn); + dev->vwc = ctrl->vwc; + memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn)); + memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); + memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr)); + if (ctrl->mdts) + dev->max_transfer_shift = (ctrl->mdts + shift); + else { + /* + * Maximum Data Transfer Size (MDTS) field indicates the maximum + * data transfer size between the host and the controller. The + * host should not submit a command that exceeds this transfer + * size. The value is in units of the minimum memory page size + * and is reported as a power of two (2^n). + * + * The spec also says: a value of 0h indicates no restrictions + * on transfer size. But in nvme_blk_read/write() below we have + * the following algorithm for maximum number of logic blocks + * per transfer: + * + * u16 lbas = 1 << (dev->max_transfer_shift - ns->lba_shift); + * + * In order for lbas not to overflow, the maximum number is 15 + * which means dev->max_transfer_shift = 15 + 9 (ns->lba_shift). + * Let's use 20 which provides 1MB size. + */ + dev->max_transfer_shift = 20; + } + + /* Apply quirk stuff */ + dm_pci_read_config16(dev->pdev, PCI_VENDOR_ID, &vendor); + dm_pci_read_config16(dev->pdev, PCI_DEVICE_ID, &device); + if ((vendor == PCI_VENDOR_ID_INTEL) && + (device == 0x0953) && ctrl->vs[3]) { + unsigned int max_transfer_shift; + dev->stripe_size = (ctrl->vs[3] + shift); + max_transfer_shift = (ctrl->vs[3] + 18); + if (dev->max_transfer_shift) { + dev->max_transfer_shift = min(max_transfer_shift, + dev->max_transfer_shift); + } else { + dev->max_transfer_shift = max_transfer_shift; + } + } + + return 0; +} + +int nvme_scan_namespace(void) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + ret = uclass_get(UCLASS_NVME, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + ret = device_probe(dev); + if (ret) + return ret; + } + + return 0; +} + +static int nvme_blk_probe(struct udevice *udev) +{ + struct nvme_dev *ndev = dev_get_priv(udev->parent); + struct blk_desc *desc = dev_get_uclass_platdata(udev); + struct nvme_ns *ns = dev_get_priv(udev); + u8 flbas; + u16 vendor; + struct nvme_id_ns buf, *id = &buf; + + memset(ns, 0, sizeof(*ns)); + ns->dev = ndev; + ns->ns_id = desc->devnum - ndev->blk_dev_start + 1; + if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)id)) + return -EIO; + + flbas = id->flbas & NVME_NS_FLBAS_LBA_MASK; + ns->flbas = flbas; + ns->lba_shift = id->lbaf[flbas].ds; + ns->mode_select_num_blocks = le64_to_cpu(id->nsze); + ns->mode_select_block_len = 1 << ns->lba_shift; + list_add(&ns->list, &ndev->namespaces); + + desc->lba = ns->mode_select_num_blocks; + desc->log2blksz = ns->lba_shift; + desc->blksz = 1 << ns->lba_shift; + desc->bdev = udev; + dm_pci_read_config16(ndev->pdev, PCI_VENDOR_ID, &vendor); + sprintf(desc->vendor, "0x%.4x", vendor); + memcpy(desc->product, ndev->serial, sizeof(ndev->serial)); + memcpy(desc->revision, ndev->firmware_rev, sizeof(ndev->firmware_rev)); + part_init(desc); + + return 0; +} + +static ulong nvme_blk_read(struct udevice *udev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer) +{ + struct nvme_ns *ns = dev_get_priv(udev); + struct nvme_dev *dev = ns->dev; + struct nvme_command c; + struct blk_desc *desc = dev_get_uclass_platdata(udev); + int status; + u64 prp2; + u64 total_len = blkcnt << desc->log2blksz; + u64 temp_len = total_len; + + u64 slba = blknr; + u16 lbas = 1 << (dev->max_transfer_shift - ns->lba_shift); + u64 total_lbas = blkcnt; + + c.rw.opcode = nvme_cmd_read; + c.rw.flags = 0; + c.rw.nsid = cpu_to_le32(ns->ns_id); + c.rw.control = 0; + c.rw.dsmgmt = 0; + c.rw.reftag = 0; + c.rw.apptag = 0; + c.rw.appmask = 0; + c.rw.metadata = 0; + + while (total_lbas) { + if (total_lbas < lbas) { + lbas = (u16)total_lbas; + total_lbas = 0; + } else { + total_lbas -= lbas; + } + + if (nvme_setup_prps + (dev, &prp2, lbas << ns->lba_shift, (ulong)buffer)) + return -EIO; + c.rw.slba = cpu_to_le64(slba); + slba += lbas; + c.rw.length = cpu_to_le16(lbas - 1); + c.rw.prp1 = cpu_to_le64((ulong)buffer); + c.rw.prp2 = cpu_to_le64(prp2); + status = nvme_submit_sync_cmd(dev->queues[1], + &c, NULL, IO_TIMEOUT); + if (status) + break; + temp_len -= lbas << ns->lba_shift; + buffer += lbas << ns->lba_shift; + } + + return (total_len - temp_len) >> desc->log2blksz; +} + +static ulong nvme_blk_write(struct udevice *udev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +{ + struct nvme_ns *ns = dev_get_priv(udev); + struct nvme_dev *dev = ns->dev; + struct nvme_command c; + struct blk_desc *desc = dev_get_uclass_platdata(udev); + int status; + u64 prp2; + u64 total_len = blkcnt << desc->log2blksz; + u64 temp_len = total_len; + + u64 slba = blknr; + u16 lbas = 1 << (dev->max_transfer_shift - ns->lba_shift); + u64 total_lbas = blkcnt; + + c.rw.opcode = nvme_cmd_write; + c.rw.flags = 0; + c.rw.nsid = cpu_to_le32(ns->ns_id); + c.rw.control = 0; + c.rw.dsmgmt = 0; + c.rw.reftag = 0; + c.rw.apptag = 0; + c.rw.appmask = 0; + c.rw.metadata = 0; + + while (total_lbas) { + if (total_lbas < lbas) { + lbas = (u16)total_lbas; + total_lbas = 0; + } else { + total_lbas -= lbas; + } + + if (nvme_setup_prps + (dev, &prp2, lbas << ns->lba_shift, (ulong)buffer)) + return -EIO; + c.rw.slba = cpu_to_le64(slba); + slba += lbas; + c.rw.length = cpu_to_le16(lbas - 1); + c.rw.prp1 = cpu_to_le64((ulong)buffer); + c.rw.prp2 = cpu_to_le64(prp2); + status = nvme_submit_sync_cmd(dev->queues[1], + &c, NULL, IO_TIMEOUT); + if (status) + break; + temp_len -= lbas << ns->lba_shift; + buffer += lbas << ns->lba_shift; + } + + return (total_len - temp_len) >> desc->log2blksz; +} + +static const struct blk_ops nvme_blk_ops = { + .read = nvme_blk_read, + .write = nvme_blk_write, +}; + +U_BOOT_DRIVER(nvme_blk) = { + .name = "nvme-blk", + .id = UCLASS_BLK, + .probe = nvme_blk_probe, + .ops = &nvme_blk_ops, + .priv_auto_alloc_size = sizeof(struct nvme_ns), +}; + +static int nvme_bind(struct udevice *udev) +{ + char name[20]; + sprintf(name, "nvme#%d", nvme_info->ndev_num++); + + return device_set_name(udev, name); +} + +static int nvme_probe(struct udevice *udev) +{ + int ret; + struct nvme_dev *ndev = dev_get_priv(udev); + u64 cap; + + ndev->pdev = pci_get_controller(udev); + ndev->instance = trailing_strtol(udev->name); + + INIT_LIST_HEAD(&ndev->namespaces); + ndev->bar = dm_pci_map_bar(udev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + if (readl(&ndev->bar->csts) == -1) { + ret = -ENODEV; + printf("Error: %s: Out of memory!\n", udev->name); + goto free_nvme; + } + + ndev->queues = malloc(2 * sizeof(struct nvme_queue)); + if (!ndev->queues) { + ret = -ENOMEM; + printf("Error: %s: Out of memory!\n", udev->name); + goto free_nvme; + } + memset(ndev->queues, 0, sizeof(2 * sizeof(struct nvme_queue))); + + ndev->prp_pool = malloc(MAX_PRP_POOL); + if (!ndev->prp_pool) { + ret = -ENOMEM; + printf("Error: %s: Out of memory!\n", udev->name); + goto free_nvme; + } + ndev->prp_entry_num = MAX_PRP_POOL >> 3; + + cap = nvme_readq(&ndev->bar->cap); + ndev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH); + ndev->db_stride = 1 << NVME_CAP_STRIDE(cap); + ndev->dbs = ((void __iomem *)ndev->bar) + 4096; + + ret = nvme_configure_admin_queue(ndev); + if (ret) + goto free_queue; + + ret = nvme_setup_io_queues(ndev); + if (ret) + goto free_queue; + + nvme_get_info_from_identify(ndev); + ndev->blk_dev_start = nvme_info->ns_num; + list_add(&ndev->node, &nvme_info->dev_list); + + return 0; + +free_queue: + free((void *)ndev->queues); +free_nvme: + return ret; +} + +U_BOOT_DRIVER(nvme) = { + .name = "nvme", + .id = UCLASS_NVME, + .bind = nvme_bind, + .probe = nvme_probe, + .priv_auto_alloc_size = sizeof(struct nvme_dev), +}; + +struct pci_device_id nvme_supported[] = { + { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, ~0) }, + {} +}; + +U_BOOT_PCI_DEVICE(nvme, nvme_supported); diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h new file mode 100644 index 0000000000..b7fdd0b8e2 --- /dev/null +++ b/drivers/nvme/nvme.h @@ -0,0 +1,717 @@ +/* + * Copyright (C) 2017 NXP Semiconductors + * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DRIVER_NVME_H__ +#define __DRIVER_NVME_H__ + +#include <asm/io.h> + +struct nvme_id_power_state { + __le16 max_power; /* centiwatts */ + __u8 rsvd2; + __u8 flags; + __le32 entry_lat; /* microseconds */ + __le32 exit_lat; /* microseconds */ + __u8 read_tput; + __u8 read_lat; + __u8 write_tput; + __u8 write_lat; + __le16 idle_power; + __u8 idle_scale; + __u8 rsvd19; + __le16 active_power; + __u8 active_work_scale; + __u8 rsvd23[9]; +}; + +enum { + NVME_PS_FLAGS_MAX_POWER_SCALE = 1 << 0, + NVME_PS_FLAGS_NON_OP_STATE = 1 << 1, +}; + +struct nvme_id_ctrl { + __le16 vid; + __le16 ssvid; + char sn[20]; + char mn[40]; + char fr[8]; + __u8 rab; + __u8 ieee[3]; + __u8 mic; + __u8 mdts; + __u16 cntlid; + __u32 ver; + __u8 rsvd84[172]; + __le16 oacs; + __u8 acl; + __u8 aerl; + __u8 frmw; + __u8 lpa; + __u8 elpe; + __u8 npss; + __u8 avscc; + __u8 apsta; + __le16 wctemp; + __le16 cctemp; + __u8 rsvd270[242]; + __u8 sqes; + __u8 cqes; + __u8 rsvd514[2]; + __le32 nn; + __le16 oncs; + __le16 fuses; + __u8 fna; + __u8 vwc; + __le16 awun; + __le16 awupf; + __u8 nvscc; + __u8 rsvd531; + __le16 acwu; + __u8 rsvd534[2]; + __le32 sgls; + __u8 rsvd540[1508]; + struct nvme_id_power_state psd[32]; + __u8 vs[1024]; +}; + +enum { + NVME_CTRL_ONCS_COMPARE = 1 << 0, + NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, + NVME_CTRL_ONCS_DSM = 1 << 2, + NVME_CTRL_VWC_PRESENT = 1 << 0, +}; + +struct nvme_lbaf { + __le16 ms; + __u8 ds; + __u8 rp; +}; + +struct nvme_id_ns { + __le64 nsze; + __le64 ncap; + __le64 nuse; + __u8 nsfeat; + __u8 nlbaf; + __u8 flbas; + __u8 mc; + __u8 dpc; + __u8 dps; + __u8 nmic; + __u8 rescap; + __u8 fpi; + __u8 rsvd33; + __le16 nawun; + __le16 nawupf; + __le16 nacwu; + __le16 nabsn; + __le16 nabo; + __le16 nabspf; + __u16 rsvd46; + __le64 nvmcap[2]; + __u8 rsvd64[40]; + __u8 nguid[16]; + __u8 eui64[8]; + struct nvme_lbaf lbaf[16]; + __u8 rsvd192[192]; + __u8 vs[3712]; +}; + +enum { + NVME_NS_FEAT_THIN = 1 << 0, + NVME_NS_FLBAS_LBA_MASK = 0xf, + NVME_NS_FLBAS_META_EXT = 0x10, + NVME_LBAF_RP_BEST = 0, + NVME_LBAF_RP_BETTER = 1, + NVME_LBAF_RP_GOOD = 2, + NVME_LBAF_RP_DEGRADED = 3, + NVME_NS_DPC_PI_LAST = 1 << 4, + NVME_NS_DPC_PI_FIRST = 1 << 3, + NVME_NS_DPC_PI_TYPE3 = 1 << 2, + NVME_NS_DPC_PI_TYPE2 = 1 << 1, + NVME_NS_DPC_PI_TYPE1 = 1 << 0, + NVME_NS_DPS_PI_FIRST = 1 << 3, + NVME_NS_DPS_PI_MASK = 0x7, + NVME_NS_DPS_PI_TYPE1 = 1, + NVME_NS_DPS_PI_TYPE2 = 2, + NVME_NS_DPS_PI_TYPE3 = 3, +}; + +struct nvme_smart_log { + __u8 critical_warning; + __u8 temperature[2]; + __u8 avail_spare; + __u8 spare_thresh; + __u8 percent_used; + __u8 rsvd6[26]; + __u8 data_units_read[16]; + __u8 data_units_written[16]; + __u8 host_reads[16]; + __u8 host_writes[16]; + __u8 ctrl_busy_time[16]; + __u8 power_cycles[16]; + __u8 power_on_hours[16]; + __u8 unsafe_shutdowns[16]; + __u8 media_errors[16]; + __u8 num_err_log_entries[16]; + __le32 warning_temp_time; + __le32 critical_comp_time; + __le16 temp_sensor[8]; + __u8 rsvd216[296]; +}; + +enum { + NVME_SMART_CRIT_SPARE = 1 << 0, + NVME_SMART_CRIT_TEMPERATURE = 1 << 1, + NVME_SMART_CRIT_RELIABILITY = 1 << 2, + NVME_SMART_CRIT_MEDIA = 1 << 3, + NVME_SMART_CRIT_VOLATILE_MEMORY = 1 << 4, +}; + +struct nvme_lba_range_type { + __u8 type; + __u8 attributes; + __u8 rsvd2[14]; + __u64 slba; + __u64 nlb; + __u8 guid[16]; + __u8 rsvd48[16]; +}; + +enum { + NVME_LBART_TYPE_FS = 0x01, + NVME_LBART_TYPE_RAID = 0x02, + NVME_LBART_TYPE_CACHE = 0x03, + NVME_LBART_TYPE_SWAP = 0x04, + + NVME_LBART_ATTRIB_TEMP = 1 << 0, + NVME_LBART_ATTRIB_HIDE = 1 << 1, +}; + +struct nvme_reservation_status { + __le32 gen; + __u8 rtype; + __u8 regctl[2]; + __u8 resv5[2]; + __u8 ptpls; + __u8 resv10[13]; + struct { + __le16 cntlid; + __u8 rcsts; + __u8 resv3[5]; + __le64 hostid; + __le64 rkey; + } regctl_ds[]; +}; + +/* I/O commands */ + +enum nvme_opcode { + nvme_cmd_flush = 0x00, + nvme_cmd_write = 0x01, + nvme_cmd_read = 0x02, + nvme_cmd_write_uncor = 0x04, + nvme_cmd_compare = 0x05, + nvme_cmd_write_zeroes = 0x08, + nvme_cmd_dsm = 0x09, + nvme_cmd_resv_register = 0x0d, + nvme_cmd_resv_report = 0x0e, + nvme_cmd_resv_acquire = 0x11, + nvme_cmd_resv_release = 0x15, +}; + +struct nvme_common_command { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __le32 cdw2[2]; + __le64 metadata; + __le64 prp1; + __le64 prp2; + __le32 cdw10[6]; +}; + +struct nvme_rw_command { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2; + __le64 metadata; + __le64 prp1; + __le64 prp2; + __le64 slba; + __le16 length; + __le16 control; + __le32 dsmgmt; + __le32 reftag; + __le16 apptag; + __le16 appmask; +}; + +enum { + NVME_RW_LR = 1 << 15, + NVME_RW_FUA = 1 << 14, + NVME_RW_DSM_FREQ_UNSPEC = 0, + NVME_RW_DSM_FREQ_TYPICAL = 1, + NVME_RW_DSM_FREQ_RARE = 2, + NVME_RW_DSM_FREQ_READS = 3, + NVME_RW_DSM_FREQ_WRITES = 4, + NVME_RW_DSM_FREQ_RW = 5, + NVME_RW_DSM_FREQ_ONCE = 6, + NVME_RW_DSM_FREQ_PREFETCH = 7, + NVME_RW_DSM_FREQ_TEMP = 8, + NVME_RW_DSM_LATENCY_NONE = 0 << 4, + NVME_RW_DSM_LATENCY_IDLE = 1 << 4, + NVME_RW_DSM_LATENCY_NORM = 2 << 4, + NVME_RW_DSM_LATENCY_LOW = 3 << 4, + NVME_RW_DSM_SEQ_REQ = 1 << 6, + NVME_RW_DSM_COMPRESSED = 1 << 7, + NVME_RW_PRINFO_PRCHK_REF = 1 << 10, + NVME_RW_PRINFO_PRCHK_APP = 1 << 11, + NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, + NVME_RW_PRINFO_PRACT = 1 << 13, +}; + +struct nvme_dsm_cmd { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[2]; + __le64 prp1; + __le64 prp2; + __le32 nr; + __le32 attributes; + __u32 rsvd12[4]; +}; + +enum { + NVME_DSMGMT_IDR = 1 << 0, + NVME_DSMGMT_IDW = 1 << 1, + NVME_DSMGMT_AD = 1 << 2, +}; + +struct nvme_dsm_range { + __le32 cattr; + __le32 nlb; + __le64 slba; +}; + +/* Admin commands */ + +enum nvme_admin_opcode { + nvme_admin_delete_sq = 0x00, + nvme_admin_create_sq = 0x01, + nvme_admin_get_log_page = 0x02, + nvme_admin_delete_cq = 0x04, + nvme_admin_create_cq = 0x05, + nvme_admin_identify = 0x06, + nvme_admin_abort_cmd = 0x08, + nvme_admin_set_features = 0x09, + nvme_admin_get_features = 0x0a, + nvme_admin_async_event = 0x0c, + nvme_admin_activate_fw = 0x10, + nvme_admin_download_fw = 0x11, + nvme_admin_format_nvm = 0x80, + nvme_admin_security_send = 0x81, + nvme_admin_security_recv = 0x82, +}; + +enum { + NVME_QUEUE_PHYS_CONTIG = (1 << 0), + NVME_CQ_IRQ_ENABLED = (1 << 1), + NVME_SQ_PRIO_URGENT = (0 << 1), + NVME_SQ_PRIO_HIGH = (1 << 1), + NVME_SQ_PRIO_MEDIUM = (2 << 1), + NVME_SQ_PRIO_LOW = (3 << 1), + NVME_FEAT_ARBITRATION = 0x01, + NVME_FEAT_POWER_MGMT = 0x02, + NVME_FEAT_LBA_RANGE = 0x03, + NVME_FEAT_TEMP_THRESH = 0x04, + NVME_FEAT_ERR_RECOVERY = 0x05, + NVME_FEAT_VOLATILE_WC = 0x06, + NVME_FEAT_NUM_QUEUES = 0x07, + NVME_FEAT_IRQ_COALESCE = 0x08, + NVME_FEAT_IRQ_CONFIG = 0x09, + NVME_FEAT_WRITE_ATOMIC = 0x0a, + NVME_FEAT_ASYNC_EVENT = 0x0b, + NVME_FEAT_AUTO_PST = 0x0c, + NVME_FEAT_SW_PROGRESS = 0x80, + NVME_FEAT_HOST_ID = 0x81, + NVME_FEAT_RESV_MASK = 0x82, + NVME_FEAT_RESV_PERSIST = 0x83, + NVME_LOG_ERROR = 0x01, + NVME_LOG_SMART = 0x02, + NVME_LOG_FW_SLOT = 0x03, + NVME_LOG_RESERVATION = 0x80, + NVME_FWACT_REPL = (0 << 3), + NVME_FWACT_REPL_ACTV = (1 << 3), + NVME_FWACT_ACTV = (2 << 3), +}; + +struct nvme_identify { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[2]; + __le64 prp1; + __le64 prp2; + __le32 cns; + __u32 rsvd11[5]; +}; + +struct nvme_features { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[2]; + __le64 prp1; + __le64 prp2; + __le32 fid; + __le32 dword11; + __u32 rsvd12[4]; +}; + +struct nvme_create_cq { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[5]; + __le64 prp1; + __u64 rsvd8; + __le16 cqid; + __le16 qsize; + __le16 cq_flags; + __le16 irq_vector; + __u32 rsvd12[4]; +}; + +struct nvme_create_sq { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[5]; + __le64 prp1; + __u64 rsvd8; + __le16 sqid; + __le16 qsize; + __le16 sq_flags; + __le16 cqid; + __u32 rsvd12[4]; +}; + +struct nvme_delete_queue { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[9]; + __le16 qid; + __u16 rsvd10; + __u32 rsvd11[5]; +}; + +struct nvme_abort_cmd { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[9]; + __le16 sqid; + __u16 cid; + __u32 rsvd11[5]; +}; + +struct nvme_download_firmware { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[5]; + __le64 prp1; + __le64 prp2; + __le32 numd; + __le32 offset; + __u32 rsvd12[4]; +}; + +struct nvme_format_cmd { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[4]; + __le32 cdw10; + __u32 rsvd11[5]; +}; + +struct nvme_command { + union { + struct nvme_common_command common; + struct nvme_rw_command rw; + struct nvme_identify identify; + struct nvme_features features; + struct nvme_create_cq create_cq; + struct nvme_create_sq create_sq; + struct nvme_delete_queue delete_queue; + struct nvme_download_firmware dlfw; + struct nvme_format_cmd format; + struct nvme_dsm_cmd dsm; + struct nvme_abort_cmd abort; + }; +}; + +enum { + NVME_SC_SUCCESS = 0x0, + NVME_SC_INVALID_OPCODE = 0x1, + NVME_SC_INVALID_FIELD = 0x2, + NVME_SC_CMDID_CONFLICT = 0x3, + NVME_SC_DATA_XFER_ERROR = 0x4, + NVME_SC_POWER_LOSS = 0x5, + NVME_SC_INTERNAL = 0x6, + NVME_SC_ABORT_REQ = 0x7, + NVME_SC_ABORT_QUEUE = 0x8, + NVME_SC_FUSED_FAIL = 0x9, + NVME_SC_FUSED_MISSING = 0xa, + NVME_SC_INVALID_NS = 0xb, + NVME_SC_CMD_SEQ_ERROR = 0xc, + NVME_SC_SGL_INVALID_LAST = 0xd, + NVME_SC_SGL_INVALID_COUNT = 0xe, + NVME_SC_SGL_INVALID_DATA = 0xf, + NVME_SC_SGL_INVALID_METADATA = 0x10, + NVME_SC_SGL_INVALID_TYPE = 0x11, + NVME_SC_LBA_RANGE = 0x80, + NVME_SC_CAP_EXCEEDED = 0x81, + NVME_SC_NS_NOT_READY = 0x82, + NVME_SC_RESERVATION_CONFLICT = 0x83, + NVME_SC_CQ_INVALID = 0x100, + NVME_SC_QID_INVALID = 0x101, + NVME_SC_QUEUE_SIZE = 0x102, + NVME_SC_ABORT_LIMIT = 0x103, + NVME_SC_ABORT_MISSING = 0x104, + NVME_SC_ASYNC_LIMIT = 0x105, + NVME_SC_FIRMWARE_SLOT = 0x106, + NVME_SC_FIRMWARE_IMAGE = 0x107, + NVME_SC_INVALID_VECTOR = 0x108, + NVME_SC_INVALID_LOG_PAGE = 0x109, + NVME_SC_INVALID_FORMAT = 0x10a, + NVME_SC_FIRMWARE_NEEDS_RESET = 0x10b, + NVME_SC_INVALID_QUEUE = 0x10c, + NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, + NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, + NVME_SC_FEATURE_NOT_PER_NS = 0x10f, + NVME_SC_FW_NEEDS_RESET_SUBSYS = 0x110, + NVME_SC_BAD_ATTRIBUTES = 0x180, + NVME_SC_INVALID_PI = 0x181, + NVME_SC_READ_ONLY = 0x182, + NVME_SC_WRITE_FAULT = 0x280, + NVME_SC_READ_ERROR = 0x281, + NVME_SC_GUARD_CHECK = 0x282, + NVME_SC_APPTAG_CHECK = 0x283, + NVME_SC_REFTAG_CHECK = 0x284, + NVME_SC_COMPARE_FAILED = 0x285, + NVME_SC_ACCESS_DENIED = 0x286, + NVME_SC_DNR = 0x4000, +}; + +struct nvme_completion { + __le32 result; /* Used by admin commands to return data */ + __u32 rsvd; + __le16 sq_head; /* how much of this queue may be reclaimed */ + __le16 sq_id; /* submission queue that generated this entry */ + __u16 command_id; /* of the command which completed */ + __le16 status; /* did the command fail, and if so, why? */ +}; + +struct nvme_user_io { + __u8 opcode; + __u8 flags; + __u16 control; + __u16 nblocks; + __u16 rsvd; + __u64 metadata; + __u64 addr; + __u64 slba; + __u32 dsmgmt; + __u32 reftag; + __u16 apptag; + __u16 appmask; +}; + +struct nvme_passthru_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 result; +}; + +/* + * Registers should always be accessed with double word or quad word + * accesses. Registers with 64-bit address pointers should be written + * to with dword accesses by writing the low dword first (ptr[0]), + * then the high dword (ptr[1]) second. + */ +static inline u64 nvme_readq(__le64 volatile *regs) +{ +#if BITS_PER_LONG == 64 + return readq(regs); +#else + __u32 *ptr = (__u32 *)regs; + u64 val_lo = readl(ptr); + u64 val_hi = readl(ptr + 1); + + return val_lo + (val_hi << 32); +#endif +} + +static inline void nvme_writeq(const u64 val, __le64 volatile *regs) +{ +#if BITS_PER_LONG == 64 + writeq(val, regs); +#else + __u32 *ptr = (__u32 *)regs; + u32 val_lo = lower_32_bits(val); + u32 val_hi = upper_32_bits(val); + writel(val_lo, ptr); + writel(val_hi, ptr + 1); +#endif +} + +struct nvme_bar { + __u64 cap; /* Controller Capabilities */ + __u32 vs; /* Version */ + __u32 intms; /* Interrupt Mask Set */ + __u32 intmc; /* Interrupt Mask Clear */ + __u32 cc; /* Controller Configuration */ + __u32 rsvd1; /* Reserved */ + __u32 csts; /* Controller Status */ + __u32 rsvd2; /* Reserved */ + __u32 aqa; /* Admin Queue Attributes */ + __u64 asq; /* Admin SQ Base Address */ + __u64 acq; /* Admin CQ Base Address */ +}; + +#define NVME_CAP_MQES(cap) ((cap) & 0xffff) +#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) +#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) +#define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) +#define NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) + +#define NVME_VS(major, minor) (((major) << 16) | ((minor) << 8)) + +enum { + NVME_CC_ENABLE = 1 << 0, + NVME_CC_CSS_NVM = 0 << 4, + NVME_CC_MPS_SHIFT = 7, + NVME_CC_ARB_RR = 0 << 11, + NVME_CC_ARB_WRRU = 1 << 11, + NVME_CC_ARB_VS = 7 << 11, + NVME_CC_SHN_NONE = 0 << 14, + NVME_CC_SHN_NORMAL = 1 << 14, + NVME_CC_SHN_ABRUPT = 2 << 14, + NVME_CC_SHN_MASK = 3 << 14, + NVME_CC_IOSQES = 6 << 16, + NVME_CC_IOCQES = 4 << 20, + NVME_CSTS_RDY = 1 << 0, + NVME_CSTS_CFS = 1 << 1, + NVME_CSTS_SHST_NORMAL = 0 << 2, + NVME_CSTS_SHST_OCCUR = 1 << 2, + NVME_CSTS_SHST_CMPLT = 2 << 2, + NVME_CSTS_SHST_MASK = 3 << 2, +}; + +/* Represents an NVM Express device. Each nvme_dev is a PCI function. */ +struct nvme_dev { + struct list_head node; + struct nvme_queue **queues; + u32 __iomem *dbs; + unsigned int cardnum; + struct udevice *pdev; + pci_dev_t pci_dev; + int instance; + uint8_t *hw_addr; + unsigned queue_count; + unsigned online_queues; + unsigned max_qid; + int q_depth; + u32 db_stride; + u32 ctrl_config; + struct nvme_bar __iomem *bar; + struct list_head namespaces; + const char *name; + char serial[20]; + char model[40]; + char firmware_rev[8]; + u32 max_transfer_shift; + u32 stripe_size; + u32 page_size; + u16 oncs; + u16 abort_limit; + u8 event_limit; + u8 vwc; + u64 *prp_pool; + u32 prp_entry_num; + u32 nn; + u32 blk_dev_start; +}; + +struct nvme_info { + int ns_num; /*the number of nvme namespaces*/ + int ndev_num; /*the number of nvme devices*/ + struct list_head dev_list; +}; + +/* + * The nvme_iod describes the data in an I/O, including the list of PRP + * entries. You can't see it in this data structure because C doesn't let + * me express that. Use nvme_alloc_iod to ensure there's enough space + * allocated to store the PRP list. + */ +struct nvme_iod { + unsigned long private; /* For the use of the submitter of the I/O */ + int npages; /* In the PRP list. 0 means small pool in use */ + int offset; /* Of PRP list */ + int nents; /* Used in scatterlist */ + int length; /* Of data, in bytes */ + dma_addr_t first_dma; +}; + +/* + * An NVM Express namespace is equivalent to a SCSI LUN. + * Each namespace is operated as an independent "device". + */ +struct nvme_ns { + struct list_head list; + struct nvme_dev *dev; + unsigned ns_id; + int devnum; + int lba_shift; + u16 ms; + u8 flbas; + u8 pi_type; + u64 mode_select_num_blocks; + u32 mode_select_block_len; +}; + +extern struct nvme_info *nvme_info; + +#endif /* __DRIVER_NVME_H__ */ diff --git a/drivers/nvme/nvme_show.c b/drivers/nvme/nvme_show.c new file mode 100644 index 0000000000..5577e5de45 --- /dev/null +++ b/drivers/nvme/nvme_show.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 NXP Semiconductors + * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <nvme.h> +#include "nvme.h" + +static void print_optional_admin_cmd(u16 oacs, int devnum) +{ + printf("Blk device %d: Optional Admin Command Support:\n", + devnum); + printf("\tNamespace Management/Attachment: %s\n", + oacs & 0x08 ? "yes" : "no"); + printf("\tFirmware Commit/Image download: %s\n", + oacs & 0x04 ? "yes" : "no"); + printf("\tFormat NVM: %s\n", + oacs & 0x02 ? "yes" : "no"); + printf("\tSecurity Send/Receive: %s\n", + oacs & 0x01 ? "yes" : "no"); +} + +static void print_optional_nvm_cmd(u16 oncs, int devnum) +{ + printf("Blk device %d: Optional NVM Command Support:\n", + devnum); + printf("\tReservation: %s\n", + oncs & 0x10 ? "yes" : "no"); + printf("\tSave/Select field in the Set/Get features: %s\n", + oncs & 0x08 ? "yes" : "no"); + printf("\tWrite Zeroes: %s\n", + oncs & 0x04 ? "yes" : "no"); + printf("\tDataset Management: %s\n", + oncs & 0x02 ? "yes" : "no"); + printf("\tWrite Uncorrectable: %s\n", + oncs & 0x01 ? "yes" : "no"); +} + +static void print_format_nvme_attributes(u8 fna, int devnum) +{ + printf("Blk device %d: Format NVM Attributes:\n", devnum); + printf("\tSupport Cryptographic Erase: %s\n", + fna & 0x04 ? "yes" : "No"); + printf("\tSupport erase a particular namespace: %s\n", + fna & 0x02 ? "No" : "Yes"); + printf("\tSupport format a particular namespace: %s\n", + fna & 0x01 ? "No" : "Yes"); +} + +static void print_format(struct nvme_lbaf *lbaf) +{ + u8 str[][10] = {"Best", "Better", "Good", "Degraded"}; + + printf("\t\tMetadata Size: %d\n", le16_to_cpu(lbaf->ms)); + printf("\t\tLBA Data Size: %d\n", 1 << lbaf->ds); + printf("\t\tRelative Performance: %s\n", str[lbaf->rp & 0x03]); +} + +static void print_formats(struct nvme_id_ns *id, struct nvme_ns *ns) +{ + int i; + + printf("Blk device %d: LBA Format Support:\n", ns->devnum); + + for (i = 0; i < id->nlbaf; i++) { + printf("\tLBA Foramt %d Support: ", i); + if (i == ns->flbas) + printf("(current)\n"); + else + printf("\n"); + print_format(id->lbaf + i); + } +} + +static void print_data_protect_cap(u8 dpc, int devnum) +{ + printf("Blk device %d: End-to-End Data", devnum); + printf("Protect Capabilities:\n"); + printf("\tAs last eight bytes: %s\n", + dpc & 0x10 ? "yes" : "No"); + printf("\tAs first eight bytes: %s\n", + dpc & 0x08 ? "yes" : "No"); + printf("\tSupport Type3: %s\n", + dpc & 0x04 ? "yes" : "No"); + printf("\tSupport Type2: %s\n", + dpc & 0x02 ? "yes" : "No"); + printf("\tSupport Type1: %s\n", + dpc & 0x01 ? "yes" : "No"); +} + +static void print_metadata_cap(u8 mc, int devnum) +{ + printf("Blk device %d: Metadata capabilities:\n", devnum); + printf("\tAs part of a separate buffer: %s\n", + mc & 0x02 ? "yes" : "No"); + printf("\tAs part of an extended data LBA: %s\n", + mc & 0x01 ? "yes" : "No"); +} + +int nvme_print_info(struct udevice *udev) +{ + struct nvme_ns *ns = dev_get_priv(udev); + struct nvme_dev *dev = ns->dev; + struct nvme_id_ns buf_ns, *id = &buf_ns; + struct nvme_id_ctrl buf_ctrl, *ctrl = &buf_ctrl; + + if (nvme_identify(dev, 0, 1, (dma_addr_t)ctrl)) + return -EIO; + + print_optional_admin_cmd(le16_to_cpu(ctrl->oacs), ns->devnum); + print_optional_nvm_cmd(le16_to_cpu(ctrl->oncs), ns->devnum); + print_format_nvme_attributes(ctrl->fna, ns->devnum); + + if (nvme_identify(dev, ns->ns_id, 0, (dma_addr_t)id)) + return -EIO; + + print_formats(id, ns); + print_data_protect_cap(id->dpc, ns->devnum); + print_metadata_cap(id->mc, ns->devnum); + + return 0; +} diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index af20cf0f3e..df76a94144 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -390,7 +390,7 @@ void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info) #ifdef CONFIG_SRIO_PCIE_BOOT_MASTER /* boot from PCIE --master */ - char *s = getenv("bootmaster"); + char *s = env_get("bootmaster"); char pcie[6]; sprintf(pcie, "PCIE%d", pci_info->pci_num); @@ -673,7 +673,7 @@ int fsl_pci_init_port(struct fsl_pci_info *pci_info, #ifdef CONFIG_SRIO_PCIE_BOOT_MASTER } else { /* boot from PCIE --master releases slave's core 0 */ - char *s = getenv("bootmaster"); + char *s = env_get("bootmaster"); char pcie[6]; sprintf(pcie, "PCIE%d", pci_info->pci_num); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6b36c187b5..bbc7dab366 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -427,7 +427,7 @@ int pci_hose_scan(struct pci_controller *hose) if (!gd->pcidelay_done) { /* wait "pcidelay" ms (if defined)... */ - s = getenv("pcidelay"); + s = env_get("pcidelay"); if (s) { int val = simple_strtoul(s, NULL, 10); for (i = 0; i < val; i++) @@ -459,7 +459,7 @@ void pci_init(void) hose_head = NULL; /* allow env to disable pci init/enum */ - if (getenv("pcidisable") != NULL) + if (env_get("pcidisable") != NULL) return; /* now call board specific pci_init()... */ diff --git a/drivers/pci/pci_common.c b/drivers/pci/pci_common.c index 6526de80db..faf25d96cb 100644 --- a/drivers/pci/pci_common.c +++ b/drivers/pci/pci_common.c @@ -89,7 +89,7 @@ __weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) /* * Only skip configuration if "pciconfighost" is not set */ - if (getenv("pciconfighost") == NULL) + if (env_get("pciconfighost") == NULL) return 1; #else return 1; diff --git a/drivers/pci/pcie_layerscape.c b/drivers/pci/pcie_layerscape.c index 78cde21cf4..610f85c4e8 100644 --- a/drivers/pci/pcie_layerscape.c +++ b/drivers/pci/pcie_layerscape.c @@ -478,6 +478,7 @@ static int ls_pcie_probe(struct udevice *dev) bool ep_mode; uint svr; int ret; + fdt_size_t cfg_size; pcie->bus = dev; @@ -539,8 +540,10 @@ static int ls_pcie_probe(struct udevice *dev) if (svr == SVR_LS2088A || svr == SVR_LS2084A || svr == SVR_LS2048A || svr == SVR_LS2044A || svr == SVR_LS2081A || svr == SVR_LS2041A) { + cfg_size = fdt_resource_size(&pcie->cfg_res); pcie->cfg_res.start = LS2088A_PCIE1_PHYS_ADDR + LS2088A_PCIE_PHYS_SIZE * pcie->idx; + pcie->cfg_res.end = pcie->cfg_res.start + cfg_size; pcie->ctrl = pcie->lut + 0x40000; } diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index b7e6188429..7d8cb5cb4c 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -5,5 +5,4 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_IDE_TI_CARDBUS) += ti_pci1410a.o obj-$(CONFIG_MARUBUN_PCCARD) += marubun_pcmcia.o diff --git a/drivers/pcmcia/ti_pci1410a.c b/drivers/pcmcia/ti_pci1410a.c deleted file mode 100644 index d83db3f022..0000000000 --- a/drivers/pcmcia/ti_pci1410a.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * (C) Copyright 2002 - * Daniel Engström, Omicron Ceti AB - * - * SPDX-License-Identifier: GPL-2.0+ - * - ******************************************************************** - * - * Lots of code copied from: - * - * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. - * (C) 1999-2000 Magnus Damm <damm@bitsmart.com> - * - * "The ExCA standard specifies that socket controllers should provide - * two IO and five memory windows per socket, which can be independently - * configured and positioned in the host address space and mapped to - * arbitrary segments of card address space. " - David A Hinds. 1999 - * - * This controller does _not_ meet the ExCA standard. - * - * m8xx pcmcia controller brief info: - * + 8 windows (attrib, mem, i/o) - * + up to two slots (SLOT_A and SLOT_B) - * + inputpins, outputpins, event and mask registers. - * - no offset register. sigh. - * - * Because of the lacking offset register we must map the whole card. - * We assign each memory window PCMCIA_MEM_WIN_SIZE address space. - * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO - * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE. - * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE. - * They are maximum 64KByte each... - */ - - -#undef DEBUG /**/ - -/* - * PCMCIA support - */ -#include <common.h> -#include <command.h> -#include <config.h> -#include <pci.h> -#include <asm/io.h> - -#include <pcmcia.h> - -#if defined(CONFIG_CMD_PCMCIA) - -int pcmcia_on(int ide_base_bus); - -static int hardware_disable(int slot); -static int hardware_enable(int slot); -static int voltage_set(int slot, int vcc, int vpp); -static void print_funcid(int func); -static void print_fixed(volatile char *p); -static int identify(volatile char *p); -static int check_ide_device(int slot, int ide_base_bus); - - -/* ------------------------------------------------------------------------- */ - - -const char *indent = "\t "; - -/* ------------------------------------------------------------------------- */ - - -static struct pci_device_id supported[] = { - { PCI_VENDOR_ID_TI, 0xac50 }, /* Ti PCI1410A */ - { PCI_VENDOR_ID_TI, 0xac56 }, /* Ti PCI1510 */ - { } -}; - -static pci_dev_t devbusfn; -static u32 socket_base; -static u32 pcmcia_cis_ptr; - -int pcmcia_on(int ide_base_bus) -{ - u16 dev_id; - u32 socket_status; - int slot = 0; - int cis_len; - u16 io_base; - u16 io_len; - - /* - * Find the CardBus PCI device(s). - */ - if ((devbusfn = pci_find_devices(supported, 0)) < 0) { - printf("Ti CardBus: not found\n"); - return 1; - } - - pci_read_config_word(devbusfn, PCI_DEVICE_ID, &dev_id); - - if (dev_id == 0xac56) { - debug("Enable PCMCIA Ti PCI1510\n"); - } else { - debug("Enable PCMCIA Ti PCI1410A\n"); - } - - pcmcia_cis_ptr = CONFIG_SYS_PCMCIA_CIS_WIN; - cis_len = CONFIG_SYS_PCMCIA_CIS_WIN_SIZE; - - io_base = CONFIG_SYS_PCMCIA_IO_WIN; - io_len = CONFIG_SYS_PCMCIA_IO_WIN_SIZE; - - /* - * Setup the PCI device. - */ - pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &socket_base); - socket_base &= ~0xf; - - socket_status = readl(socket_base+8); - if ((socket_status & 6) == 0) { - printf("Card Present: "); - - switch (socket_status & 0x3c00) { - - case 0x400: - printf("5V "); - break; - case 0x800: - printf("3.3V "); - break; - case 0xc00: - printf("3.3/5V "); - break; - default: - printf("unsupported Vcc "); - break; - } - switch (socket_status & 0x30) { - case 0x10: - printf("16bit PC-Card\n"); - break; - case 0x20: - printf("32bit CardBus Card\n"); - break; - default: - printf("8bit PC-Card\n"); - break; - } - } - - - writeb(0x41, socket_base + 0x806); /* Enable I/O window 0 and memory window 0 */ - writeb(0x0e, socket_base + 0x807); /* Reset I/O window options */ - - /* Careful: the linux yenta driver do not seem to reset the offset - * in the i/o windows, so leaving them non-zero is a problem */ - - writeb(io_base & 0xff, socket_base + 0x808); /* I/O window 0 base address */ - writeb(io_base>>8, socket_base + 0x809); - writeb((io_base + io_len - 1) & 0xff, socket_base + 0x80a); /* I/O window 0 end address */ - writeb((io_base + io_len - 1)>>8, socket_base + 0x80b); - writeb(0x00, socket_base + 0x836); /* I/O window 0 offset address 0x000 */ - writeb(0x00, socket_base + 0x837); - - - writeb((pcmcia_cis_ptr&0x000ff000) >> 12, - socket_base + 0x810); /* Memory window 0 start address bits 19-12 */ - writeb((pcmcia_cis_ptr&0x00f00000) >> 20, - socket_base + 0x811); /* Memory window 0 start address bits 23-20 */ - writeb(((pcmcia_cis_ptr+cis_len-1) & 0x000ff000) >> 12, - socket_base + 0x812); /* Memory window 0 end address bits 19-12*/ - writeb(((pcmcia_cis_ptr+cis_len-1) & 0x00f00000) >> 20, - socket_base + 0x813); /* Memory window 0 end address bits 23-20*/ - writeb(0x00, socket_base + 0x814); /* Memory window 0 offset bits 19-12 */ - writeb(0x40, socket_base + 0x815); /* Memory window 0 offset bits 23-20 and - * options (read/write, attribute access) */ - writeb(0x00, socket_base + 0x816); /* ExCA card-detect and general control */ - writeb(0x00, socket_base + 0x81e); /* ExCA global control (interrupt modes) */ - - writeb((pcmcia_cis_ptr & 0xff000000) >> 24, - socket_base + 0x840); /* Memory window address bits 31-24 */ - - - /* turn off voltage */ - if (voltage_set(slot, 0, 0)) { - return 1; - } - - /* Enable external hardware */ - if (hardware_enable(slot)) { - return 1; - } - - if (check_ide_device(slot, ide_base_bus)) { - return 1; - } - - return 0; -} - -/* ------------------------------------------------------------------------- */ - - -#if defined(CONFIG_CMD_PCMCIA) -int pcmcia_off (void) -{ - int slot = 0; - - writeb(0x00, socket_base + 0x806); /* disable all I/O and memory windows */ - - writeb(0x00, socket_base + 0x808); /* I/O window 0 base address */ - writeb(0x00, socket_base + 0x809); - writeb(0x00, socket_base + 0x80a); /* I/O window 0 end address */ - writeb(0x00, socket_base + 0x80b); - writeb(0x00, socket_base + 0x836); /* I/O window 0 offset address */ - writeb(0x00, socket_base + 0x837); - - writeb(0x00, socket_base + 0x80c); /* I/O window 1 base address */ - writeb(0x00, socket_base + 0x80d); - writeb(0x00, socket_base + 0x80e); /* I/O window 1 end address */ - writeb(0x00, socket_base + 0x80f); - writeb(0x00, socket_base + 0x838); /* I/O window 1 offset address */ - writeb(0x00, socket_base + 0x839); - - writeb(0x00, socket_base + 0x810); /* Memory window 0 start address */ - writeb(0x00, socket_base + 0x811); - writeb(0x00, socket_base + 0x812); /* Memory window 0 end address */ - writeb(0x00, socket_base + 0x813); - writeb(0x00, socket_base + 0x814); /* Memory window 0 offset */ - writeb(0x00, socket_base + 0x815); - - writeb(0xc0, socket_base + 0x840); /* Memory window 0 page address */ - - - /* turn off voltage */ - voltage_set(slot, 0, 0); - - /* disable external hardware */ - printf ("Shutdown and Poweroff Ti PCI1410A\n"); - hardware_disable(slot); - - return 0; -} - -#endif - -/* ------------------------------------------------------------------------- */ - - -#define MAX_TUPEL_SZ 512 -#define MAX_FEATURES 4 -int ide_devices_found; -static int check_ide_device(int slot, int ide_base_bus) -{ - volatile char *ident = NULL; - volatile char *feature_p[MAX_FEATURES]; - volatile char *p, *start; - int n_features = 0; - uchar func_id = ~0; - uchar code, len; - ushort config_base = 0; - int found = 0; - int i; - u32 socket_status; - - debug ("PCMCIA MEM: %08X\n", pcmcia_cis_ptr); - - socket_status = readl(socket_base+8); - - if ((socket_status & 6) != 0 || (socket_status & 0x20) != 0) { - printf("no card or CardBus card\n"); - return 1; - } - - start = p = (volatile char *) pcmcia_cis_ptr; - - while ((p - start) < MAX_TUPEL_SZ) { - - code = *p; p += 2; - - if (code == 0xFF) { /* End of chain */ - break; - } - - len = *p; p += 2; -#if defined(DEBUG) && (DEBUG > 1) - { - volatile uchar *q = p; - printf ("\nTuple code %02x length %d\n\tData:", - code, len); - - for (i = 0; i < len; ++i) { - printf (" %02x", *q); - q+= 2; - } - } -#endif /* DEBUG */ - switch (code) { - case CISTPL_VERS_1: - ident = p + 4; - break; - case CISTPL_FUNCID: - /* Fix for broken SanDisk which may have 0x80 bit set */ - func_id = *p & 0x7F; - break; - case CISTPL_FUNCE: - if (n_features < MAX_FEATURES) - feature_p[n_features++] = p; - break; - case CISTPL_CONFIG: - config_base = (*(p+6) << 8) + (*(p+4)); - debug ("\n## Config_base = %04x ###\n", config_base); - default: - break; - } - p += 2 * len; - } - - found = identify(ident); - - if (func_id != ((uchar)~0)) { - print_funcid (func_id); - - if (func_id == CISTPL_FUNCID_FIXED) - found = 1; - else - return 1; /* no disk drive */ - } - - for (i=0; i<n_features; ++i) { - print_fixed(feature_p[i]); - } - - if (!found) { - printf("unknown card type\n"); - return 1; - } - - /* select config index 1 */ - writeb(1, pcmcia_cis_ptr + config_base); - -#if 0 - printf("Confiuration Option Register: %02x\n", readb(pcmcia_cis_ptr + config_base)); - printf("Card Confiuration and Status Register: %02x\n", readb(pcmcia_cis_ptr + config_base + 2)); - printf("Pin Replacement Register Register: %02x\n", readb(pcmcia_cis_ptr + config_base + 4)); - printf("Socket and Copy Register: %02x\n", readb(pcmcia_cis_ptr + config_base + 6)); -#endif - ide_devices_found |= (1 << (slot+ide_base_bus)); - - return 0; -} - - -static int voltage_set(int slot, int vcc, int vpp) -{ - u32 socket_control; - int reg=0; - - switch (slot) { - case 0: - reg = socket_base + 0x10; - break; - default: - return 1; - } - - socket_control = 0; - - - switch (vcc) { - case 50: - socket_control |= 0x20; - break; - case 33: - socket_control |= 0x30; - break; - case 0: - default: ; - } - - switch (vpp) { - case 120: - socket_control |= 0x1; - break; - case 50: - socket_control |= 0x2; - break; - case 33: - socket_control |= 0x3; - break; - case 0: - default: ; - } - - writel(socket_control, reg); - - debug ("voltage_set: Ti PCI1410A Slot %d, Vcc=%d.%d, Vpp=%d.%d\n", - slot, vcc/10, vcc%10, vpp/10, vpp%10); - - udelay(500); - return 0; -} - - -static int hardware_enable(int slot) -{ - u32 socket_status; - u16 brg_ctrl; - int is_82365sl; - - socket_status = readl(socket_base+8); - - if ((socket_status & 6) == 0) { - - switch (socket_status & 0x3c00) { - - case 0x400: - printf("5V "); - voltage_set(slot, 50, 0); - break; - case 0x800: - voltage_set(slot, 33, 0); - break; - case 0xc00: - voltage_set(slot, 33, 0); - break; - default: - voltage_set(slot, 0, 0); - break; - } - } else { - voltage_set(slot, 0, 0); - } - - pci_read_config_word(devbusfn, PCI_BRIDGE_CONTROL, &brg_ctrl); - brg_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(devbusfn, PCI_BRIDGE_CONTROL, brg_ctrl); - is_82365sl = ((readb(socket_base+0x800) & 0x0f) == 2); - writeb(is_82365sl?0x90:0x98, socket_base+0x802); - writeb(0x67, socket_base+0x803); - udelay(100000); -#if 0 - printf("ExCA Id %02x, Card Status %02x, Power config %02x, Interrupt Config %02x, bridge control %04x %d\n", - readb(socket_base+0x800), readb(socket_base+0x801), - readb(socket_base+0x802), readb(socket_base+0x803), brg_ctrl, is_82365sl); -#endif - - return ((readb(socket_base+0x801)&0x6c)==0x6c)?0:1; -} - - -static int hardware_disable(int slot) -{ - voltage_set(slot, 0, 0); - return 0; -} - -static void print_funcid(int func) -{ - puts(indent); - switch (func) { - case CISTPL_FUNCID_MULTI: - puts(" Multi-Function"); - break; - case CISTPL_FUNCID_MEMORY: - puts(" Memory"); - break; - case CISTPL_FUNCID_SERIAL: - puts(" Serial Port"); - break; - case CISTPL_FUNCID_PARALLEL: - puts(" Parallel Port"); - break; - case CISTPL_FUNCID_FIXED: - puts(" Fixed Disk"); - break; - case CISTPL_FUNCID_VIDEO: - puts(" Video Adapter"); - break; - case CISTPL_FUNCID_NETWORK: - puts(" Network Adapter"); - break; - case CISTPL_FUNCID_AIMS: - puts(" AIMS Card"); - break; - case CISTPL_FUNCID_SCSI: - puts(" SCSI Adapter"); - break; - default: - puts(" Unknown"); - break; - } - puts(" Card\n"); -} - -/* ------------------------------------------------------------------------- */ - -static void print_fixed(volatile char *p) -{ - if (p == NULL) - return; - - puts(indent); - - switch (*p) { - case CISTPL_FUNCE_IDE_IFACE: - { uchar iface = *(p+2); - - puts ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown"); - puts (" interface "); - break; - } - case CISTPL_FUNCE_IDE_MASTER: - case CISTPL_FUNCE_IDE_SLAVE: - { - uchar f1 = *(p+2); - uchar f2 = *(p+4); - - puts((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]"); - - if (f1 & CISTPL_IDE_UNIQUE) { - puts(" [unique]"); - } - - puts((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]"); - - if (f2 & CISTPL_IDE_HAS_SLEEP) { - puts(" [sleep]"); - } - - if (f2 & CISTPL_IDE_HAS_STANDBY) { - puts(" [standby]"); - } - - if (f2 & CISTPL_IDE_HAS_IDLE) { - puts(" [idle]"); - } - - if (f2 & CISTPL_IDE_LOW_POWER) { - puts(" [low power]"); - } - - if (f2 & CISTPL_IDE_REG_INHIBIT) { - puts(" [reg inhibit]"); - } - - if (f2 & CISTPL_IDE_HAS_INDEX) { - puts(" [index]"); - } - - if (f2 & CISTPL_IDE_IOIS16) { - puts(" [IOis16]"); - } - - break; - } - } - putc('\n'); -} - -/* ------------------------------------------------------------------------- */ - -#define MAX_IDENT_CHARS 64 -#define MAX_IDENT_FIELDS 4 - -static char *known_cards[] = { - "ARGOSY PnPIDE D5", - NULL -}; - -static int identify(volatile char *p) -{ - char id_str[MAX_IDENT_CHARS]; - char data; - char *t; - char **card; - int i, done; - - if (p == NULL) - return (0); /* Don't know */ - - t = id_str; - done =0; - - for (i=0; i<=4 && !done; ++i, p+=2) { - while ((data = *p) != '\0') { - if (data == 0xFF) { - done = 1; - break; - } - *t++ = data; - if (t == &id_str[MAX_IDENT_CHARS-1]) { - done = 1; - break; - } - p += 2; - } - if (!done) - *t++ = ' '; - } - *t = '\0'; - while (--t > id_str) { - if (*t == ' ') { - *t = '\0'; - } else { - break; - } - } - puts(id_str); - putc('\n'); - - for (card=known_cards; *card; ++card) { - debug ("## Compare against \"%s\"\n", *card); - if (strcmp(*card, id_str) == 0) { /* found! */ - debug ("## CARD FOUND ##\n"); - return 1; - } - } - - return 0; /* don't know */ -} - -#endif /* CONFIG_CMD_PCMCIA */ diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3368.c b/drivers/pinctrl/rockchip/pinctrl_rk3368.c index bdf0758c0c..81ce2e31a7 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3368.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3368.c @@ -1,8 +1,11 @@ /* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * Author: Andy Yan <andy.yan@rock-chips.com> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH + * * SPDX-License-Identifier: GPL-2.0+ */ + #include <common.h> #include <dm.h> #include <errno.h> @@ -16,6 +19,428 @@ DECLARE_GLOBAL_DATA_PTR; +/* PMUGRF_GPIO0B_IOMUX */ +enum { + GPIO0B5_SHIFT = 10, + GPIO0B5_MASK = GENMASK(GPIO0B5_SHIFT + 1, GPIO0B5_SHIFT), + GPIO0B5_GPIO = 0, + GPIO0B5_SPI2_CSN0 = (2 << GPIO0B5_SHIFT), + + GPIO0B4_SHIFT = 8, + GPIO0B4_MASK = GENMASK(GPIO0B4_SHIFT + 1, GPIO0B4_SHIFT), + GPIO0B4_GPIO = 0, + GPIO0B4_SPI2_CLK = (2 << GPIO0B4_SHIFT), + + GPIO0B3_SHIFT = 6, + GPIO0B3_MASK = GENMASK(GPIO0B3_SHIFT + 1, GPIO0B3_SHIFT), + GPIO0B3_GPIO = 0, + GPIO0B3_SPI2_TXD = (2 << GPIO0B3_SHIFT), + + GPIO0B2_SHIFT = 4, + GPIO0B2_MASK = GENMASK(GPIO0B2_SHIFT + 1, GPIO0B2_SHIFT), + GPIO0B2_GPIO = 0, + GPIO0B2_SPI2_RXD = (2 << GPIO0B2_SHIFT), +}; + +/*GRF_GPIO0C_IOMUX*/ +enum { + GPIO0C7_SHIFT = 14, + GPIO0C7_MASK = GENMASK(GPIO0C7_SHIFT + 1, GPIO0C7_SHIFT), + GPIO0C7_GPIO = 0, + GPIO0C7_LCDC_D19 = (1 << GPIO0C7_SHIFT), + GPIO0C7_TRACE_D9 = (2 << GPIO0C7_SHIFT), + GPIO0C7_UART1_RTSN = (3 << GPIO0C7_SHIFT), + + GPIO0C6_SHIFT = 12, + GPIO0C6_MASK = GENMASK(GPIO0C6_SHIFT + 1, GPIO0C6_SHIFT), + GPIO0C6_GPIO = 0, + GPIO0C6_LCDC_D18 = (1 << GPIO0C6_SHIFT), + GPIO0C6_TRACE_D8 = (2 << GPIO0C6_SHIFT), + GPIO0C6_UART1_CTSN = (3 << GPIO0C6_SHIFT), + + GPIO0C5_SHIFT = 10, + GPIO0C5_MASK = GENMASK(GPIO0C5_SHIFT + 1, GPIO0C5_SHIFT), + GPIO0C5_GPIO = 0, + GPIO0C5_LCDC_D17 = (1 << GPIO0C5_SHIFT), + GPIO0C5_TRACE_D7 = (2 << GPIO0C5_SHIFT), + GPIO0C5_UART1_SOUT = (3 << GPIO0C5_SHIFT), + + GPIO0C4_SHIFT = 8, + GPIO0C4_MASK = GENMASK(GPIO0C4_SHIFT + 1, GPIO0C4_SHIFT), + GPIO0C4_GPIO = 0, + GPIO0C4_LCDC_D16 = (1 << GPIO0C4_SHIFT), + GPIO0C4_TRACE_D6 = (2 << GPIO0C4_SHIFT), + GPIO0C4_UART1_SIN = (3 << GPIO0C4_SHIFT), + + GPIO0C3_SHIFT = 6, + GPIO0C3_MASK = GENMASK(GPIO0C3_SHIFT + 1, GPIO0C3_SHIFT), + GPIO0C3_GPIO = 0, + GPIO0C3_LCDC_D15 = (1 << GPIO0C3_SHIFT), + GPIO0C3_TRACE_D5 = (2 << GPIO0C3_SHIFT), + GPIO0C3_MCU_JTAG_TDO = (3 << GPIO0C3_SHIFT), + + GPIO0C2_SHIFT = 4, + GPIO0C2_MASK = GENMASK(GPIO0C2_SHIFT + 1, GPIO0C2_SHIFT), + GPIO0C2_GPIO = 0, + GPIO0C2_LCDC_D14 = (1 << GPIO0C2_SHIFT), + GPIO0C2_TRACE_D4 = (2 << GPIO0C2_SHIFT), + GPIO0C2_MCU_JTAG_TDI = (3 << GPIO0C2_SHIFT), + + GPIO0C1_SHIFT = 2, + GPIO0C1_MASK = GENMASK(GPIO0C1_SHIFT + 1, GPIO0C1_SHIFT), + GPIO0C1_GPIO = 0, + GPIO0C1_LCDC_D13 = (1 << GPIO0C1_SHIFT), + GPIO0C1_TRACE_D3 = (2 << GPIO0C1_SHIFT), + GPIO0C1_MCU_JTAG_TRTSN = (3 << GPIO0C1_SHIFT), + + GPIO0C0_SHIFT = 0, + GPIO0C0_MASK = GENMASK(GPIO0C0_SHIFT + 1, GPIO0C0_SHIFT), + GPIO0C0_GPIO = 0, + GPIO0C0_LCDC_D12 = (1 << GPIO0C0_SHIFT), + GPIO0C0_TRACE_D2 = (2 << GPIO0C0_SHIFT), + GPIO0C0_MCU_JTAG_TDO = (3 << GPIO0C0_SHIFT), +}; + +/*GRF_GPIO0D_IOMUX*/ +enum { + GPIO0D7_SHIFT = 14, + GPIO0D7_MASK = GENMASK(GPIO0D7_SHIFT + 1, GPIO0D7_SHIFT), + GPIO0D7_GPIO = 0, + GPIO0D7_LCDC_DCLK = (1 << GPIO0D7_SHIFT), + GPIO0D7_TRACE_CTL = (2 << GPIO0D7_SHIFT), + GPIO0D7_PMU_DEBUG5 = (3 << GPIO0D7_SHIFT), + + GPIO0D6_SHIFT = 12, + GPIO0D6_MASK = GENMASK(GPIO0D6_SHIFT + 1, GPIO0D6_SHIFT), + GPIO0D6_GPIO = 0, + GPIO0D6_LCDC_DEN = (1 << GPIO0D6_SHIFT), + GPIO0D6_TRACE_CLK = (2 << GPIO0D6_SHIFT), + GPIO0D6_PMU_DEBUG4 = (3 << GPIO0D6_SHIFT), + + GPIO0D5_SHIFT = 10, + GPIO0D5_MASK = GENMASK(GPIO0D5_SHIFT + 1, GPIO0D5_SHIFT), + GPIO0D5_GPIO = 0, + GPIO0D5_LCDC_VSYNC = (1 << GPIO0D5_SHIFT), + GPIO0D5_TRACE_D15 = (2 << GPIO0D5_SHIFT), + GPIO0D5_PMU_DEBUG3 = (3 << GPIO0D5_SHIFT), + + GPIO0D4_SHIFT = 8, + GPIO0D4_MASK = GENMASK(GPIO0D4_SHIFT + 1, GPIO0D4_SHIFT), + GPIO0D4_GPIO = 0, + GPIO0D4_LCDC_HSYNC = (1 << GPIO0D4_SHIFT), + GPIO0D4_TRACE_D14 = (2 << GPIO0D4_SHIFT), + GPIO0D4_PMU_DEBUG2 = (3 << GPIO0D4_SHIFT), + + GPIO0D3_SHIFT = 6, + GPIO0D3_MASK = GENMASK(GPIO0D3_SHIFT + 1, GPIO0D3_SHIFT), + GPIO0D3_GPIO = 0, + GPIO0D3_LCDC_D23 = (1 << GPIO0D3_SHIFT), + GPIO0D3_TRACE_D13 = (2 << GPIO0D3_SHIFT), + GPIO0D3_UART4_SIN = (3 << GPIO0D3_SHIFT), + + GPIO0D2_SHIFT = 4, + GPIO0D2_MASK = GENMASK(GPIO0D2_SHIFT + 1, GPIO0D2_SHIFT), + GPIO0D2_GPIO = 0, + GPIO0D2_LCDC_D22 = (1 << GPIO0D2_SHIFT), + GPIO0D2_TRACE_D12 = (2 << GPIO0D2_SHIFT), + GPIO0D2_UART4_SOUT = (3 << GPIO0D2_SHIFT), + + GPIO0D1_SHIFT = 2, + GPIO0D1_MASK = GENMASK(GPIO0D1_SHIFT + 1, GPIO0D1_SHIFT), + GPIO0D1_GPIO = 0, + GPIO0D1_LCDC_D21 = (1 << GPIO0D1_SHIFT), + GPIO0D1_TRACE_D11 = (2 << GPIO0D1_SHIFT), + GPIO0D1_UART4_RTSN = (3 << GPIO0D1_SHIFT), + + GPIO0D0_SHIFT = 0, + GPIO0D0_MASK = GENMASK(GPIO0D0_SHIFT + 1, GPIO0D0_SHIFT), + GPIO0D0_GPIO = 0, + GPIO0D0_LCDC_D20 = (1 << GPIO0D0_SHIFT), + GPIO0D0_TRACE_D10 = (2 << GPIO0D0_SHIFT), + GPIO0D0_UART4_CTSN = (3 << GPIO0D0_SHIFT), +}; + +/*GRF_GPIO2A_IOMUX*/ +enum { + GPIO2A7_SHIFT = 14, + GPIO2A7_MASK = GENMASK(GPIO2A7_SHIFT + 1, GPIO2A7_SHIFT), + GPIO2A7_GPIO = 0, + GPIO2A7_SDMMC0_D2 = (1 << GPIO2A7_SHIFT), + GPIO2A7_JTAG_TCK = (2 << GPIO2A7_SHIFT), + + GPIO2A6_SHIFT = 12, + GPIO2A6_MASK = GENMASK(GPIO2A6_SHIFT + 1, GPIO2A6_SHIFT), + GPIO2A6_GPIO = 0, + GPIO2A6_SDMMC0_D1 = (1 << GPIO2A6_SHIFT), + GPIO2A6_UART2_SIN = (2 << GPIO2A6_SHIFT), + + GPIO2A5_SHIFT = 10, + GPIO2A5_MASK = GENMASK(GPIO2A5_SHIFT + 1, GPIO2A5_SHIFT), + GPIO2A5_GPIO = 0, + GPIO2A5_SDMMC0_D0 = (1 << GPIO2A5_SHIFT), + GPIO2A5_UART2_SOUT = (2 << GPIO2A5_SHIFT), + + GPIO2A4_SHIFT = 8, + GPIO2A4_MASK = GENMASK(GPIO2A4_SHIFT + 1, GPIO2A4_SHIFT), + GPIO2A4_GPIO = 0, + GPIO2A4_FLASH_DQS = (1 << GPIO2A4_SHIFT), + GPIO2A4_EMMC_CLKOUT = (2 << GPIO2A4_SHIFT), + + GPIO2A3_SHIFT = 6, + GPIO2A3_MASK = GENMASK(GPIO2A3_SHIFT + 1, GPIO2A3_SHIFT), + GPIO2A3_GPIO = 0, + GPIO2A3_FLASH_CSN3 = (1 << GPIO2A3_SHIFT), + GPIO2A3_EMMC_RSTNOUT = (2 << GPIO2A3_SHIFT), + + GPIO2A2_SHIFT = 4, + GPIO2A2_MASK = GENMASK(GPIO2A2_SHIFT + 1, GPIO2A2_SHIFT), + GPIO2A2_GPIO = 0, + GPIO2A2_FLASH_CSN2 = (1 << GPIO2A2_SHIFT), + + GPIO2A1_SHIFT = 2, + GPIO2A1_MASK = GENMASK(GPIO2A1_SHIFT + 1, GPIO2A1_SHIFT), + GPIO2A1_GPIO = 0, + GPIO2A1_FLASH_CSN1 = (1 << GPIO2A1_SHIFT), + + GPIO2A0_SHIFT = 0, + GPIO2A0_MASK = GENMASK(GPIO2A0_SHIFT + 1, GPIO2A0_SHIFT), + GPIO2A0_GPIO = 0, + GPIO2A0_FLASH_CSN0 = (1 << GPIO2A0_SHIFT), +}; + +/*GRF_GPIO2D_IOMUX*/ +enum { + GPIO2D7_SHIFT = 14, + GPIO2D7_MASK = GENMASK(GPIO2D7_SHIFT + 1, GPIO2D7_SHIFT), + GPIO2D7_GPIO = 0, + GPIO2D7_SDIO0_D3 = (1 << GPIO2D7_SHIFT), + + GPIO2D6_SHIFT = 12, + GPIO2D6_MASK = GENMASK(GPIO2D6_SHIFT + 1, GPIO2D6_SHIFT), + GPIO2D6_GPIO = 0, + GPIO2D6_SDIO0_D2 = (1 << GPIO2D6_SHIFT), + + GPIO2D5_SHIFT = 10, + GPIO2D5_MASK = GENMASK(GPIO2D5_SHIFT + 1, GPIO2D5_SHIFT), + GPIO2D5_GPIO = 0, + GPIO2D5_SDIO0_D1 = (1 << GPIO2D5_SHIFT), + + GPIO2D4_SHIFT = 8, + GPIO2D4_MASK = GENMASK(GPIO2D4_SHIFT + 1, GPIO2D4_SHIFT), + GPIO2D4_GPIO = 0, + GPIO2D4_SDIO0_D0 = (1 << GPIO2D4_SHIFT), + + GPIO2D3_SHIFT = 6, + GPIO2D3_MASK = GENMASK(GPIO2D3_SHIFT + 1, GPIO2D3_SHIFT), + GPIO2D3_GPIO = 0, + GPIO2D3_UART0_RTS0 = (1 << GPIO2D3_SHIFT), + + GPIO2D2_SHIFT = 4, + GPIO2D2_MASK = GENMASK(GPIO2D2_SHIFT + 1, GPIO2D2_SHIFT), + GPIO2D2_GPIO = 0, + GPIO2D2_UART0_CTS0 = (1 << GPIO2D2_SHIFT), + + GPIO2D1_SHIFT = 2, + GPIO2D1_MASK = GENMASK(GPIO2D1_SHIFT + 1, GPIO2D1_SHIFT), + GPIO2D1_GPIO = 0, + GPIO2D1_UART0_SOUT = (1 << GPIO2D1_SHIFT), + + GPIO2D0_SHIFT = 0, + GPIO2D0_MASK = GENMASK(GPIO2D0_SHIFT + 1, GPIO2D0_SHIFT), + GPIO2D0_GPIO = 0, + GPIO2D0_UART0_SIN = (1 << GPIO2D0_SHIFT), +}; + +/* GRF_GPIO1B_IOMUX */ +enum { + GPIO1B7_SHIFT = 14, + GPIO1B7_MASK = GENMASK(GPIO1B7_SHIFT + 1, GPIO1B7_SHIFT), + GPIO1B7_GPIO = 0, + GPIO1B7_SPI1_CSN0 = (2 << GPIO1B7_SHIFT), + + GPIO1B6_SHIFT = 12, + GPIO1B6_MASK = GENMASK(GPIO1B6_SHIFT + 1, GPIO1B6_SHIFT), + GPIO1B6_GPIO = 0, + GPIO1B6_SPI1_CLK = (2 << GPIO1B6_SHIFT), +}; + +/* GRF_GPIO1C_IOMUX */ +enum { + GPIO1C7_SHIFT = 14, + GPIO1C7_MASK = GENMASK(GPIO1C7_SHIFT + 1, GPIO1C7_SHIFT), + GPIO1C7_GPIO = 0, + GPIO1C7_EMMC_DATA5 = (2 << GPIO1C7_SHIFT), + GPIO1C7_SPI0_TXD = (3 << GPIO1C7_SHIFT), + + GPIO1C6_SHIFT = 12, + GPIO1C6_MASK = GENMASK(GPIO1C6_SHIFT + 1, GPIO1C6_SHIFT), + GPIO1C6_GPIO = 0, + GPIO1C6_EMMC_DATA4 = (2 << GPIO1C6_SHIFT), + GPIO1C6_SPI0_RXD = (3 << GPIO1C6_SHIFT), + + GPIO1C5_SHIFT = 10, + GPIO1C5_MASK = GENMASK(GPIO1C5_SHIFT + 1, GPIO1C5_SHIFT), + GPIO1C5_GPIO = 0, + GPIO1C5_EMMC_DATA3 = (2 << GPIO1C5_SHIFT), + + GPIO1C4_SHIFT = 8, + GPIO1C4_MASK = GENMASK(GPIO1C4_SHIFT + 1, GPIO1C4_SHIFT), + GPIO1C4_GPIO = 0, + GPIO1C4_EMMC_DATA2 = (2 << GPIO1C4_SHIFT), + + GPIO1C3_SHIFT = 6, + GPIO1C3_MASK = GENMASK(GPIO1C3_SHIFT + 1, GPIO1C3_SHIFT), + GPIO1C3_GPIO = 0, + GPIO1C3_EMMC_DATA1 = (2 << GPIO1C3_SHIFT), + + GPIO1C2_SHIFT = 4, + GPIO1C2_MASK = GENMASK(GPIO1C2_SHIFT + 1, GPIO1C2_SHIFT), + GPIO1C2_GPIO = 0, + GPIO1C2_EMMC_DATA0 = (2 << GPIO1C2_SHIFT), + + GPIO1C1_SHIFT = 2, + GPIO1C1_MASK = GENMASK(GPIO1C1_SHIFT + 1, GPIO1C1_SHIFT), + GPIO1C1_GPIO = 0, + GPIO1C1_SPI1_RXD = (2 << GPIO1C1_SHIFT), + + GPIO1C0_SHIFT = 0, + GPIO1C0_MASK = GENMASK(GPIO1C0_SHIFT + 1, GPIO1C0_SHIFT), + GPIO1C0_GPIO = 0, + GPIO1C0_SPI1_TXD = (2 << GPIO1C0_SHIFT), +}; + +/* GRF_GPIO1D_IOMUX*/ +enum { + GPIO1D5_SHIFT = 10, + GPIO1D5_MASK = GENMASK(GPIO1D5_SHIFT + 1, GPIO1D5_SHIFT), + GPIO1D5_GPIO = 0, + GPIO1D5_SPI0_CLK = (2 << GPIO1D5_SHIFT), + + GPIO1D3_SHIFT = 6, + GPIO1D3_MASK = GENMASK(GPIO1D3_SHIFT + 1, GPIO1D3_SHIFT), + GPIO1D3_GPIO = 0, + GPIO1D3_EMMC_PWREN = (2 << GPIO1D3_SHIFT), + + GPIO1D2_SHIFT = 4, + GPIO1D2_MASK = GENMASK(GPIO1D2_SHIFT + 1, GPIO1D2_SHIFT), + GPIO1D2_GPIO = 0, + GPIO1D2_EMMC_CMD = (2 << GPIO1D2_SHIFT), + + GPIO1D1_SHIFT = 2, + GPIO1D1_MASK = GENMASK(GPIO1D1_SHIFT + 1, GPIO1D1_SHIFT), + GPIO1D1_GPIO = 0, + GPIO1D1_EMMC_DATA7 = (2 << GPIO1D1_SHIFT), + GPIO1D1_SPI0_CSN1 = (3 << GPIO1D1_SHIFT), + + GPIO1D0_SHIFT = 0, + GPIO1D0_MASK = GENMASK(GPIO1D0_SHIFT + 1, GPIO1D0_SHIFT), + GPIO1D0_GPIO = 0, + GPIO1D0_EMMC_DATA6 = (2 << GPIO1D0_SHIFT), + GPIO1D0_SPI0_CSN0 = (3 << GPIO1D0_SHIFT), +}; + + +/*GRF_GPIO3B_IOMUX*/ +enum { + GPIO3B7_SHIFT = 14, + GPIO3B7_MASK = GENMASK(GPIO3B7_SHIFT + 1, GPIO3B7_SHIFT), + GPIO3B7_GPIO = 0, + GPIO3B7_MAC_RXD0 = (1 << GPIO3B7_SHIFT), + + GPIO3B6_SHIFT = 12, + GPIO3B6_MASK = GENMASK(GPIO3B6_SHIFT + 1, GPIO3B6_SHIFT), + GPIO3B6_GPIO = 0, + GPIO3B6_MAC_TXD3 = (1 << GPIO3B6_SHIFT), + + GPIO3B5_SHIFT = 10, + GPIO3B5_MASK = GENMASK(GPIO3B5_SHIFT + 1, GPIO3B5_SHIFT), + GPIO3B5_GPIO = 0, + GPIO3B5_MAC_TXEN = (1 << GPIO3B5_SHIFT), + + GPIO3B4_SHIFT = 8, + GPIO3B4_MASK = GENMASK(GPIO3B4_SHIFT + 1, GPIO3B4_SHIFT), + GPIO3B4_GPIO = 0, + GPIO3B4_MAC_COL = (1 << GPIO3B4_SHIFT), + + GPIO3B3_SHIFT = 6, + GPIO3B3_MASK = GENMASK(GPIO3B3_SHIFT + 1, GPIO3B3_SHIFT), + GPIO3B3_GPIO = 0, + GPIO3B3_MAC_CRS = (1 << GPIO3B3_SHIFT), + + GPIO3B2_SHIFT = 4, + GPIO3B2_MASK = GENMASK(GPIO3B2_SHIFT + 1, GPIO3B2_SHIFT), + GPIO3B2_GPIO = 0, + GPIO3B2_MAC_TXD2 = (1 << GPIO3B2_SHIFT), + + GPIO3B1_SHIFT = 2, + GPIO3B1_MASK = GENMASK(GPIO3B1_SHIFT + 1, GPIO3B1_SHIFT), + GPIO3B1_GPIO = 0, + GPIO3B1_MAC_TXD1 = (1 << GPIO3B1_SHIFT), + + GPIO3B0_SHIFT = 0, + GPIO3B0_MASK = GENMASK(GPIO3B0_SHIFT + 1, GPIO3B0_SHIFT), + GPIO3B0_GPIO = 0, + GPIO3B0_MAC_TXD0 = (1 << GPIO3B0_SHIFT), + GPIO3B0_PWM0 = (2 << GPIO3B0_SHIFT), +}; + +/*GRF_GPIO3C_IOMUX*/ +enum { + GPIO3C6_SHIFT = 12, + GPIO3C6_MASK = GENMASK(GPIO3C6_SHIFT + 1, GPIO3C6_SHIFT), + GPIO3C6_GPIO = 0, + GPIO3C6_MAC_CLK = (1 << GPIO3C6_SHIFT), + + GPIO3C5_SHIFT = 10, + GPIO3C5_MASK = GENMASK(GPIO3C5_SHIFT + 1, GPIO3C5_SHIFT), + GPIO3C5_GPIO = 0, + GPIO3C5_MAC_RXEN = (1 << GPIO3C5_SHIFT), + + GPIO3C4_SHIFT = 8, + GPIO3C4_MASK = GENMASK(GPIO3C4_SHIFT + 1, GPIO3C4_SHIFT), + GPIO3C4_GPIO = 0, + GPIO3C4_MAC_RXDV = (1 << GPIO3C4_SHIFT), + + GPIO3C3_SHIFT = 6, + GPIO3C3_MASK = GENMASK(GPIO3C3_SHIFT + 1, GPIO3C3_SHIFT), + GPIO3C3_GPIO = 0, + GPIO3C3_MAC_MDC = (1 << GPIO3C3_SHIFT), + + GPIO3C2_SHIFT = 4, + GPIO3C2_MASK = GENMASK(GPIO3C2_SHIFT + 1, GPIO3C2_SHIFT), + GPIO3C2_GPIO = 0, + GPIO3C2_MAC_RXD3 = (1 << GPIO3C2_SHIFT), + + GPIO3C1_SHIFT = 2, + GPIO3C1_MASK = GENMASK(GPIO3C1_SHIFT + 1, GPIO3C1_SHIFT), + GPIO3C1_GPIO = 0, + GPIO3C1_MAC_RXD2 = (1 << GPIO3C1_SHIFT), + + GPIO3C0_SHIFT = 0, + GPIO3C0_MASK = GENMASK(GPIO3C0_SHIFT + 1, GPIO3C0_SHIFT), + GPIO3C0_GPIO = 0, + GPIO3C0_MAC_RXD1 = (1 << GPIO3C0_SHIFT), +}; + +/*GRF_GPIO3D_IOMUX*/ +enum { + GPIO3D4_SHIFT = 8, + GPIO3D4_MASK = GENMASK(GPIO3D4_SHIFT + 1, GPIO3D4_SHIFT), + GPIO3D4_GPIO = 0, + GPIO3D4_MAC_TXCLK = (1 << GPIO3D4_SHIFT), + GPIO3D4_SPI1_CNS1 = (2 << GPIO3D4_SHIFT), + + GPIO3D1_SHIFT = 2, + GPIO3D1_MASK = GENMASK(GPIO3D1_SHIFT + 1, GPIO3D1_SHIFT), + GPIO3D1_GPIO = 0, + GPIO3D1_MAC_RXCLK = (1 << GPIO3D1_SHIFT), + + GPIO3D0_SHIFT = 0, + GPIO3D0_MASK = GENMASK(GPIO3D0_SHIFT + 1, GPIO3D0_SHIFT), + GPIO3D0_GPIO = 0, + GPIO3D0_MAC_MDIO = (1 << GPIO3D0_SHIFT), +}; + struct rk3368_pinctrl_priv { struct rk3368_grf *grf; struct rk3368_pmu_grf *pmugrf; @@ -31,8 +456,7 @@ static void pinctrl_rk3368_uart_config(struct rk3368_pinctrl_priv *priv, case PERIPH_ID_UART2: rk_clrsetreg(&grf->gpio2a_iomux, GPIO2A6_MASK | GPIO2A5_MASK, - GPIO2A6_UART2_SIN << GPIO2A6_SHIFT | - GPIO2A5_UART2_SOUT << GPIO2A5_SHIFT); + GPIO2A6_UART2_SIN | GPIO2A5_UART2_SOUT); break; case PERIPH_ID_UART0: break; @@ -44,10 +468,8 @@ static void pinctrl_rk3368_uart_config(struct rk3368_pinctrl_priv *priv, rk_clrsetreg(&pmugrf->gpio0d_iomux, GPIO0D0_MASK | GPIO0D1_MASK | GPIO0D2_MASK | GPIO0D3_MASK, - GPIO0D0_GPIO << GPIO0D0_SHIFT | - GPIO0D1_GPIO << GPIO0D1_SHIFT | - GPIO0D2_UART4_SOUT << GPIO0D2_SHIFT | - GPIO0D3_UART4_SIN << GPIO0D3_SHIFT); + GPIO0D0_GPIO | GPIO0D1_GPIO | + GPIO0D2_UART4_SOUT | GPIO0D3_UART4_SIN); break; default: debug("uart id = %d iomux error!\n", uart_id); @@ -55,6 +477,121 @@ static void pinctrl_rk3368_uart_config(struct rk3368_pinctrl_priv *priv, } } +static void pinctrl_rk3368_spi_config(struct rk3368_pinctrl_priv *priv, + int spi_id) +{ + struct rk3368_grf *grf = priv->grf; + struct rk3368_pmu_grf *pmugrf = priv->pmugrf; + + switch (spi_id) { + case PERIPH_ID_SPI0: + /* + * eMMC can only be connected with 4 bits, when SPI0 is used. + * This is all-or-nothing, so we assume that if someone asks us + * to configure SPI0, that their eMMC interface is unused or + * configured appropriately. + */ + rk_clrsetreg(&grf->gpio1d_iomux, + GPIO1D0_MASK | GPIO1D1_MASK | + GPIO1D5_MASK, + GPIO1D0_SPI0_CSN0 | GPIO1D1_SPI0_CSN1 | + GPIO1D5_SPI0_CLK); + rk_clrsetreg(&grf->gpio1c_iomux, + GPIO1C6_MASK | GPIO1C7_MASK, + GPIO1C6_SPI0_RXD | GPIO1C7_SPI0_TXD); + break; + case PERIPH_ID_SPI1: + /* + * We don't implement support for configuring SPI1_CSN#1, as it + * conflicts with the GMAC (MAC TX clk-out). + */ + rk_clrsetreg(&grf->gpio1b_iomux, + GPIO1B6_MASK | GPIO1B7_MASK, + GPIO1B6_SPI1_CLK | GPIO1B7_SPI1_CSN0); + rk_clrsetreg(&grf->gpio1c_iomux, + GPIO1C0_MASK | GPIO1C1_MASK, + GPIO1C0_SPI1_TXD | GPIO1C1_SPI1_RXD); + break; + case PERIPH_ID_SPI2: + rk_clrsetreg(&pmugrf->gpio0b_iomux, + GPIO0B2_MASK | GPIO0B3_MASK | + GPIO0B4_MASK | GPIO0B5_MASK, + GPIO0B2_SPI2_RXD | GPIO0B3_SPI2_TXD | + GPIO0B4_SPI2_CLK | GPIO0B5_SPI2_CSN0); + break; + default: + debug("%s: spi id = %d iomux error!\n", __func__, spi_id); + break; + } +} + +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) +static void pinctrl_rk3368_gmac_config(struct rk3368_grf *grf, int gmac_id) +{ + rk_clrsetreg(&grf->gpio3b_iomux, + GPIO3B0_MASK | GPIO3B1_MASK | + GPIO3B2_MASK | GPIO3B5_MASK | + GPIO3B6_MASK | GPIO3B7_MASK, + GPIO3B0_MAC_TXD0 | GPIO3B1_MAC_TXD1 | + GPIO3B2_MAC_TXD2 | GPIO3B5_MAC_TXEN | + GPIO3B6_MAC_TXD3 | GPIO3B7_MAC_RXD0); + rk_clrsetreg(&grf->gpio3c_iomux, + GPIO3C0_MASK | GPIO3C1_MASK | + GPIO3C2_MASK | GPIO3C3_MASK | + GPIO3C4_MASK | GPIO3C5_MASK | + GPIO3C6_MASK, + GPIO3C0_MAC_RXD1 | GPIO3C1_MAC_RXD2 | + GPIO3C2_MAC_RXD3 | GPIO3C3_MAC_MDC | + GPIO3C4_MAC_RXDV | GPIO3C5_MAC_RXEN | + GPIO3C6_MAC_CLK); + rk_clrsetreg(&grf->gpio3d_iomux, + GPIO3D0_MASK | GPIO3D1_MASK | + GPIO3D4_MASK, + GPIO3D0_MAC_MDIO | GPIO3D1_MAC_RXCLK | + GPIO3D4_MAC_TXCLK); +} +#endif + +static void pinctrl_rk3368_sdmmc_config(struct rk3368_grf *grf, int mmc_id) +{ + switch (mmc_id) { + case PERIPH_ID_EMMC: + debug("mmc id = %d setting registers!\n", mmc_id); + rk_clrsetreg(&grf->gpio1c_iomux, + GPIO1C2_MASK | GPIO1C3_MASK | + GPIO1C4_MASK | GPIO1C5_MASK | + GPIO1C6_MASK | GPIO1C7_MASK, + GPIO1C2_EMMC_DATA0 | + GPIO1C3_EMMC_DATA1 | + GPIO1C4_EMMC_DATA2 | + GPIO1C5_EMMC_DATA3 | + GPIO1C6_EMMC_DATA4 | + GPIO1C7_EMMC_DATA5); + rk_clrsetreg(&grf->gpio1d_iomux, + GPIO1D0_MASK | GPIO1D1_MASK | + GPIO1D2_MASK | GPIO1D3_MASK, + GPIO1D0_EMMC_DATA6 | + GPIO1D1_EMMC_DATA7 | + GPIO1D2_EMMC_CMD | + GPIO1D3_EMMC_PWREN); + rk_clrsetreg(&grf->gpio2a_iomux, + GPIO2A3_MASK | GPIO2A4_MASK, + GPIO2A3_EMMC_RSTNOUT | + GPIO2A4_EMMC_CLKOUT); + break; + case PERIPH_ID_SDCARD: + /* + * We assume that the BROM has already set this up + * correctly for us and that there's nothing to do + * here. + */ + break; + default: + debug("mmc id = %d iomux error!\n", mmc_id); + break; + } +} + static int rk3368_pinctrl_request(struct udevice *dev, int func, int flags) { struct rk3368_pinctrl_priv *priv = dev_get_priv(dev); @@ -68,6 +605,20 @@ static int rk3368_pinctrl_request(struct udevice *dev, int func, int flags) case PERIPH_ID_UART4: pinctrl_rk3368_uart_config(priv, func); break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + pinctrl_rk3368_spi_config(priv, func); + break; + case PERIPH_ID_EMMC: + case PERIPH_ID_SDCARD: + pinctrl_rk3368_sdmmc_config(priv->grf, func); + break; +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) + case PERIPH_ID_GMAC: + pinctrl_rk3368_gmac_config(priv->grf, func); + break; +#endif default: return -EINVAL; } @@ -97,6 +648,20 @@ static int rk3368_pinctrl_get_periph_id(struct udevice *dev, return PERIPH_ID_UART1; case 55: return PERIPH_ID_UART0; + case 44: + return PERIPH_ID_SPI0; + case 45: + return PERIPH_ID_SPI1; + case 41: + return PERIPH_ID_SPI2; + case 35: + return PERIPH_ID_EMMC; + case 32: + return PERIPH_ID_SDCARD; +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) + case 27: + return PERIPH_ID_GMAC; +#endif } return -ENOENT; diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 61afd7a92a..836be25507 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -10,13 +10,22 @@ config RAM config SPL_RAM bool "Enable RAM support in SPL" - depends on RAM + depends on RAM && SPL_DM help The RAM subsystem adds a small amount of overhead to the image. If this is acceptable and you have a need to use RAM drivers in SPL, enable this option. It might provide a cleaner interface to setting up RAM (e.g. SDRAM / DDR) within SPL. +config TPL_RAM + bool "Enable RAM support in SPL" + depends on RAM && TPL_DM + help + The RAM subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use RAM drivers in + TPL, enable this option. It might provide a cleaner interface to + setting up RAM (e.g. SDRAM / DDR) within TPL. + config STM32_SDRAM bool "Enable STM32 SDRAM support" depends on RAM diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile index c409c480fc..51ae6be655 100644 --- a/drivers/ram/Makefile +++ b/drivers/ram/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_RAM) += ram-uclass.o obj-$(CONFIG_SANDBOX) += sandbox_ram.o obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o + +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile new file mode 100644 index 0000000000..b09d03c2c9 --- /dev/null +++ b/drivers/ram/rockchip/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o diff --git a/drivers/ram/rockchip/dmc-rk3368.c b/drivers/ram/rockchip/dmc-rk3368.c new file mode 100644 index 0000000000..ca7b1ff0c8 --- /dev/null +++ b/drivers/ram/rockchip/dmc-rk3368.c @@ -0,0 +1,1007 @@ +/* + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-bindings/memory/rk3368-dmc.h> +#include <dt-structs.h> +#include <ram.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/cru_rk3368.h> +#include <asm/arch/grf_rk3368.h> +#include <asm/arch/ddr_rk3368.h> +#include <asm/arch/sdram.h> +#include <asm/arch/sdram_common.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dram_info { + struct ram_info info; + struct clk ddr_clk; + struct rk3368_cru *cru; + struct rk3368_grf *grf; + struct rk3368_ddr_pctl *pctl; + struct rk3368_ddrphy *phy; + struct rk3368_pmu_grf *pmugrf; + struct rk3368_msch *msch; +}; + +struct rk3368_sdram_params { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_rockchip_rk3368_dmc of_plat; +#endif + struct rk3288_sdram_pctl_timing pctl_timing; + u32 trefi_mem_ddr3; + struct rk3288_sdram_channel chan; + struct regmap *map; + u32 ddr_freq; + u32 memory_schedule; + u32 ddr_speed_bin; + u32 tfaw_mult; +}; + +/* PTCL bits */ +enum { + /* PCTL_DFISTCFG0 */ + DFI_INIT_START = BIT(0), + DFI_DATA_BYTE_DISABLE_EN = BIT(2), + + /* PCTL_DFISTCFG1 */ + DFI_DRAM_CLK_SR_EN = BIT(0), + DFI_DRAM_CLK_DPD_EN = BIT(1), + ODT_LEN_BL8_W_SHIFT = 16, + + /* PCTL_DFISTCFG2 */ + DFI_PARITY_INTR_EN = BIT(0), + DFI_PARITY_EN = BIT(1), + + /* PCTL_DFILPCFG0 */ + TLP_RESP_TIME_SHIFT = 16, + LP_SR_EN = BIT(8), + LP_PD_EN = BIT(0), + + /* PCTL_DFIODTCFG */ + RANK0_ODT_WRITE_SEL = BIT(3), + RANK1_ODT_WRITE_SEL = BIT(11), + + /* PCTL_SCFG */ + HW_LOW_POWER_EN = BIT(0), + + /* PCTL_MCMD */ + START_CMD = BIT(31), + MCMD_RANK0 = BIT(20), + MCMD_RANK1 = BIT(21), + DESELECT_CMD = 0, + PREA_CMD, + REF_CMD, + MRS_CMD, + ZQCS_CMD, + ZQCL_CMD, + RSTL_CMD, + MRR_CMD = 8, + DPDE_CMD, + + /* PCTL_POWCTL */ + POWER_UP_START = BIT(0), + + /* PCTL_POWSTAT */ + POWER_UP_DONE = BIT(0), + + /* PCTL_SCTL */ + INIT_STATE = 0, + CFG_STATE, + GO_STATE, + SLEEP_STATE, + WAKEUP_STATE, + + /* PCTL_STAT */ + LP_TRIG_SHIFT = 4, + LP_TRIG_MASK = 7, + PCTL_STAT_MSK = 7, + INIT_MEM = 0, + CONFIG, + CONFIG_REQ, + ACCESS, + ACCESS_REQ, + LOW_POWER, + LOW_POWER_ENTRY_REQ, + LOW_POWER_EXIT_REQ, + + /* PCTL_MCFG */ + DDR2_DDR3_BL_8 = BIT(0), + DDR3_EN = BIT(5), + TFAW_TRRD_MULT4 = (0 << 18), + TFAW_TRRD_MULT5 = (1 << 18), + TFAW_TRRD_MULT6 = (2 << 18), +}; + +#define DDR3_MR0_WR(n) \ + ((n <= 8) ? ((n - 4) << 9) : (((n >> 1) & 0x7) << 9)) +#define DDR3_MR0_CL(n) \ + ((((n - 4) & 0x7) << 4) | (((n - 4) & 0x8) >> 2)) +#define DDR3_MR0_BL8 \ + (0 << 0) +#define DDR3_MR0_DLL_RESET \ + (1 << 8) +#define DDR3_MR1_RTT120OHM \ + ((0 << 9) | (1 << 6) | (0 << 2)) +#define DDR3_MR2_TWL(n) \ + (((n - 5) & 0x7) << 3) + + +#ifdef CONFIG_TPL_BUILD + +static void ddr_set_noc_spr_err_stall(struct rk3368_grf *grf, bool enable) +{ + if (enable) + rk_setreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); + else + rk_clrreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); +} + +static void ddr_set_ddr3_mode(struct rk3368_grf *grf, bool ddr3_mode) +{ + if (ddr3_mode) + rk_setreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); + else + rk_clrreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); +} + +static void ddrphy_config(struct rk3368_ddrphy *phy, + u32 tcl, u32 tal, u32 tcwl) +{ + int i; + + /* Set to DDR3 mode */ + clrsetbits_le32(&phy->reg[1], 0x3, 0x0); + + /* DDRPHY_REGB: CL, AL */ + clrsetbits_le32(&phy->reg[0xb], 0xff, tcl << 4 | tal); + /* DDRPHY_REGC: CWL */ + clrsetbits_le32(&phy->reg[0xc], 0x0f, tcwl); + + /* Update drive-strength */ + writel(0xcc, &phy->reg[0x11]); + writel(0xaa, &phy->reg[0x16]); + /* + * Update NRCOMP/PRCOMP for all 4 channels (for details of all + * affected registers refer to the documentation of DDRPHY_REG20 + * and DDRPHY_REG21 in the RK3368 TRM. + */ + for (i = 0; i < 4; ++i) { + writel(0xcc, &phy->reg[0x20 + i * 0x10]); + writel(0x44, &phy->reg[0x21 + i * 0x10]); + } + + /* Enable write-leveling calibration bypass */ + setbits_le32(&phy->reg[2], BIT(3)); +} + +static void copy_to_reg(u32 *dest, const u32 *src, u32 n) +{ + int i; + + for (i = 0; i < n / sizeof(u32); i++) + writel(*src++, dest++); +} + +static void send_command(struct rk3368_ddr_pctl *pctl, u32 rank, u32 cmd) +{ + u32 mcmd = START_CMD | cmd | rank; + + debug("%s: writing %x to MCMD\n", __func__, mcmd); + writel(mcmd, &pctl->mcmd); + while (readl(&pctl->mcmd) & START_CMD) + /* spin */; +} + +static void send_mrs(struct rk3368_ddr_pctl *pctl, + u32 rank, u32 mr_num, u32 mr_data) +{ + u32 mcmd = START_CMD | MRS_CMD | rank | (mr_num << 17) | (mr_data << 4); + + debug("%s: writing %x to MCMD\n", __func__, mcmd); + writel(mcmd, &pctl->mcmd); + while (readl(&pctl->mcmd) & START_CMD) + /* spin */; +} + +static int memory_init(struct rk3368_ddr_pctl *pctl, + struct rk3368_sdram_params *params) +{ + u32 mr[4]; + const ulong timeout_ms = 500; + ulong tmp; + + /* + * Power up DRAM by DDR_PCTL_POWCTL[0] register of PCTL and + * wait power up DRAM finish with DDR_PCTL_POWSTAT[0] register + * of PCTL. + */ + writel(POWER_UP_START, &pctl->powctl); + + tmp = get_timer(0); + do { + if (get_timer(tmp) > timeout_ms) { + error("%s: POWER_UP_START did not complete in %ld ms\n", + __func__, timeout_ms); + return -ETIME; + } + } while (!(readl(&pctl->powstat) & POWER_UP_DONE)); + + /* Configure MR0 through MR3 */ + mr[0] = DDR3_MR0_WR(params->pctl_timing.twr) | + DDR3_MR0_CL(params->pctl_timing.tcl) | + DDR3_MR0_DLL_RESET; + mr[1] = DDR3_MR1_RTT120OHM; + mr[2] = DDR3_MR2_TWL(params->pctl_timing.tcwl); + mr[3] = 0; + + /* + * Also see RK3368 Technical Reference Manual: + * "16.6.2 Initialization (DDR3 Initialization Sequence)" + */ + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, DESELECT_CMD); + udelay(1); + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 2, mr[2]); + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 3, mr[3]); + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 1, mr[1]); + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 0, mr[0]); + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, ZQCL_CMD); + + return 0; +} + +static void move_to_config_state(struct rk3368_ddr_pctl *pctl) +{ + /* + * Also see RK3368 Technical Reference Manual: + * "16.6.1 State transition of PCTL (Moving to Config State)" + */ + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; + + switch (state) { + case LOW_POWER: + writel(WAKEUP_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) + /* spin */; + + /* fall-through */ + case ACCESS: + case INIT_MEM: + writel(CFG_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) + /* spin */; + break; + + case CONFIG: + return; + + default: + break; + } +} + +static void move_to_access_state(struct rk3368_ddr_pctl *pctl) +{ + /* + * Also see RK3368 Technical Reference Manual: + * "16.6.1 State transition of PCTL (Moving to Access State)" + */ + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; + + switch (state) { + case LOW_POWER: + if (((readl(&pctl->stat) >> LP_TRIG_SHIFT) & + LP_TRIG_MASK) == 1) + return; + + writel(WAKEUP_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) + /* spin */; + + /* fall-through */ + case INIT_MEM: + writel(CFG_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) + /* spin */; + + /* fall-through */ + case CONFIG: + writel(GO_STATE, &pctl->sctl); + while ((readl(&pctl->stat) & PCTL_STAT_MSK) == CONFIG) + /* spin */; + break; + + case ACCESS: + return; + + default: + break; + } +} + +static void ddrctl_reset(struct rk3368_cru *cru) +{ + const u32 ctl_reset = BIT(3) | BIT(2); + const u32 phy_reset = BIT(1) | BIT(0); + + /* + * The PHY reset should be released before the PCTL reset. + * + * Note that the following sequence (including the number of + * us to delay between releasing the PHY and PCTL reset) has + * been adapted per feedback received from Rockchips, so do + * not try to optimise. + */ + rk_setreg(&cru->softrst_con[10], ctl_reset | phy_reset); + udelay(1); + rk_clrreg(&cru->softrst_con[10], phy_reset); + udelay(5); + rk_clrreg(&cru->softrst_con[10], ctl_reset); +} + +static void ddrphy_reset(struct rk3368_ddrphy *ddrphy) +{ + /* + * The analog part of the PHY should be release at least 1000 + * DRAM cycles before the digital part of the PHY (waiting for + * 5us will ensure this for a DRAM clock as low as 200MHz). + */ + clrbits_le32(&ddrphy->reg[0], BIT(3) | BIT(2)); + udelay(1); + setbits_le32(&ddrphy->reg[0], BIT(2)); + udelay(5); + setbits_le32(&ddrphy->reg[0], BIT(3)); +} + +static void ddrphy_config_delays(struct rk3368_ddrphy *ddrphy, u32 freq) +{ + u32 dqs_dll_delay; + + setbits_le32(&ddrphy->reg[0x13], BIT(4)); + clrbits_le32(&ddrphy->reg[0x14], BIT(3)); + + setbits_le32(&ddrphy->reg[0x26], BIT(4)); + clrbits_le32(&ddrphy->reg[0x27], BIT(3)); + + setbits_le32(&ddrphy->reg[0x36], BIT(4)); + clrbits_le32(&ddrphy->reg[0x37], BIT(3)); + + setbits_le32(&ddrphy->reg[0x46], BIT(4)); + clrbits_le32(&ddrphy->reg[0x47], BIT(3)); + + setbits_le32(&ddrphy->reg[0x56], BIT(4)); + clrbits_le32(&ddrphy->reg[0x57], BIT(3)); + + if (freq <= 400000000) + setbits_le32(&ddrphy->reg[0xa4], 0x1f); + else + clrbits_le32(&ddrphy->reg[0xa4], 0x1f); + + if (freq < 681000000) + dqs_dll_delay = 3; /* 67.5 degree delay */ + else + dqs_dll_delay = 2; /* 45 degree delay */ + + writel(dqs_dll_delay, &ddrphy->reg[0x28]); + writel(dqs_dll_delay, &ddrphy->reg[0x38]); + writel(dqs_dll_delay, &ddrphy->reg[0x48]); + writel(dqs_dll_delay, &ddrphy->reg[0x58]); +} + +static int dfi_cfg(struct rk3368_ddr_pctl *pctl) +{ + const ulong timeout_ms = 200; + ulong tmp; + + writel(DFI_DATA_BYTE_DISABLE_EN, &pctl->dfistcfg0); + + writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, + &pctl->dfistcfg1); + writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2); + writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN, + &pctl->dfilpcfg0); + + writel(1, &pctl->dfitphyupdtype0); + + writel(0x1f, &pctl->dfitphyrdlat); + writel(0, &pctl->dfitphywrdata); + writel(0, &pctl->dfiupdcfg); /* phyupd and ctrlupd disabled */ + + setbits_le32(&pctl->dfistcfg0, DFI_INIT_START); + + tmp = get_timer(0); + do { + if (get_timer(tmp) > timeout_ms) { + error("%s: DFI init did not complete within %ld ms\n", + __func__, timeout_ms); + return -ETIME; + } + } while ((readl(&pctl->dfiststat0) & 1) == 0); + + return 0; +} + +static inline u32 ps_to_tCK(const u32 ps, const ulong freq) +{ + const ulong MHz = 1000000; + return DIV_ROUND_UP(ps * freq, 1000000 * MHz); +} + +static inline u32 ns_to_tCK(const u32 ns, const ulong freq) +{ + return ps_to_tCK(ns * 1000, freq); +} + +static inline u32 tCK_to_ps(const ulong tCK, const ulong freq) +{ + const ulong MHz = 1000000; + return DIV_ROUND_UP(tCK * 1000000 * MHz, freq); +} + +static int pctl_calc_timings(struct rk3368_sdram_params *params, + ulong freq) +{ + struct rk3288_sdram_pctl_timing *pctl_timing = ¶ms->pctl_timing; + const ulong MHz = 1000000; + u32 tccd; + u32 tfaw_as_ps; + + if (params->ddr_speed_bin != DDR3_1600K) { + error("%s: unimplemented DDR3 speed bin %d\n", + __func__, params->ddr_speed_bin); + return -1; + } + + /* PCTL is clocked at 1/2 the DRAM clock; err on the side of caution */ + pctl_timing->togcnt1u = DIV_ROUND_UP(freq, 2 * MHz); + pctl_timing->togcnt100n = DIV_ROUND_UP(freq / 10, 2 * MHz); + + pctl_timing->tinit = 200; /* 200 usec */ + pctl_timing->trsth = 500; /* 500 usec */ + pctl_timing->trefi = 78; /* 7.8usec = 78 * 100ns */ + params->trefi_mem_ddr3 = ns_to_tCK(pctl_timing->trefi * 100, freq); + + if (freq <= (400 * MHz)) { + pctl_timing->tcl = 6; + pctl_timing->tcwl = 10; + } else if (freq <= (533 * MHz)) { + pctl_timing->tcl = 8; + pctl_timing->tcwl = 6; + } else if (freq <= (666 * MHz)) { + pctl_timing->tcl = 10; + pctl_timing->tcwl = 7; + } else { + pctl_timing->tcl = 11; + pctl_timing->tcwl = 8; + } + + pctl_timing->tmrd = 4; /* 4 tCK (all speed bins) */ + pctl_timing->trfc = ns_to_tCK(350, freq); /* tRFC: 350 (max) @ 8GBit */ + pctl_timing->trp = max(4u, ps_to_tCK(13750, freq)); + /* + * JESD-79: + * READ to WRITE Command Delay = RL + tCCD / 2 + 2tCK - WL + */ + tccd = 4; + pctl_timing->trtw = pctl_timing->tcl + tccd/2 + 2 - pctl_timing->tcwl; + pctl_timing->tal = 0; + pctl_timing->tras = ps_to_tCK(35000, freq); + pctl_timing->trc = ps_to_tCK(48750, freq); + pctl_timing->trcd = ps_to_tCK(13750, freq); + pctl_timing->trrd = max(4u, ps_to_tCK(7500, freq)); + pctl_timing->trtp = max(4u, ps_to_tCK(7500, freq)); + pctl_timing->twr = ps_to_tCK(15000, freq); + /* The DDR3 mode-register does only support even values for tWR > 8. */ + if (pctl_timing->twr > 8) + pctl_timing->twr = (pctl_timing->twr + 1) & ~1; + pctl_timing->twtr = max(4u, ps_to_tCK(7500, freq)); + pctl_timing->texsr = 512; /* tEXSR(max) is tDLLLK */ + pctl_timing->txp = max(3u, ps_to_tCK(6000, freq)); + pctl_timing->txpdll = max(10u, ps_to_tCK(24000, freq)); + pctl_timing->tzqcs = max(64u, ps_to_tCK(80000, freq)); + pctl_timing->tzqcsi = 10000; /* as used by Rockchip */ + pctl_timing->tdqs = 1; /* fixed for DDR3 */ + pctl_timing->tcksre = max(5u, ps_to_tCK(10000, freq)); + pctl_timing->tcksrx = max(5u, ps_to_tCK(10000, freq)); + pctl_timing->tcke = max(3u, ps_to_tCK(5000, freq)); + pctl_timing->tmod = max(12u, ps_to_tCK(15000, freq)); + pctl_timing->trstl = ns_to_tCK(100, freq); + pctl_timing->tzqcl = max(256u, ps_to_tCK(320000, freq)); /* tZQoper */ + pctl_timing->tmrr = 0; + pctl_timing->tckesr = pctl_timing->tcke + 1; /* JESD-79: tCKE + 1tCK */ + pctl_timing->tdpd = 0; /* RK3368 TRM: "allowed values for DDR3: 0" */ + + + /* + * The controller can represent tFAW as 4x, 5x or 6x tRRD only. + * We want to use the smallest multiplier that satisfies the tFAW + * requirements of the given speed-bin. If necessary, we stretch out + * tRRD to allow us to operate on a 6x multiplier for tFAW. + */ + tfaw_as_ps = 40000; /* 40ns: tFAW for DDR3-1600K, 2KB page-size */ + if (tCK_to_ps(pctl_timing->trrd * 6, freq) < tfaw_as_ps) { + /* If tFAW is > 6 x tRRD, we need to stretch tRRD */ + pctl_timing->trrd = ps_to_tCK(DIV_ROUND_UP(40000, 6), freq); + params->tfaw_mult = TFAW_TRRD_MULT6; + } else if (tCK_to_ps(pctl_timing->trrd * 5, freq) < tfaw_as_ps) { + params->tfaw_mult = TFAW_TRRD_MULT6; + } else if (tCK_to_ps(pctl_timing->trrd * 4, freq) < tfaw_as_ps) { + params->tfaw_mult = TFAW_TRRD_MULT5; + } else { + params->tfaw_mult = TFAW_TRRD_MULT4; + } + + return 0; +} + +static void pctl_cfg(struct rk3368_ddr_pctl *pctl, + struct rk3368_sdram_params *params, + struct rk3368_grf *grf) +{ + /* Configure PCTL timing registers */ + params->pctl_timing.trefi |= BIT(31); /* see PCTL_TREFI */ + copy_to_reg(&pctl->togcnt1u, ¶ms->pctl_timing.togcnt1u, + sizeof(params->pctl_timing)); + writel(params->trefi_mem_ddr3, &pctl->trefi_mem_ddr3); + + /* Set up ODT write selector and ODT write length */ + writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL), &pctl->dfiodtcfg); + writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1); + + /* Set up the CL/CWL-dependent timings of DFI */ + writel((params->pctl_timing.tcl - 1) / 2 - 1, &pctl->dfitrddataen); + writel((params->pctl_timing.tcwl - 1) / 2 - 1, &pctl->dfitphywrlat); + + /* DDR3 */ + writel(params->tfaw_mult | DDR3_EN | DDR2_DDR3_BL_8, &pctl->mcfg); + writel(0x001c0004, &grf->ddrc0_con0); + + setbits_le32(&pctl->scfg, HW_LOW_POWER_EN); +} + +static int ddrphy_data_training(struct rk3368_ddr_pctl *pctl, + struct rk3368_ddrphy *ddrphy) +{ + const u32 trefi = readl(&pctl->trefi); + const ulong timeout_ms = 500; + ulong tmp; + + /* disable auto-refresh */ + writel(0 | BIT(31), &pctl->trefi); + + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x21); + + tmp = get_timer(0); + do { + if (get_timer(tmp) > timeout_ms) { + error("%s: did not complete within %ld ms\n", + __func__, timeout_ms); + return -ETIME; + } + } while ((readl(&ddrphy->reg[0xff]) & 0xf) != 0xf); + + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); + /* resume auto-refresh */ + writel(trefi | BIT(31), &pctl->trefi); + + return 0; +} + +static int sdram_col_row_detect(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + struct rk3368_sdram_params *params = dev_get_platdata(dev); + struct rk3368_ddr_pctl *pctl = priv->pctl; + struct rk3368_msch *msch = priv->msch; + const u32 test_pattern = 0x5aa5f00f; + int row, col; + uintptr_t addr; + + move_to_config_state(pctl); + writel(6, &msch->ddrconf); + move_to_access_state(pctl); + + /* Detect col */ + for (col = 11; col >= 9; col--) { + writel(0, CONFIG_SYS_SDRAM_BASE); + addr = CONFIG_SYS_SDRAM_BASE + + (1 << (col + params->chan.bw - 1)); + writel(test_pattern, addr); + if ((readl(addr) == test_pattern) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + break; + } + + if (col == 8) { + error("%s: col detect error\n", __func__); + return -EINVAL; + } + + move_to_config_state(pctl); + writel(15, &msch->ddrconf); + move_to_access_state(pctl); + + /* Detect row*/ + for (row = 16; row >= 12; row--) { + writel(0, CONFIG_SYS_SDRAM_BASE); + addr = CONFIG_SYS_SDRAM_BASE + (1 << (row + 15 - 1)); + writel(test_pattern, addr); + if ((readl(addr) == test_pattern) && + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) + break; + } + + if (row == 11) { + error("%s: row detect error\n", __func__); + return -EINVAL; + } + + /* Record results */ + debug("%s: col %d, row %d\n", __func__, col, row); + params->chan.col = col; + params->chan.cs0_row = row; + params->chan.cs1_row = row; + params->chan.row_3_4 = 0; + + return 0; +} + +static int msch_niu_config(struct rk3368_msch *msch, + struct rk3368_sdram_params *params) +{ + int i; + const u8 cols = params->chan.col - ((params->chan.bw == 2) ? 0 : 1); + const u8 rows = params->chan.cs0_row; + + /* + * The DDR address-translation table always assumes a 32bit + * bus and the comparison below takes care of adjusting for + * a 16bit bus (i.e. one column-address is consumed). + */ + const struct { + u8 rows; + u8 columns; + u8 type; + } ddrconf_table[] = { + /* + * C-B-R-D patterns are first. For these we require an + * exact match for the columns and rows (as there's + * one entry per possible configuration). + */ + [0] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CBRD }, + [1] = { .rows = 14, .columns = 10, .type = DMC_MSCH_CBRD }, + [2] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CBRD }, + [3] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBRD }, + [4] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CBRD }, + [5] = { .rows = 15, .columns = 11, .type = DMC_MSCH_CBRD }, + [6] = { .rows = 16, .columns = 11, .type = DMC_MSCH_CBRD }, + [7] = { .rows = 13, .columns = 9, .type = DMC_MSCH_CBRD }, + [8] = { .rows = 14, .columns = 9, .type = DMC_MSCH_CBRD }, + [9] = { .rows = 15, .columns = 9, .type = DMC_MSCH_CBRD }, + [10] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBRD }, + /* + * 11 through 13 are C-R-B-D patterns. These are + * matched for an exact number of columns and to + * ensure that the hardware uses at least as many rows + * as the pattern requires (i.e. we make sure that + * there's no gaps up until we hit the device/chip-select; + * however, these patterns can accept up to 16 rows, + * as the row-address continues right after the CS + * switching) + */ + [11] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CRBD }, + [12] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CRBD }, + [13] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CRBD }, + /* + * 14 and 15 are catch-all variants using a C-B-D-R + * scheme (i.e. alternating the chip-select every time + * C-B overflows) and stuffing the remaining C-bits + * into the top. Matching needs to make sure that the + * number of columns is either an exact match (i.e. we + * can use less the the maximum number of rows) -or- + * that the columns exceed what is given in this table + * and the rows are an exact match (in which case the + * remaining C-bits will be stuffed onto the top after + * the device/chip-select switches). + */ + [14] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBDR }, + [15] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBDR }, + }; + + /* + * For C-B-R-D, we need an exact match (i.e. both for the number of + * columns and rows), while for C-B-D-R, only the the number of + * columns needs to match. + */ + for (i = 0; i < ARRAY_SIZE(ddrconf_table); i++) { + bool match = false; + + /* If this entry if for a different matcher, then skip it */ + if (ddrconf_table[i].type != params->memory_schedule) + continue; + + /* + * Match according to the rules (exact/inexact/at-least) + * documented in the ddrconf_table above. + */ + switch (params->memory_schedule) { + case DMC_MSCH_CBRD: + match = (ddrconf_table[i].columns == cols) && + (ddrconf_table[i].rows == rows); + break; + + case DMC_MSCH_CRBD: + match = (ddrconf_table[i].columns == cols) && + (ddrconf_table[i].rows <= rows); + break; + + case DMC_MSCH_CBDR: + match = (ddrconf_table[i].columns == cols) || + ((ddrconf_table[i].columns <= cols) && + (ddrconf_table[i].rows == rows)); + break; + + default: + break; + } + + if (match) { + debug("%s: setting ddrconf 0x%x\n", __func__, i); + writel(i, &msch->ddrconf); + return 0; + } + } + + error("%s: ddrconf (NIU config) not found\n", __func__); + return -EINVAL; +} + +static void dram_all_config(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + struct rk3368_pmu_grf *pmugrf = priv->pmugrf; + struct rk3368_sdram_params *params = dev_get_platdata(dev); + const struct rk3288_sdram_channel *info = ¶ms->chan; + u32 sys_reg = 0; + const int chan = 0; + + sys_reg |= DDR3 << SYS_REG_DDRTYPE_SHIFT; + sys_reg |= 0 << SYS_REG_NUM_CH_SHIFT; + + sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan); + sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(chan); + sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(chan); + sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(chan); + sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(chan); + sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(chan); + sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(chan); + sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(chan); + sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(chan); + + writel(sys_reg, &pmugrf->os_reg[2]); +} + +static int setup_sdram(struct udevice *dev) +{ + struct dram_info *priv = dev_get_priv(dev); + struct rk3368_sdram_params *params = dev_get_platdata(dev); + + struct rk3368_ddr_pctl *pctl = priv->pctl; + struct rk3368_ddrphy *ddrphy = priv->phy; + struct rk3368_cru *cru = priv->cru; + struct rk3368_grf *grf = priv->grf; + struct rk3368_msch *msch = priv->msch; + + int ret; + + /* The input clock (i.e. DPLL) needs to be 2x the DRAM frequency */ + ret = clk_set_rate(&priv->ddr_clk, 2 * params->ddr_freq); + if (ret < 0) { + debug("%s: could not set DDR clock: %d\n", __func__, ret); + return ret; + } + + /* Update the read-latency for the RK3368 */ + writel(0x32, &msch->readlatency); + + /* Initialise the DDR PCTL and DDR PHY */ + ddrctl_reset(cru); + ddrphy_reset(ddrphy); + ddrphy_config_delays(ddrphy, params->ddr_freq); + dfi_cfg(pctl); + /* Configure relative system information of grf_ddrc0_con0 register */ + ddr_set_ddr3_mode(grf, true); + ddr_set_noc_spr_err_stall(grf, true); + /* Calculate timings */ + pctl_calc_timings(params, params->ddr_freq); + /* Initialise the device timings in protocol controller */ + pctl_cfg(pctl, params, grf); + /* Configure AL, CL ... information of PHY registers */ + ddrphy_config(ddrphy, + params->pctl_timing.tcl, + params->pctl_timing.tal, + params->pctl_timing.tcwl); + + /* Initialize DRAM and configure with mode-register values */ + ret = memory_init(pctl, params); + if (ret) + goto error; + + move_to_config_state(pctl); + /* Perform data-training */ + ddrphy_data_training(pctl, ddrphy); + move_to_access_state(pctl); + + /* TODO(prt): could detect rank in training... */ + params->chan.rank = 2; + /* TODO(prt): bus width is not auto-detected (yet)... */ + params->chan.bw = 2; /* 32bit wide bus */ + params->chan.dbw = params->chan.dbw; /* 32bit wide bus */ + + /* DDR3 is always 8 bank */ + params->chan.bk = 3; + /* Detect col and row number */ + ret = sdram_col_row_detect(dev); + if (ret) + goto error; + + /* Configure NIU DDR configuration */ + ret = msch_niu_config(msch, params); + if (ret) + goto error; + + /* set up OS_REG to communicate w/ next stage and OS */ + dram_all_config(dev); + + return 0; + +error: + printf("DRAM init failed!\n"); + hang(); +} +#endif + +static int rk3368_dmc_ofdata_to_platdata(struct udevice *dev) +{ + int ret = 0; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3368_sdram_params *plat = dev_get_platdata(dev); + + ret = regmap_init_mem(dev, &plat->map); + if (ret) + return ret; +#endif + + return ret; +} + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +static int conv_of_platdata(struct udevice *dev) +{ + struct rk3368_sdram_params *plat = dev_get_platdata(dev); + struct dtd_rockchip_rk3368_dmc *of_plat = &plat->of_plat; + int ret; + + plat->ddr_freq = of_plat->rockchip_ddr_frequency; + plat->ddr_speed_bin = of_plat->rockchip_ddr_speed_bin; + plat->memory_schedule = of_plat->rockchip_memory_schedule; + + ret = regmap_init_mem_platdata(dev, of_plat->reg, + ARRAY_SIZE(of_plat->reg) / 2, + &plat->map); + if (ret) + return ret; + + return 0; +} +#endif + +static int rk3368_dmc_probe(struct udevice *dev) +{ +#ifdef CONFIG_TPL_BUILD + struct rk3368_sdram_params *plat = dev_get_platdata(dev); + struct rk3368_ddr_pctl *pctl; + struct rk3368_ddrphy *ddrphy; + struct rk3368_cru *cru; + struct rk3368_grf *grf; + struct rk3368_msch *msch; + int ret; + struct udevice *dev_clk; +#endif + struct dram_info *priv = dev_get_priv(dev); + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + ret = conv_of_platdata(dev); + if (ret) + return ret; +#endif + + priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); + debug("%s: pmugrf=%p\n", __func__, priv->pmugrf); + +#ifdef CONFIG_TPL_BUILD + pctl = regmap_get_range(plat->map, 0); + ddrphy = regmap_get_range(plat->map, 1); + msch = syscon_get_first_range(ROCKCHIP_SYSCON_MSCH); + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + + priv->pctl = pctl; + priv->phy = ddrphy; + priv->msch = msch; + priv->grf = grf; + + ret = rockchip_get_clk(&dev_clk); + if (ret) + return ret; + priv->ddr_clk.id = CLK_DDR; + ret = clk_request(dev_clk, &priv->ddr_clk); + if (ret) + return ret; + + cru = rockchip_get_cru(); + priv->cru = cru; + if (IS_ERR(priv->cru)) + return PTR_ERR(priv->cru); + + ret = setup_sdram(dev); + if (ret) + return ret; +#endif + + priv->info.base = 0; + priv->info.size = + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]); + + /* + * we use the 0x00000000~0xfdffffff space since 0xff000000~0xffffffff + * is SoC register space (i.e. reserved), and 0xfe000000~0xfeffffff is + * inaccessible for some IP controller. + */ + priv->info.size = min(priv->info.size, (size_t)0xfe000000); + + return 0; +} + +static int rk3368_dmc_get_info(struct udevice *dev, struct ram_info *info) +{ + struct dram_info *priv = dev_get_priv(dev); + + *info = priv->info; + return 0; +} + +static struct ram_ops rk3368_dmc_ops = { + .get_info = rk3368_dmc_get_info, +}; + + +static const struct udevice_id rk3368_dmc_ids[] = { + { .compatible = "rockchip,rk3368-dmc" }, + { } +}; + +U_BOOT_DRIVER(dmc_rk3368) = { + .name = "rockchip_rk3368_dmc", + .id = UCLASS_RAM, + .of_match = rk3368_dmc_ids, + .ops = &rk3368_dmc_ops, + .probe = rk3368_dmc_probe, + .priv_auto_alloc_size = sizeof(struct dram_info), + .ofdata_to_platdata = rk3368_dmc_ofdata_to_platdata, + .probe = rk3368_dmc_probe, + .priv_auto_alloc_size = sizeof(struct dram_info), + .platdata_auto_alloc_size = sizeof(struct rk3368_sdram_params), +}; diff --git a/drivers/rtc/m41t60.c b/drivers/rtc/m41t60.c index 95083f0d72..2ddfd4ea5c 100644 --- a/drivers/rtc/m41t60.c +++ b/drivers/rtc/m41t60.c @@ -200,7 +200,7 @@ int rtc_set(struct rtc_time *tmp) void rtc_reset(void) { uchar *const data = rtc_validate(); - char const *const s = getenv("rtccal"); + char const *const s = env_get("rtccal"); if (!data) return; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index f192ca597c..1a65a3f9b9 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -682,7 +682,7 @@ int scsi_scan(bool verbose) printf("Found %d device(s).\n", scsi_max_devs); #ifndef CONFIG_SPL_BUILD - setenv_ulong("scsidevs", scsi_max_devs); + env_set_ulong("scsidevs", scsi_max_devs); #endif return 0; } diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index 29799dce93..353109c070 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -526,9 +526,9 @@ int drv_usbtty_init (void) int snlen; /* Ger seiral number */ - if (!(sn = getenv("serial#"))) { + sn = env_get("serial#"); + if (!sn) sn = "000000000000"; - } snlen = strlen(sn); if (snlen > sizeof(serial_number) - 1) { printf ("Warning: serial number %s is too long (%d > %lu)\n", @@ -540,10 +540,9 @@ int drv_usbtty_init (void) /* Decide on which type of UDC device to be. */ - - if(!(tt = getenv("usbtty"))) { + tt = env_get("usbtty"); + if (!tt) tt = "generic"; - } usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); /* prepare buffers... */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8a8e8e480f..3c5582a950 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -188,13 +188,6 @@ config ZYNQ_QSPI Zynq QSPI IP core. This IP is used to connect the flash in 4-bit qspi, 8-bit dual stacked and shared 4-bit dual parallel. -config OMAP3_SPI - bool "McSPI driver for OMAP" - help - SPI master controller for OMAP24XX and later Multichannel SPI - (McSPI). This driver be used to access SPI chips on platforms - embedding this OMAP3 McSPI IP core. - endif # if DM_SPI config SOFT_SPI @@ -229,4 +222,11 @@ config MPC8XX_SPI help Enable support for SPI on MPC8XX +config OMAP3_SPI + bool "McSPI driver for OMAP" + help + SPI master controller for OMAP24XX and later Multichannel SPI + (McSPI). This driver be used to access SPI chips on platforms + embedding this OMAP3 McSPI IP core. + endmenu # menu "SPI Support" diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c index 7921ea0d75..c70d636277 100644 --- a/drivers/spi/rk_spi.c +++ b/drivers/spi/rk_spi.c @@ -210,6 +210,14 @@ static int rockchip_spi_ofdata_to_platdata(struct udevice *bus) static int rockchip_spi_calc_modclk(ulong max_freq) { + /* + * While this is not strictly correct for the RK3368, as the + * GPLL will be 576MHz, things will still work, as the + * clk_set_rate(...) implementation in our clock-driver will + * chose the next closest rate not exceeding what we request + * based on the output of this function. + */ + unsigned div; const unsigned long gpll_hz = 594000000UL; @@ -443,6 +451,7 @@ static const struct dm_spi_ops rockchip_spi_ops = { static const struct udevice_id rockchip_spi_ids[] = { { .compatible = "rockchip,rk3288-spi" }, + { .compatible = "rockchip,rk3368-spi" }, { .compatible = "rockchip,rk3399-spi" }, { } }; diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index c666303331..13f122350b 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -9,6 +9,24 @@ config TIMER will be used. The timer is usually a 32 bits free-running up counter. There may be no real tick, and no timer interrupt. +config SPL_TIMER + bool "Enable driver model for timer drivers in SPL" + depends on TIMER && SPL + help + Enable support for timer drivers in SPL. These can be used to get + a timer value when in SPL, or perhaps for implementing a delay + function. This enables the drivers in drivers/timer as part of an + SPL build. + +config TPL_TIMER + bool "Enable driver model for timer drivers in TPL" + depends on TIMER && TPL + help + Enable support for timer drivers in TPL. These can be used to get + a timer value when in TPL, or perhaps for implementing a delay + function. This enables the drivers in drivers/timer as part of an + TPL build. + config TIMER_EARLY bool "Allow timer to be used early in U-Boot" depends on TIMER @@ -85,4 +103,11 @@ config AE3XX_TIMER help Select this to enable a timer for AE3XX devices. +config ROCKCHIP_TIMER + bool "Rockchip timer support" + depends on TIMER + help + Select this to enable support for the timer found on + Rockchip devices. + endmenu diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index ced7bd66bd..fa7ce7c835 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_TIMER) += timer-uclass.o +obj-y += timer-uclass.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o @@ -14,3 +14,4 @@ obj-$(CONFIG_STI_TIMER) += sti-timer.o obj-$(CONFIG_ARC_TIMER) += arc_timer.o obj-$(CONFIG_AG101P_TIMER) += ag101p_timer.o obj-$(CONFIG_AE3XX_TIMER) += ae3xx_timer.o +obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o diff --git a/drivers/timer/rockchip_timer.c b/drivers/timer/rockchip_timer.c new file mode 100644 index 0000000000..0848033c66 --- /dev/null +++ b/drivers/timer/rockchip_timer.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <asm/arch/timer.h> +#include <dt-structs.h> +#include <timer.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct rockchip_timer_plat { + struct dtd_rockchip_rk3368_timer dtd; +}; +#endif + +/* Driver private data. Contains timer id. Could be either 0 or 1. */ +struct rockchip_timer_priv { + struct rk_timer *timer; +}; + +static int rockchip_timer_get_count(struct udevice *dev, u64 *count) +{ + struct rockchip_timer_priv *priv = dev_get_priv(dev); + uint64_t timebase_h, timebase_l; + uint64_t cntr; + + timebase_l = readl(&priv->timer->timer_curr_value0); + timebase_h = readl(&priv->timer->timer_curr_value1); + + /* timers are down-counting */ + cntr = timebase_h << 32 | timebase_l; + *count = ~0ull - cntr; + return 0; +} + +static int rockchip_clk_ofdata_to_platdata(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct rockchip_timer_priv *priv = dev_get_priv(dev); + + priv->timer = (struct rk_timer *)devfdt_get_addr(dev); +#endif + + return 0; +} + +static int rockchip_timer_start(struct udevice *dev) +{ + struct rockchip_timer_priv *priv = dev_get_priv(dev); + const uint64_t reload_val = ~0uLL; + const uint32_t reload_val_l = reload_val & 0xffffffff; + const uint32_t reload_val_h = reload_val >> 32; + + /* disable timer and reset all control */ + writel(0, &priv->timer->timer_ctrl_reg); + /* write reload value */ + writel(reload_val_l, &priv->timer->timer_load_count0); + writel(reload_val_h, &priv->timer->timer_load_count1); + /* enable timer */ + writel(1, &priv->timer->timer_ctrl_reg); + + return 0; +} + +static int rockchip_timer_probe(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct rockchip_timer_priv *priv = dev_get_priv(dev); + struct rockchip_timer_plat *plat = dev_get_platdata(dev); + + priv->timer = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]); + uc_priv->clock_rate = plat->dtd.clock_frequency; +#endif + + return rockchip_timer_start(dev); +} + +static const struct timer_ops rockchip_timer_ops = { + .get_count = rockchip_timer_get_count, +}; + +static const struct udevice_id rockchip_timer_ids[] = { + { .compatible = "rockchip,rk3368-timer" }, + {} +}; + +U_BOOT_DRIVER(arc_timer) = { + .name = "rockchip_rk3368_timer", + .id = UCLASS_TIMER, + .of_match = rockchip_timer_ids, + .probe = rockchip_timer_probe, + .ops = &rockchip_timer_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct rockchip_timer_priv), +#if CONFIG_IS_ENABLED(OF_PLATDATA) + .platdata_auto_alloc_size = sizeof(struct rockchip_timer_plat), +#endif + .ofdata_to_platdata = rockchip_clk_ofdata_to_platdata, +}; diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index ec10b28288..a84755f4c5 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -42,6 +42,7 @@ unsigned long notrace timer_get_rate(struct udevice *dev) static int timer_pre_probe(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct clk timer_clk; int err; @@ -56,6 +57,7 @@ static int timer_pre_probe(struct udevice *dev) } else uc_priv->clock_rate = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "clock-frequency", 0); +#endif return 0; } @@ -81,16 +83,18 @@ u64 timer_conv_64(u32 count) int notrace dm_timer_init(void) { - const void *blob = gd->fdt_blob; + __maybe_unused const void *blob = gd->fdt_blob; struct udevice *dev = NULL; - int node; + int node = -ENOENT; int ret; if (gd->timer) return 0; +#if !CONFIG_IS_ENABLED(OF_PLATDATA) /* Check for a chosen timer to be used for tick */ node = fdtdec_get_chosen_node(blob, "tick-timer"); +#endif if (node < 0) { /* No chosen timer, trying first available timer */ ret = uclass_first_device_err(UCLASS_TIMER, &dev); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index da3ec2fa75..62126aad2f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -94,4 +94,6 @@ endif source "drivers/usb/gadget/Kconfig" +source "drivers/usb/eth/Kconfig" + endif diff --git a/drivers/usb/eth/Kconfig b/drivers/usb/eth/Kconfig new file mode 100644 index 0000000000..14cfa26cdf --- /dev/null +++ b/drivers/usb/eth/Kconfig @@ -0,0 +1,17 @@ +comment "USB to Ethernet Controller Drivers" + +config USB_ETHER_LAN75XX + bool "Microchip LAN75XX support" + ---help--- + Say Y here if you would like to support Microchip LAN75XX Hi-Speed + USB 2.0 to 10/100/1000 Gigabit Ethernet controller. + Supports 10Base-T/ 100Base-TX/1000Base-T. + This driver supports the internal PHY. + +config USB_ETHER_LAN78XX + bool "Microchip LAN78XX support" + ---help--- + Say Y here if you would like to support Microchip LAN78XX USB 3.1 + Gen 1 to 10/100/1000 Gigabit Ethernet controller. + Supports 10Base-T/ 100Base-TX/1000Base-T. + This driver supports the internal PHY. diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile index 4c44efc7a1..4b935a3f04 100644 --- a/drivers/usb/eth/Makefile +++ b/drivers/usb/eth/Makefile @@ -9,4 +9,6 @@ obj-$(CONFIG_USB_ETHER_ASIX) += asix.o obj-$(CONFIG_USB_ETHER_ASIX88179) += asix88179.o obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o +obj-$(CONFIG_USB_ETHER_LAN75XX) += lan7x.o lan75xx.o +obj-$(CONFIG_USB_ETHER_LAN78XX) += lan7x.o lan78xx.o obj-$(CONFIG_USB_ETHER_RTL8152) += r8152.o r8152_fw.o diff --git a/drivers/usb/eth/lan75xx.c b/drivers/usb/eth/lan75xx.c new file mode 100644 index 0000000000..1c8015805a --- /dev/null +++ b/drivers/usb/eth/lan75xx.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm.h> +#include <usb.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* LAN75xx specific register/bit defines */ +#define LAN75XX_HW_CFG_BIR BIT(7) + +#define LAN75XX_BURST_CAP 0x034 + +#define LAN75XX_BULK_IN_DLY 0x03C + +#define LAN75XX_RFE_CTL 0x060 + +#define LAN75XX_FCT_RX_CTL 0x090 + +#define LAN75XX_FCT_TX_CTL 0x094 + +#define LAN75XX_FCT_RX_FIFO_END 0x098 + +#define LAN75XX_FCT_TX_FIFO_END 0x09C + +#define LAN75XX_FCT_FLOW 0x0A0 + +/* MAC ADDRESS PERFECT FILTER For LAN75xx */ +#define LAN75XX_ADDR_FILTX 0x300 +#define LAN75XX_ADDR_FILTX_FB_VALID BIT(31) + +/* + * Lan75xx infrastructure commands + */ +static int lan75xx_phy_gig_workaround(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret = 0; + + /* Only internal phy */ + /* Set the phy in Gig loopback */ + lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, + (BMCR_LOOPBACK | BMCR_SPEED1000)); + + /* Wait for the link up */ + ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS", + dev->phy_id, MII_BMSR, BMSR_LSTATUS, + true, PHY_CONNECT_TIMEOUT_MS, 1); + if (ret) + return ret; + + /* phy reset */ + return lan7x_pmt_phy_reset(udev, dev); +} + +static int lan75xx_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev) +{ + uint32_t flow = 0, fct_flow = 0; + int ret; + + ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow); + if (ret) + return ret; + return lan7x_write_reg(udev, FLOW, flow); +} + +static int lan75xx_set_receive_filter(struct usb_device *udev) +{ + /* No multicast in u-boot */ + return lan7x_write_reg(udev, LAN75XX_RFE_CTL, + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); +} + +/* starts the TX path */ +static void lan75xx_start_tx_path(struct usb_device *udev) +{ + /* Enable Tx at MAC */ + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); + + /* Enable Tx at SCSRs */ + lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, FCT_TX_CTL_EN); +} + +/* Starts the Receive path */ +static void lan75xx_start_rx_path(struct usb_device *udev) +{ + /* Enable Rx at MAC */ + lan7x_write_reg(udev, MAC_RX, + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | + MAC_RX_FCS_STRIP | MAC_RX_RXEN); + + /* Enable Rx at SCSRs */ + lan7x_write_reg(udev, LAN75XX_FCT_RX_CTL, FCT_RX_CTL_EN); +} + +static int lan75xx_basic_reset(struct usb_device *udev, + struct ueth_data *dev, + struct lan7x_private *priv) +{ + int ret; + u32 val; + + ret = lan7x_basic_reset(udev, dev); + if (ret) + return ret; + + /* Keep the chip ID */ + ret = lan7x_read_reg(udev, ID_REV, &val); + if (ret) + return ret; + debug("LAN75xx ID_REV = 0x%08x\n", val); + + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; + + /* Respond to the IN token with a NAK */ + ret = lan7x_read_reg(udev, HW_CFG, &val); + if (ret) + return ret; + val |= LAN75XX_HW_CFG_BIR; + return lan7x_write_reg(udev, HW_CFG, val); +} + +int lan75xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + unsigned char *enetaddr = pdata->enetaddr; + u32 addr_lo = get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]); + int ret; + + /* set hardware address */ + ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX + 4, addr_lo); + if (ret) + return ret; + + addr_hi |= LAN75XX_ADDR_FILTX_FB_VALID; + ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX, addr_hi); + if (ret) + return ret; + + debug("MAC addr %pM written\n", enetaddr); + + return 0; +} + +static int lan75xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int ret; + u32 write_buf; + + /* Reset and read Mac addr were done in probe() */ + ret = lan75xx_write_hwaddr(dev); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_BURST_CAP, 0); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + if (ret) + return ret; + + /* set FIFO sizes */ + write_buf = (MAX_RX_FIFO_SIZE - 512) / 512; + ret = lan7x_write_reg(udev, LAN75XX_FCT_RX_FIFO_END, write_buf); + if (ret) + return ret; + + write_buf = (MAX_TX_FIFO_SIZE - 512) / 512; + ret = lan7x_write_reg(udev, LAN75XX_FCT_TX_FIFO_END, write_buf); + if (ret) + return ret; + + /* Init Tx */ + ret = lan7x_write_reg(udev, FLOW, 0); + if (ret) + return ret; + + /* Init Rx. Set Vlan, keep default for VLAN on 75xx */ + ret = lan75xx_set_receive_filter(udev); + if (ret) + return ret; + + /* phy workaround for gig link */ + ret = lan75xx_phy_gig_workaround(udev, ueth); + if (ret) + return ret; + + /* Init PHY, autonego, and link */ + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); + if (ret) + return ret; + ret = lan7x_eth_phylib_config_start(dev); + if (ret) + return ret; + + /* + * MAC_CR has to be set after PHY init. + * MAC will auto detect the PHY speed. + */ + ret = lan7x_read_reg(udev, MAC_CR, &write_buf); + if (ret) + return ret; + write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP; + ret = lan7x_write_reg(udev, MAC_CR, write_buf); + if (ret) + return ret; + + lan75xx_start_tx_path(udev); + lan75xx_start_rx_path(udev); + + return lan75xx_update_flowcontrol(udev, ueth); +} + +int lan75xx_read_rom_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + int ret; + + /* + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + ret = lan7x_read_eeprom_mac(pdata->enetaddr, udev); + if (ret) + memset(pdata->enetaddr, 0, 6); + + return 0; +} + +static int lan75xx_eth_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + struct eth_pdata *pdata = dev_get_platdata(dev); + int ret; + + /* Do a reset in order to get the MAC address from HW */ + if (lan75xx_basic_reset(udev, ueth, priv)) + return 0; + + /* Get the MAC address */ + /* + * We must set the eth->enetaddr from HW because the upper layer + * will force to use the environmental var (usbethaddr) or random if + * there is no valid MAC address in eth->enetaddr. + * + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + lan7x_read_eeprom_mac(pdata->enetaddr, udev); + /* Do not return 0 for not finding MAC addr in HW */ + + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); + if (ret) + return ret; + + /* Register phylib */ + return lan7x_phylib_register(dev); +} + +static const struct eth_ops lan75xx_eth_ops = { + .start = lan75xx_eth_start, + .send = lan7x_eth_send, + .recv = lan7x_eth_recv, + .free_pkt = lan7x_free_pkt, + .stop = lan7x_eth_stop, + .write_hwaddr = lan75xx_write_hwaddr, + .read_rom_hwaddr = lan75xx_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(lan75xx_eth) = { + .name = "lan75xx_eth", + .id = UCLASS_ETH, + .probe = lan75xx_eth_probe, + .remove = lan7x_eth_remove, + .ops = &lan75xx_eth_ops, + .priv_auto_alloc_size = sizeof(struct lan7x_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id lan75xx_eth_id_table[] = { + { USB_DEVICE(0x0424, 0x7500) }, /* LAN7500 USB Ethernet */ + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(lan75xx_eth, lan75xx_eth_id_table); diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c new file mode 100644 index 0000000000..d1e61c32da --- /dev/null +++ b/drivers/usb/eth/lan78xx.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm.h> +#include <usb.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* LAN78xx specific register/bit defines */ +#define LAN78XX_HW_CFG_LED1_EN BIT(21) /* Muxed with EEDO */ +#define LAN78XX_HW_CFG_LED0_EN BIT(20) /* Muxed with EECLK */ + +#define LAN78XX_USB_CFG0 0x080 +#define LAN78XX_USB_CFG0_BIR BIT(6) + +#define LAN78XX_BURST_CAP 0x090 + +#define LAN78XX_BULK_IN_DLY 0x094 + +#define LAN78XX_RFE_CTL 0x0B0 + +#define LAN78XX_FCT_RX_CTL 0x0C0 + +#define LAN78XX_FCT_TX_CTL 0x0C4 + +#define LAN78XX_FCT_RX_FIFO_END 0x0C8 + +#define LAN78XX_FCT_TX_FIFO_END 0x0CC + +#define LAN78XX_FCT_FLOW 0x0D0 + +#define LAN78XX_MAF_BASE 0x400 +#define LAN78XX_MAF_HIX 0x00 +#define LAN78XX_MAF_LOX 0x04 +#define LAN78XX_MAF_HI_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_HIX) +#define LAN78XX_MAF_LO_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_LOX) +#define LAN78XX_MAF_HI(index) (LAN78XX_MAF_BASE + (8 * (index)) + \ + LAN78XX_MAF_HIX) +#define LAN78XX_MAF_LO(index) (LAN78XX_MAF_BASE + (8 * (index)) + \ + LAN78XX_MAF_LOX) +#define LAN78XX_MAF_HI_VALID BIT(31) + +/* OTP registers */ +#define LAN78XX_OTP_BASE_ADDR 0x00001000 + +#define LAN78XX_OTP_PWR_DN (LAN78XX_OTP_BASE_ADDR + 4 * 0x00) +#define LAN78XX_OTP_PWR_DN_PWRDN_N BIT(0) + +#define LAN78XX_OTP_ADDR1 (LAN78XX_OTP_BASE_ADDR + 4 * 0x01) +#define LAN78XX_OTP_ADDR1_15_11 0x1F + +#define LAN78XX_OTP_ADDR2 (LAN78XX_OTP_BASE_ADDR + 4 * 0x02) +#define LAN78XX_OTP_ADDR2_10_3 0xFF + +#define LAN78XX_OTP_RD_DATA (LAN78XX_OTP_BASE_ADDR + 4 * 0x06) + +#define LAN78XX_OTP_FUNC_CMD (LAN78XX_OTP_BASE_ADDR + 4 * 0x08) +#define LAN78XX_OTP_FUNC_CMD_READ BIT(0) + +#define LAN78XX_OTP_CMD_GO (LAN78XX_OTP_BASE_ADDR + 4 * 0x0A) +#define LAN78XX_OTP_CMD_GO_GO BIT(0) + +#define LAN78XX_OTP_STATUS (LAN78XX_OTP_BASE_ADDR + 4 * 0x0C) +#define LAN78XX_OTP_STATUS_BUSY BIT(0) + +#define LAN78XX_OTP_INDICATOR_1 0xF3 +#define LAN78XX_OTP_INDICATOR_2 0xF7 + +/* + * Lan78xx infrastructure commands + */ +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset, + u32 length, u8 *data) +{ + int i; + int ret; + u32 buf; + + ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf); + if (ret) + return ret; + + if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) { + /* clear it and wait to be cleared */ + ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0); + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_PWR_DN_PWRDN_N", + LAN78XX_OTP_PWR_DN, + LAN78XX_OTP_PWR_DN_PWRDN_N, + false, 1000, 0); + if (ret) + return ret; + } + + for (i = 0; i < length; i++) { + ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1, + ((offset + i) >> 8) & + LAN78XX_OTP_ADDR1_15_11); + if (ret) + return ret; + ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2, + ((offset + i) & LAN78XX_OTP_ADDR2_10_3)); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD, + LAN78XX_OTP_FUNC_CMD_READ); + if (ret) + return ret; + ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO, + LAN78XX_OTP_CMD_GO_GO); + + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY", + LAN78XX_OTP_STATUS, + LAN78XX_OTP_STATUS_BUSY, + false, 1000, 0); + if (ret) + return ret; + + ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf); + if (ret) + return ret; + + data[i] = (u8)(buf & 0xFF); + } + + return 0; +} + +static int lan78xx_read_otp(struct usb_device *udev, u32 offset, + u32 length, u8 *data) +{ + u8 sig; + int ret; + + ret = lan78xx_read_raw_otp(udev, 0, 1, &sig); + + if (!ret) { + if (sig == LAN78XX_OTP_INDICATOR_1) + offset = offset; + else if (sig == LAN78XX_OTP_INDICATOR_2) + offset += 0x100; + else + return -EINVAL; + ret = lan78xx_read_raw_otp(udev, offset, length, data); + if (ret) + return ret; + } + debug("LAN78x: MAC address from OTP = %pM\n", data); + + return ret; +} + +static int lan78xx_read_otp_mac(unsigned char *enetaddr, + struct usb_device *udev) +{ + int ret; + + memset(enetaddr, 0, 6); + + ret = lan78xx_read_otp(udev, + EEPROM_MAC_OFFSET, + ETH_ALEN, + enetaddr); + if (!ret && is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from OTP %pM\n", enetaddr); + return 0; + } + debug("MAC address read from OTP invalid %pM\n", enetaddr); + + memset(enetaddr, 0, 6); + return -EINVAL; +} + +static int lan78xx_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev) +{ + uint32_t flow = 0, fct_flow = 0; + int ret; + + ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_FCT_FLOW, fct_flow); + if (ret) + return ret; + return lan7x_write_reg(udev, FLOW, flow); +} + +static int lan78xx_read_mac(unsigned char *enetaddr, + struct usb_device *udev, + struct lan7x_private *priv) +{ + u32 val; + int ret; + int saved = 0, done = 0; + + /* + * Depends on chip, some EEPROM pins are muxed with LED function. + * disable & restore LED function to access EEPROM. + */ + if ((priv->chipid == ID_REV_CHIP_ID_7800) || + (priv->chipid == ID_REV_CHIP_ID_7850)) { + ret = lan7x_read_reg(udev, HW_CFG, &val); + if (ret) + return ret; + saved = val; + val &= ~(LAN78XX_HW_CFG_LED1_EN | LAN78XX_HW_CFG_LED0_EN); + ret = lan7x_write_reg(udev, HW_CFG, val); + if (ret) + goto restore; + } + + /* + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + /* try reading mac address from EEPROM, then from OTP */ + ret = lan7x_read_eeprom_mac(enetaddr, udev); + if (!ret) + done = 1; + +restore: + if ((priv->chipid == ID_REV_CHIP_ID_7800) || + (priv->chipid == ID_REV_CHIP_ID_7850)) { + ret = lan7x_write_reg(udev, HW_CFG, saved); + if (ret) + return ret; + } + /* if the EEPROM mac address is good, then exit */ + if (done) + return 0; + + /* try reading mac address from OTP if the device is LAN78xx */ + return lan78xx_read_otp_mac(enetaddr, udev); +} + +static int lan78xx_set_receive_filter(struct usb_device *udev) +{ + /* No multicast in u-boot for now */ + return lan7x_write_reg(udev, LAN78XX_RFE_CTL, + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); +} + +/* starts the TX path */ +static void lan78xx_start_tx_path(struct usb_device *udev) +{ + /* Enable Tx at MAC */ + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); + + /* Enable Tx at SCSRs */ + lan7x_write_reg(udev, LAN78XX_FCT_TX_CTL, FCT_TX_CTL_EN); +} + +/* Starts the Receive path */ +static void lan78xx_start_rx_path(struct usb_device *udev) +{ + /* Enable Rx at MAC */ + lan7x_write_reg(udev, MAC_RX, + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | + MAC_RX_FCS_STRIP | MAC_RX_RXEN); + + /* Enable Rx at SCSRs */ + lan7x_write_reg(udev, LAN78XX_FCT_RX_CTL, FCT_RX_CTL_EN); +} + +static int lan78xx_basic_reset(struct usb_device *udev, + struct ueth_data *dev, + struct lan7x_private *priv) +{ + int ret; + u32 val; + + ret = lan7x_basic_reset(udev, dev); + if (ret) + return ret; + + /* Keep the chip ID */ + ret = lan7x_read_reg(udev, ID_REV, &val); + if (ret) + return ret; + debug("LAN78xx ID_REV = 0x%08x\n", val); + + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; + + /* Respond to the IN token with a NAK */ + ret = lan7x_read_reg(udev, LAN78XX_USB_CFG0, &val); + if (ret) + return ret; + val |= LAN78XX_USB_CFG0_BIR; + return lan7x_write_reg(udev, LAN78XX_USB_CFG0, val); +} + +int lan78xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + unsigned char *enetaddr = pdata->enetaddr; + u32 addr_lo = get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]); + int ret; + + /* set hardware address */ + ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_MAF_LO(0), addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_MAF_HI(0), + addr_hi | LAN78XX_MAF_HI_VALID); + if (ret) + return ret; + + debug("MAC addr %pM written\n", enetaddr); + + return 0; +} + +static int lan78xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + + int ret; + u32 write_buf; + + /* Reset and read Mac addr were done in probe() */ + ret = lan78xx_write_hwaddr(dev); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_BURST_CAP, 0); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF); + if (ret) + return ret; + + /* set FIFO sizes */ + ret = lan7x_write_reg(udev, LAN78XX_FCT_RX_FIFO_END, + (MAX_RX_FIFO_SIZE - 512) / 512); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_FCT_TX_FIFO_END, + (MAX_TX_FIFO_SIZE - 512) / 512); + if (ret) + return ret; + + /* Init Tx */ + ret = lan7x_write_reg(udev, FLOW, 0); + if (ret) + return ret; + + /* Init Rx. Set Vlan, keep default for VLAN on 78xx */ + ret = lan78xx_set_receive_filter(udev); + if (ret) + return ret; + + /* Init PHY, autonego, and link */ + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); + if (ret) + return ret; + ret = lan7x_eth_phylib_config_start(dev); + if (ret) + return ret; + + /* + * MAC_CR has to be set after PHY init. + * MAC will auto detect the PHY speed. + */ + ret = lan7x_read_reg(udev, MAC_CR, &write_buf); + if (ret) + return ret; + write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP; + ret = lan7x_write_reg(udev, MAC_CR, write_buf); + if (ret) + return ret; + + lan78xx_start_tx_path(udev); + lan78xx_start_rx_path(udev); + + return lan78xx_update_flowcontrol(udev, &priv->ueth); +} + +int lan78xx_read_rom_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + struct lan7x_private *priv = dev_get_priv(dev); + int ret; + + ret = lan78xx_read_mac(pdata->enetaddr, udev, priv); + if (ret) + memset(pdata->enetaddr, 0, 6); + + return 0; +} + +static int lan78xx_eth_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + struct eth_pdata *pdata = dev_get_platdata(dev); + int ret; + + /* Do a reset in order to get the MAC address from HW */ + if (lan78xx_basic_reset(udev, ueth, priv)) + return 0; + + /* Get the MAC address */ + /* + * We must set the eth->enetaddr from HW because the upper layer + * will force to use the environmental var (usbethaddr) or random if + * there is no valid MAC address in eth->enetaddr. + */ + lan78xx_read_mac(pdata->enetaddr, udev, priv); + /* Do not return 0 for not finding MAC addr in HW */ + + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); + if (ret) + return ret; + + /* Register phylib */ + return lan7x_phylib_register(dev); +} + +static const struct eth_ops lan78xx_eth_ops = { + .start = lan78xx_eth_start, + .send = lan7x_eth_send, + .recv = lan7x_eth_recv, + .free_pkt = lan7x_free_pkt, + .stop = lan7x_eth_stop, + .write_hwaddr = lan78xx_write_hwaddr, + .read_rom_hwaddr = lan78xx_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(lan78xx_eth) = { + .name = "lan78xx_eth", + .id = UCLASS_ETH, + .probe = lan78xx_eth_probe, + .remove = lan7x_eth_remove, + .ops = &lan78xx_eth_ops, + .priv_auto_alloc_size = sizeof(struct lan7x_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id lan78xx_eth_id_table[] = { + { USB_DEVICE(0x0424, 0x7800) }, /* LAN7800 USB Ethernet */ + { USB_DEVICE(0x0424, 0x7850) }, /* LAN7850 USB Ethernet */ + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(lan78xx_eth, lan78xx_eth_id_table); diff --git a/drivers/usb/eth/lan7x.c b/drivers/usb/eth/lan7x.c new file mode 100644 index 0000000000..222d327bdd --- /dev/null +++ b/drivers/usb/eth/lan7x.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm.h> +#include <malloc.h> +#include <miiphy.h> +#include <memalign.h> +#include <usb.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* + * Lan7x infrastructure commands + */ +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + cpu_to_le32s(&data); + tmpbuf[0] = data; + + len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(data), + USB_CTRL_SET_TIMEOUT_MS); + if (len != sizeof(data)) { + debug("%s failed: index=%d, data=%d, len=%d", + __func__, index, data, len); + return -EIO; + } + return 0; +} + +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(*data), + USB_CTRL_GET_TIMEOUT_MS); + *data = tmpbuf[0]; + if (len != sizeof(*data)) { + debug("%s failed: index=%d, len=%d", __func__, index, len); + return -EIO; + } + + le32_to_cpus(data); + return 0; +} + +static int lan7x_phy_wait_not_busy(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + MII_ACC, MII_ACC_MII_BUSY, + false, 100, 0); +} + +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (lan7x_phy_wait_not_busy(udev)) { + debug("MII is busy in %s\n", __func__); + return -ETIMEDOUT; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy_id << 11) | (idx << 6) | + MII_ACC_MII_READ | MII_ACC_MII_BUSY; + lan7x_write_reg(udev, MII_ACC, addr); + + if (lan7x_phy_wait_not_busy(udev)) { + debug("Timed out reading MII reg %02X\n", idx); + return -ETIMEDOUT; + } + + lan7x_read_reg(udev, MII_DATA, &val); + + return val & 0xFFFF; +} + +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, int regval) +{ + u32 addr; + + /* confirm MII not busy */ + if (lan7x_phy_wait_not_busy(udev)) { + debug("MII is busy in %s\n", __func__); + return; + } + + lan7x_write_reg(udev, MII_DATA, regval); + + /* set the address, index & direction (write to PHY) */ + addr = (phy_id << 11) | (idx << 6) | + MII_ACC_MII_WRITE | MII_ACC_MII_BUSY; + lan7x_write_reg(udev, MII_ACC, addr); + + if (lan7x_phy_wait_not_busy(udev)) + debug("Timed out writing MII reg %02X\n", idx); +} + +/* + * Lan7x phylib wrappers + */ +static int lan7x_phylib_mdio_read(struct mii_dev *bus, + int addr, int devad, int reg) +{ + struct usb_device *udev = dev_get_parent_priv(bus->priv); + + return lan7x_mdio_read(udev, addr, reg); +} + +static int lan7x_phylib_mdio_write(struct mii_dev *bus, + int addr, int devad, int reg, u16 val) +{ + struct usb_device *udev = dev_get_parent_priv(bus->priv); + + lan7x_mdio_write(udev, addr, reg, (int)val); + + return 0; +} + +/* + * Lan7x eeprom functions + */ +static int lan7x_eeprom_confirm_not_busy(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + E2P_CMD, E2P_CMD_EPC_BUSY, + false, 100, 0); +} + +static int lan7x_wait_eeprom(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + E2P_CMD, + (E2P_CMD_EPC_BUSY | E2P_CMD_EPC_TIMEOUT), + false, 100, 0); +} + +static int lan7x_read_eeprom(struct usb_device *udev, + u32 offset, u32 length, u8 *data) +{ + u32 val; + int i, ret; + + ret = lan7x_eeprom_confirm_not_busy(udev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_EPC_BUSY | E2P_CMD_EPC_CMD_READ | + (offset & E2P_CMD_EPC_ADDR_MASK); + lan7x_write_reg(udev, E2P_CMD, val); + + ret = lan7x_wait_eeprom(udev); + if (ret) + return ret; + + lan7x_read_reg(udev, E2P_DATA, &val); + data[i] = val & 0xFF; + offset++; + } + return ret; +} + +/* + * Lan7x phylib functions + */ +int lan7x_phylib_register(struct udevice *udev) +{ + struct usb_device *usbdev = dev_get_parent_priv(udev); + struct lan7x_private *priv = dev_get_priv(udev); + int ret; + + priv->mdiobus = mdio_alloc(); + if (!priv->mdiobus) { + printf("mdio_alloc failed\n"); + return -ENOMEM; + } + priv->mdiobus->read = lan7x_phylib_mdio_read; + priv->mdiobus->write = lan7x_phylib_mdio_write; + sprintf(priv->mdiobus->name, + "lan7x_mdiobus-d%hu-p%hu", usbdev->devnum, usbdev->portnr); + priv->mdiobus->priv = (void *)udev; + + ret = mdio_register(priv->mdiobus); + if (ret) { + printf("mdio_register failed\n"); + free(priv->mdiobus); + return -ENOMEM; + } + + return 0; +} + +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev) +{ + struct lan7x_private *priv = dev_get_priv(udev); + + priv->phydev = phy_connect(priv->mdiobus, dev->phy_id, + udev, PHY_INTERFACE_MODE_MII); + + if (!priv->phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + return 0; +} + +int lan7x_eth_phylib_config_start(struct udevice *udev) +{ + struct lan7x_private *priv = dev_get_priv(udev); + int ret; + + /* configure supported modes */ + priv->phydev->supported = PHY_BASIC_FEATURES | + SUPPORTED_1000baseT_Full | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause; + + priv->phydev->advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_Pause | + ADVERTISED_Asym_Pause | + ADVERTISED_Autoneg; + + priv->phydev->autoneg = AUTONEG_ENABLE; + + ret = genphy_config_aneg(priv->phydev); + if (ret) { + printf("genphy_config_aneg failed\n"); + return ret; + } + ret = phy_startup(priv->phydev); + if (ret) { + printf("phy_startup failed\n"); + return ret; + } + + debug("** %s() speed %i duplex %i adv %X supp %X\n", __func__, + priv->phydev->speed, priv->phydev->duplex, + priv->phydev->advertising, priv->phydev->supported); + + return 0; +} + +int lan7x_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev, + uint32_t *flow, uint32_t *fct_flow) +{ + uint32_t lcladv, rmtadv; + u8 cap = 0; + struct lan7x_private *priv = dev_get_priv(udev->dev); + + debug("** %s()\n", __func__); + debug("** %s() priv->phydev->speed %i duplex %i\n", __func__, + priv->phydev->speed, priv->phydev->duplex); + + if (priv->phydev->duplex == DUPLEX_FULL) { + lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE); + rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA); + cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + + debug("TX Flow "); + if (cap & FLOW_CTRL_TX) { + *flow = (FLOW_CR_TX_FCEN | 0xFFFF); + /* set fct_flow thresholds to 20% and 80% */ + *fct_flow = ((MAX_RX_FIFO_SIZE * 2) / (10 * 512)) + & 0x7FUL; + *fct_flow <<= 8UL; + *fct_flow |= ((MAX_RX_FIFO_SIZE * 8) / (10 * 512)) + & 0x7FUL; + debug("EN "); + } else { + debug("DIS "); + } + debug("RX Flow "); + if (cap & FLOW_CTRL_RX) { + *flow |= FLOW_CR_RX_FCEN; + debug("EN"); + } else { + debug("DIS"); + } + } + debug("\n"); + return 0; +} + +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev) +{ + int ret; + + memset(enetaddr, 0, 6); + + ret = lan7x_read_eeprom(udev, 0, 1, enetaddr); + + if ((ret == 0) && (enetaddr[0] == EEPROM_INDICATOR)) { + ret = lan7x_read_eeprom(udev, + EEPROM_MAC_OFFSET, ETH_ALEN, + enetaddr); + if ((ret == 0) && is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from EEPROM %pM\n", + enetaddr); + return 0; + } + } + debug("MAC address read from EEPROM invalid %pM\n", enetaddr); + + memset(enetaddr, 0, 6); + return -EINVAL; +} + +int lan7x_pmt_phy_reset(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret; + u32 data; + + ret = lan7x_read_reg(udev, PMT_CTL, &data); + if (ret) + return ret; + ret = lan7x_write_reg(udev, PMT_CTL, data | PMT_CTL_PHY_RST); + if (ret) + return ret; + + /* for LAN7x, we need to check PMT_CTL_READY asserted */ + ret = lan7x_wait_for_bit(udev, "PMT_CTL_PHY_RST", + PMT_CTL, PMT_CTL_PHY_RST, + false, 1000, 0); /* could take over 125mS */ + if (ret) + return ret; + + return lan7x_wait_for_bit(udev, "PMT_CTL_READY", + PMT_CTL, PMT_CTL_READY, + true, 1000, 0); +} + +int lan7x_basic_reset(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret; + + dev->phy_id = LAN7X_INTERNAL_PHY_ID; /* fixed phy id */ + + ret = lan7x_write_reg(udev, HW_CFG, HW_CFG_LRST); + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "HW_CFG_LRST", + HW_CFG, HW_CFG_LRST, + false, 1000, 0); + if (ret) + return ret; + + debug("USB devnum %d portnr %d\n", udev->devnum, udev->portnr); + + return lan7x_pmt_phy_reset(udev, dev); +} + +void lan7x_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int lan7x_eth_send(struct udevice *dev, void *packet, int length) +{ + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int err; + int actual_len; + u32 tx_cmd_a; + u32 tx_cmd_b; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)); + + debug("** %s(), len %d, buf %#x\n", __func__, length, + (unsigned int)(ulong) msg); + if (length > PKTSIZE) + return -ENOSPC; + + /* LAN7x disable all TX offload features for u-boot */ + tx_cmd_a = (u32) (length & TX_CMD_A_LEN_MASK) | TX_CMD_A_FCS; + tx_cmd_b = 0; + cpu_to_le32s(&tx_cmd_a); + cpu_to_le32s(&tx_cmd_b); + + /* prepend cmd_a and cmd_b */ + memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a)); + memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b)); + memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet, + length); + err = usb_bulk_msg(ueth->pusb_dev, + usb_sndbulkpipe(ueth->pusb_dev, ueth->ep_out), + (void *)msg, + length + sizeof(tx_cmd_a) + + sizeof(tx_cmd_b), + &actual_len, USB_BULK_SEND_TIMEOUT_MS); + debug("Tx: len = %u, actual = %u, err = %d\n", + (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)), + (unsigned int)actual_len, err); + + return err; +} + +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len = 0; + u32 rx_cmd_a = 0; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data plus error info. + * Extract data length. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&rx_cmd_a, ptr, sizeof(rx_cmd_a)); + le32_to_cpus(&rx_cmd_a); + if (rx_cmd_a & RX_CMD_A_RXE) { + debug("Rx: Error header=%#x", rx_cmd_a); + goto err; + } + packet_len = (u16) (rx_cmd_a & RX_CMD_A_LEN_MASK); + + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + /* + * For LAN7x, the length in command A does not + * include command A, B, and C length. + * So use it as is. + */ + + *packetp = ptr + 10; + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct lan7x_private *priv = dev_get_priv(dev); + + packet_len = ALIGN(packet_len, 4); + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int lan7x_eth_remove(struct udevice *dev) +{ + struct lan7x_private *priv = dev_get_priv(dev); + + debug("** %s()\n", __func__); + free(priv->phydev); + mdio_unregister(priv->mdiobus); + mdio_free(priv->mdiobus); + + return 0; +} diff --git a/drivers/usb/eth/lan7x.h b/drivers/usb/eth/lan7x.h new file mode 100644 index 0000000000..4f4b3f85e2 --- /dev/null +++ b/drivers/usb/eth/lan7x.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <console.h> +#include <watchdog.h> + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 +#define USB_VENDOR_REQUEST_GET_STATS 0xA2 + +/* Tx Command A */ +#define TX_CMD_A_FCS BIT(22) +#define TX_CMD_A_LEN_MASK 0x000FFFFF + +/* Rx Command A */ +#define RX_CMD_A_RXE BIT(18) +#define RX_CMD_A_LEN_MASK 0x00003FFF + +/* SCSRs */ +#define ID_REV 0x00 +#define ID_REV_CHIP_ID_MASK 0xFFFF0000 +#define ID_REV_CHIP_ID_7500 0x7500 +#define ID_REV_CHIP_ID_7800 0x7800 +#define ID_REV_CHIP_ID_7850 0x7850 + +#define INT_STS 0x0C + +#define HW_CFG 0x010 +#define HW_CFG_LRST BIT(1) + +#define PMT_CTL 0x014 +#define PMT_CTL_PHY_PWRUP BIT(10) +#define PMT_CTL_READY BIT(7) +#define PMT_CTL_PHY_RST BIT(4) + +#define E2P_CMD 0x040 +#define E2P_CMD_EPC_BUSY BIT(31) +#define E2P_CMD_EPC_CMD_READ 0x00000000 +#define E2P_CMD_EPC_TIMEOUT BIT(10) +#define E2P_CMD_EPC_ADDR_MASK 0x000001FF + +#define E2P_DATA 0x044 + +#define RFE_CTL_BCAST_EN BIT(10) +#define RFE_CTL_DA_PERFECT BIT(1) + +#define FCT_RX_CTL_EN BIT(31) + +#define FCT_TX_CTL_EN BIT(31) + +#define MAC_CR 0x100 +#define MAC_CR_ADP BIT(13) +#define MAC_CR_AUTO_DUPLEX BIT(12) +#define MAC_CR_AUTO_SPEED BIT(11) + +#define MAC_RX 0x104 +#define MAC_RX_FCS_STRIP BIT(4) +#define MAC_RX_RXEN BIT(0) + +#define MAC_TX 0x108 +#define MAC_TX_TXEN BIT(0) + +#define FLOW 0x10C +#define FLOW_CR_TX_FCEN BIT(30) +#define FLOW_CR_RX_FCEN BIT(29) + +#define RX_ADDRH 0x118 +#define RX_ADDRL 0x11C + +#define MII_ACC 0x120 +#define MII_ACC_MII_READ 0x00000000 +#define MII_ACC_MII_WRITE 0x00000002 +#define MII_ACC_MII_BUSY BIT(0) + +#define MII_DATA 0x124 + +#define SS_USB_PKT_SIZE 1024 +#define HS_USB_PKT_SIZE 512 +#define FS_USB_PKT_SIZE 64 + +#define MAX_RX_FIFO_SIZE (12 * 1024) +#define MAX_TX_FIFO_SIZE (12 * 1024) +#define DEFAULT_BULK_IN_DELAY 0x0800 + +#define EEPROM_INDICATOR 0xA5 +#define EEPROM_MAC_OFFSET 0x01 + +/* Some extra defines */ +#define LAN7X_INTERNAL_PHY_ID 1 + +#define LAN7X_MAC_RX_MAX_SIZE(mtu) \ + ((mtu) << 16) /* Max frame size */ +#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \ + LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */) + +/* Timeouts */ +#define USB_CTRL_SET_TIMEOUT_MS 5000 +#define USB_CTRL_GET_TIMEOUT_MS 5000 +#define USB_BULK_SEND_TIMEOUT_MS 5000 +#define USB_BULK_RECV_TIMEOUT_MS 5000 +#define TIMEOUT_RESOLUTION_MS 50 +#define PHY_CONNECT_TIMEOUT_MS 5000 + +#define RX_URB_SIZE 2048 + +/* driver private */ +struct lan7x_private { + struct ueth_data ueth; + u32 chipid; /* Chip or device ID */ + struct mii_dev *mdiobus; + struct phy_device *phydev; +}; + +/* + * Lan7x infrastructure commands + */ + +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data); + +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data); + +static inline int lan7x_wait_for_bit(struct usb_device *udev, + const char *prefix, const u32 reg, + const u32 mask, const bool set, + const unsigned int timeout_ms, + const bool breakable) +{ + u32 val; + unsigned long start = get_timer(0); + + while (1) { + lan7x_read_reg(udev, reg, &val); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + if (get_timer(start) > timeout_ms) + break; + + if (breakable && ctrlc()) { + puts("Abort\n"); + return -EINTR; + } + + udelay(1); + WATCHDOG_RESET(); + } + + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, + mask, set); + + return -ETIMEDOUT; +} + +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx); + +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, + int regval); + +static inline int lan7x_mdio_wait_for_bit(struct usb_device *udev, + const char *prefix, + int phy_id, const u32 reg, + const u32 mask, const bool set, + const unsigned int timeout_ms, + const bool breakable) +{ + u32 val; + unsigned long start = get_timer(0); + + while (1) { + val = lan7x_mdio_read(udev, phy_id, reg); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + if (get_timer(start) > timeout_ms) + break; + + if (breakable && ctrlc()) { + puts("Abort\n"); + return -EINTR; + } + + udelay(1); + WATCHDOG_RESET(); + } + + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, + mask, set); + + return -ETIMEDOUT; +} + +int lan7x_phylib_register(struct udevice *udev); + +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev); + +int lan7x_eth_phylib_config_start(struct udevice *udev); + +int lan7x_pmt_phy_reset(struct usb_device *udev, + struct ueth_data *dev); + +int lan7x_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev, + uint32_t *flow, uint32_t *fct_flow); + +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev); + +int lan7x_basic_reset(struct usb_device *udev, + struct ueth_data *dev); + +void lan7x_eth_stop(struct udevice *dev); + +int lan7x_eth_send(struct udevice *dev, void *packet, int length); + +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp); + +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len); + +int lan7x_eth_remove(struct udevice *dev); diff --git a/drivers/usb/gadget/designware_udc.c b/drivers/usb/gadget/designware_udc.c index 0db7a3b6c1..a25f5019e8 100644 --- a/drivers/usb/gadget/designware_udc.c +++ b/drivers/usb/gadget/designware_udc.c @@ -601,7 +601,7 @@ void udc_setup_ep(struct usb_device_instance *device, if ((ep != 0) && (udc_device->device_state < STATE_ADDRESSED)) return; - tt = getenv("usbtty"); + tt = env_get("usbtty"); if (!tt) tt = "generic"; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 4137d76c42..2cf5c8d31e 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -2384,12 +2384,12 @@ static int _usb_eth_init(struct ether_priv *priv) strlcpy(host_addr, CONFIG_USBNET_HOST_ADDR, sizeof(host_addr)); #endif /* Check if the user overruled the MAC addresses */ - if (getenv("usbnet_devaddr")) - strlcpy(dev_addr, getenv("usbnet_devaddr"), + if (env_get("usbnet_devaddr")) + strlcpy(dev_addr, env_get("usbnet_devaddr"), sizeof(dev_addr)); - if (getenv("usbnet_hostaddr")) - strlcpy(host_addr, getenv("usbnet_hostaddr"), + if (env_get("usbnet_hostaddr")) + strlcpy(host_addr, env_get("usbnet_hostaddr"), sizeof(host_addr)); if (!is_eth_addr_valid(dev_addr)) { @@ -2420,8 +2420,8 @@ static int _usb_eth_init(struct ether_priv *priv) gadget = dev->gadget; usb_gadget_connect(gadget); - if (getenv("cdc_connect_timeout")) - timeout = simple_strtoul(getenv("cdc_connect_timeout"), + if (env_get("cdc_connect_timeout")) + timeout = simple_strtoul(env_get("cdc_connect_timeout"), NULL, 10) * CONFIG_SYS_HZ; ts = get_timer(0); while (!dev->network_started) { @@ -2685,7 +2685,7 @@ static int usb_eth_probe(struct udevice *dev) l_priv = priv; get_ether_addr(CONFIG_USBNET_DEVADDR, pdata->enetaddr); - eth_setenv_enetaddr("usbnet_devaddr", pdata->enetaddr); + eth_env_set_enetaddr("usbnet_devaddr", pdata->enetaddr); return 0; } diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index dfa4359577..bc4be712da 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -725,7 +725,7 @@ static int dfu_bind(struct usb_configuration *c, struct usb_function *f) cdev->req->context = f_dfu; - s = getenv("serial#"); + s = env_get("serial#"); if (s) g_dnl_set_serialnumber((char *)s); diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 7cd6d24bf5..d05b74b2d2 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -212,7 +212,7 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = fb_hs_function; } - s = getenv("serial#"); + s = env_get("serial#"); if (s) g_dnl_set_serialnumber((char *)s); @@ -426,7 +426,7 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) sprintf(str_num, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE); strncat(response, str_num, chars_left); } else if (!strcmp_l1("serialno", cmd)) { - s = getenv("serial#"); + s = env_get("serial#"); if (s) strncat(response, s, chars_left); else @@ -441,7 +441,7 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) } sprintf(envstr, "fastboot.%s", cmd); - s = getenv(envstr); + s = env_get(envstr); if (s) { strncat(response, s, chars_left); } else { diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index b57c6cd35a..62c431b99f 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -225,7 +225,7 @@ static int ehci_fsl_init(int index, struct usb_ehci *ehci, "phy_type", &len); #endif else - phy_type = getenv("usb_phy_type"); + phy_type = env_get("usb_phy_type"); if (!phy_type) { #ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY diff --git a/drivers/video/ati_radeon_fb.c b/drivers/video/ati_radeon_fb.c index 07a29eaba1..5b6c42291c 100644 --- a/drivers/video/ati_radeon_fb.c +++ b/drivers/video/ati_radeon_fb.c @@ -637,7 +637,8 @@ void *video_hw_init(void) videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; /* get video mode via environment */ - if ((penv = getenv ("videomode")) != NULL) { + penv = env_get("videomode"); + if (penv) { /* deceide if it is a string */ if (penv[0] <= '9') { videomode = (int) simple_strtoul (penv, NULL, 16); diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index b6fc7e145b..6c5425c195 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -1856,7 +1856,7 @@ static void *video_logo(void) splash_get_pos(&video_logo_xpos, &video_logo_ypos); #ifdef CONFIG_SPLASH_SCREEN - s = getenv("splashimage"); + s = env_get("splashimage"); if (s != NULL) { ret = splash_screen_prepare(); if (ret < 0) diff --git a/drivers/video/mb862xx.c b/drivers/video/mb862xx.c index 1c74e97c5e..e0565e1d2e 100644 --- a/drivers/video/mb862xx.c +++ b/drivers/video/mb862xx.c @@ -246,7 +246,8 @@ unsigned int card_init (void) tmp = 0; videomode = 0x310; /* get video mode via environment */ - if ((penv = getenv ("videomode")) != NULL) { + penv = env_get("videomode"); + if (penv) { /* decide if it is a string */ if (penv[0] <= '9') { videomode = (int) simple_strtoul (penv, NULL, 16); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 51d06d6677..78e595ea4a 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -816,7 +816,7 @@ void *video_hw_init(void) videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; /* get video mode via environment */ - penv = getenv("videomode"); + penv = env_get("videomode"); if (penv) { /* decide if it is a string */ if (penv[0] <= '9') { diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 0ddce3db58..9d810bab31 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -161,7 +161,7 @@ void *video_hw_init(void) puts("Video: "); /* Suck display configuration from "videomode" variable */ - penv = getenv("videomode"); + penv = env_get("videomode"); if (!penv) { puts("MXSFB: 'videomode' variable not set!\n"); return NULL; diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index cf71ad120e..6d96b33f20 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -165,7 +165,8 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv) /* first search for the environment containing the real param string */ s = penv; - if ((p = getenv (s)) != NULL) + p = env_get(s); + if (p) s = p; /* @@ -234,7 +235,7 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv) int video_get_video_mode(unsigned int *xres, unsigned int *yres, unsigned int *depth, unsigned int *freq, const char **options) { - char *p = getenv("video-mode"); + char *p = env_get("video-mode"); if (!p) return 0; diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index bb9ae80866..8a30f024fd 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -13,14 +13,14 @@ DECLARE_GLOBAL_DATA_PTR; -int wdt_start(struct udevice *dev, u64 timeout, ulong flags) +int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) { const struct wdt_ops *ops = device_get_ops(dev); if (!ops->start) return -ENOSYS; - return ops->start(dev, timeout, flags); + return ops->start(dev, timeout_ms, flags); } int wdt_stop(struct udevice *dev) |